探索Firefox浏览器存储凭据的秘密

随着互联网的迅速发展,用户在不同的网站和服务上创建了大量的账户,这就需要记住大量的用户名和密码。为了提高用户体验和安全性,现代浏览器提供了自动保存用户凭据的功能。本文将深入探讨Firefox浏览器如何安全地存储和加密用户凭据,以及如何通过编程方式获取这些凭据。

Firefox的安全性

Firefox浏览器在最近的版本中,相较于旧版本和其他浏览器,其安全性有了显著提升。过去,只需要获取位于Firefox配置文件目录中的signons.txt文件,就可以找到所有存储的凭据。但从3.5版本开始,文本格式被SQLite数据库和JSON文件(即logins.json)所替代。此外,如果设置了“主密码”,则在不知道这个密码的情况下,无法解密存储的凭据。如果未设置主密码,可以在logins.json中找到数据并解密凭据。每个凭据的用户名和密码都使用PK#11进行加密

定位Firefox配置文件路径

首先,需要找到要获取的数据的位置,即Firefox配置文件路径。通常位于c:\users\<用户账户>\AppData\Local\Mozilla\Firefox\profiles.ini。以下是一个C++函数,用于获取Firefox配置文件路径:

std::wstring GetFirefoxProfilePath() { wchar_t strAppData[MAX_PATH]; SHGetSpecialFolderPath(NULL, strAppData, CSIDL_APPDATA, TRUE); wchar_t strIniPath[MAX_PATH]; swprintf_s(strIniPath, MAX_PATH, L"%s\\Mozilla\\Firefox\\profiles.ini", strAppData); wchar_t strRelativePath[MAX_PATH]; GetPrivateProfileString(L"Profile0", L"Path", L"", strRelativePath, MAX_PATH, strIniPath); wchar_t strFullPath[MAX_PATH]; swprintf_s(strFullPath, MAX_PATH, L"%s\\Mozilla\\Firefox\\%s", strAppData, strRelativePath); wchar_t strShortPath[MAX_PATH]; GetShortPathName(strFullPath, strShortPath, MAX_PATH); if (!PathFileExists(strShortPath)) { return L""; } return ((std::wstring)strShortPath); }

映射nss3.dll

nss3.dll是一组称为网络安全服务(NSS)的库所使用的。这些库旨在支持跨平台开发支持SSL、S/MIME和其他互联网安全标准的通信应用程序。由于本文是关于Windows的实现,只提及Windows使用的库:nss3.dll(Windows共享库)、nss3.lib(Windows导入库绑定到nss3.dll)和nss.lib(Windows静态库)。首先,需要映射打算使用的nss3.dll中的函数。

fpNSS_Init = (NSS_Init_p)GetProcAddress(moduleNSS, "NSS_Init"); fpNSS_Shutdown = (NSS_Shutdown_p)GetProcAddress(moduleNSS, "NSS_Shutdown"); fpPL_ArenaFinish = (PL_ArenaFinish_p)GetProcAddress(moduleNSS, "PL_ArenaFinish"); fpPR_Cleanup = (PR_Cleanup_p)GetProcAddress(moduleNSS, "PR_Cleanup"); fpPK11_GetInternalKeySlot = (PK11_GetInternalKeySlot_p)GetProcAddress(moduleNSS, "PK11_GetInternalKeySlot"); fpPK11_FreeSlot = (PK11_FreeSlot_p)GetProcAddress(moduleNSS, "PK11_FreeSlot"); fpPK11SDR_Decrypt = (PK11SDR_Decrypt_p)GetProcAddress(moduleNSS, "PK11SDR_Decrypt"); PK11_CheckUserPassword = (PK11CheckUserPassword)GetProcAddress(moduleNSS, "PK11_CheckUserPassword");

主密码

Firefox引入了一种新的安全措施,称为主密码。主密码是用于加密所有存储凭据的集中加密密钥。这个密钥被哈希处理,但没有存储在任何地方,因此无法找到。

在已知主密码的情况下,程序化解密凭据

进行了一些测试,试图了解在设置了主密码的情况下,需要做什么才能解密Firefox存储的凭据。首先,应该解释一下在已知主密码的情况下,如何程序化地解密凭据。

猜测主密码

另外,黑客可能会使用暴力破解技术尝试猜测主密码。这可以通过运行所有可能的字符串(或字母数字字符串)组合来完成,长度为1-7个字符(使用这种方法几乎不可能破解更长的密码)。另一种方法是运行常用密码列表。这种方法称为“字典攻击”。

检查给定字符串的代码

以下代码检查"MyGuess"是否确实是主密码。如果是,可以继续执行工具的其余代码以解密所有存储的密码。

bool GuessMasterPassword(char *MyGuess) { bool result = FALSE; PK11SlotInfo *pK11Slot = fpPK11_GetInternalKeySlot(); if (PK11_CheckUserPassword(pK11Slot, MyGuess) == SECSuccess) { result = true; } (*PK11FreeSlot)(pK11Slot); return result; }

解密凭据

代码的下一部分将解密凭据条目的任何加密部分,无论是否设置了主密码,都应该使用这段代码。如果没有设置主密码,仍然需要执行此代码,如果设置了主密码,假设GuessMasterPassword对于任何原因都返回了'true'(知道它,猜到了它,或者暴力破解了它)。

LPSTR DecryptString(LPSTR strCryptData) { if (strCryptData[0] == 0x0) return FALSE; DWORD dwOut; LPSTR strClearData = ""; LPBYTE lpBuffer = base64_decode((char *)strCryptData, strlen(strCryptData), (int *)&dwOut); PK11SlotInfo *pK11Slot = fpPK11_GetInternalKeySlot(); if (pK11Slot) { SECItem pInSecItem, pOutSecItem; pInSecItem.data = lpBuffer; pInSecItem.len = dwOut; pOutSecItem.data = 0; pOutSecItem.len = 0; if (fpPK11SDR_Decrypt(&pInSecItem, &pOutSecItem, NULL) == 0) { strClearData = (LPSTR)malloc(pOutSecItem.len + 1); memcpy(strClearData, pOutSecItem.data, pOutSecItem.len); *(strClearData+pOutSecItem.len) = '\0'; } fpPK11_FreeSlot(pK11Slot); } return strClearData; }

处理Firefox日期/时间

为了能够处理各种浏览器的记录,每个浏览器都使用自己的方法来存储日期和时间。需要将每个日期/时间记录转换为单一格式,以便稍后可以用于操作,例如查找给定日期的所有凭据,或自从上次检查以来。当涉及到Firefox时,它以大整数的形式存储日期。这个数字称为Epoch Time。可以将Epoch Time输入到这个网站,看看它代表的日期,反之亦然。

将Firefox日期/时间从JSON转换为CTime

首先,从JSON元素中读取日期/时间,但只使用前10个字符(共13个字符)。

double tempnum = json_object_get_number(commit, strDateTime); string datetime; datetime = std::to_string(tempnum); datetime = datetime.substr(0, 10);

Parson库提供了一个函数,用于读取数字为"double",这就是为什么首先将日期/时间读取到一个double变量中。接下来,将double转换为std::string。然后,去掉最后3位数字,留下字符串长度为10个字符。调用FirefoxTimeToSysTime(),将字符串发送给它。将SYSTEMTIME转换为CTime非常简单:

SYSTEMTIME newtime = utils::FirefoxTimeToSysTime(datetime); CTime ct(newtime);

稍后可以在报告或屏幕上显示数据,将CTime变量转换为用户友好的日期格式。在Secured Globe, Inc.,使用以下格式:

#define SG_FRIEDLY_DATEFORMAT L"%d-%m-%Y, %H:%M:%S"

因此,当需要使用存储的凭据并显示其一个元素时,将使用:

credentials[i].DateCreated.FormatGmt(SG_FRIEDLY_DATEFORMAT).GetBuffer();

FormatGmt很重要,如果想处理来自不同国家的用户的数据。对于本地使用,代码将是:

credentials[i].DateCreated.Format(SG_FRIEDLY_DATEFORMAT).GetBuffer();
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485