问题:读取 chrome/edge cookie 以提取从桌面应用程序启动的浏览器中用户的 XSRF-Token 和 .AspNet.Cookies 值并将其保存在注册表项中
我使用了下面的代码,过去两个月工作正常,突然有一天,秘密密钥能够像平常一样提取,但初始化向量和加密密码似乎与之前不同,两个密钥似乎都正在提取,但面临问题:
'utf-8' codec can't decode byte 0xf0 in position 0: invalid continuation byte
由于取值不正确,请参考代码后的值。
import os
import win32crypt
from Crypto.Cipher import AES
import base64
import sqlite3
import json
import winreg as reg
#Extracting the secret key
def get_aes_key():
local_state_path = 'C:/Users/<username>/AppData/Local/Google/Chrome/User Data/Local state' # Update with your path
with open(local_state_path, 'r', encoding='utf-8') as f:
local_state_data = json.load(f)
encrypted_key = local_state_data['os_crypt']['encrypted_key']
print(encrypted_key)
encrypted_key_base64_dpapi = base64.b64decode(encrypted_key)
print(encrypted_key_base64_dpapi)
encrypted_key_base64 = encrypted_key_base64_dpapi[5:] # Strip 'DPAPI' prefix
encrypted_key_base64 = win32crypt.CryptUnprotectData(encrypted_key_base64, None, None, None, 0)[1]
aes_key = encrypted_key_base64 # Strip DPAPI prefix
return aes_key
def decrypt_payload(cipher, payload):
return cipher.decrypt(payload)
def generate_cipher(aes_key, iv):
return AES.new(aes_key, AES.MODE_GCM, iv)
# Function to decrypt cookie value using AES
def decrypt_aes(dec_aes_key, encrypted_data):
try:
# (3-a) Initialisation vector for AES decryption
initialisation_vector = encrypted_data[3:15]
# (3-b) Get encrypted password by removing suffix bytes (last 16 bits)
# Encrypted password is 192 bits
encrypted_password = encrypted_data[15:-16]
# (4) Build the cipher to decrypt the ciphertext
cipher = generate_cipher(dec_aes_key, initialisation_vector)
decrypted_pass = decrypt_payload(cipher, encrypted_password)
decrypted_pass = decrypted_pass.decode()
return decrypted_pass
except Exception as e:
print("%s" % str(e))
print("[ERR] Unable to decrypt, Chrome version <80 not supported. Please check.")
return ""
# Path to Chrome's Cookies database
logged_in_user = os.getlogin()
cookies_db = 'C:/Users/' + logged_in_user + '/AppData/Local/Google/Chrome/User
Data/Default/Network/Cookies'
# Connect to the Cookies database
conn = sqlite3.connect(cookies_db)
cursor = conn.cursor()
aes_key = get_aes_key()
print(aes_key)
# Example query to fetch cookies
cursor.execute('SELECT host_key, name, encrypted_value FROM cookies')
def set_environment_variable(name, value):
try:
# Open the registry key for environment variables
with reg.OpenKey(reg.HKEY_LOCAL_MACHINE, r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 0, reg.KEY_SET_VALUE) as key:
# Set the environment variable
reg.SetValueEx(key, name, 0, reg.REG_SZ, value)
print(f'Successfully set {name} to {value}')
except PermissionError:
print("Permission denied. You need to run this script with administrative privileges.")
except Exception as e:
print(f'Failed to set environment variable: {e}')
for row in cursor.fetchall():
host_key, name, encrypted_value = row
if host_key == "adregression02":
#print(f'Name: {name}, encrypted value: {encrypted_value}')
decrypted_value = decrypt_aes(aes_key, encrypted_value)
#print(f'Name: {name}, Decrypted Value: {decrypted_value.decode("utf-8")}')
print("Name: %s\ncookie: %s\n" % (name, decrypted_value))
set_environment_variable(name, decrypted_value)
conn.close()
我认为问题在于以下几行:
def decrypt_aes(dec_aes_key, encrypted_data):
try:
# (3-a) Initialisation vector for AES decryption
initialisation_vector = encrypted_data[3:15]
# Encrypted password is 192 bits
encrypted_password = encrypted_data[15:-16]
# (4) Build the cipher to decrypt the ciphertext
cipher = generate_cipher(dec_aes_key, initialisation_vector)
decrypted_pass = decrypt_payload(cipher, encrypted_password)
decrypted_pass = decrypted_pass.decode()
return decrypted_pass
以下是提取的值:
名称:XSRF-TOKEN,加密值:
b"v20>QT\xc3.A^E\x85\xc6c+\xac\x0eT\xa3z\xe4\x94\xaa!\x83\x99?\x1e\x04\xdd\x1ad\xbc\x15\r\xf7\x18\x86-\x0bVi\xae\xe8\x0b\x12\x16\xf9+\xb5\xcb\xf8I\x8d\xa9\x1ez\x13\x1fwt\xa5\xb2\x8e\x1dW\xfd\xa1\x99;\xe1\xdc\x8cw[\x05\xd72z\xbfHNm\x05\xcb\x02\xc1\xf5\r\xdd\x9d\xfe\x80\r\x9a:\x81\xcc\x88\xd7-uj\xfe\x9c\xe1\x19\xeb\xbb?@ \x17\xa2\xd1\x87\xc6\xba\x8b\r'\xf1n\r\x1e\x0bK\xad-\xa3r\x1c4d\x93\x14\x9ae\xbca\xfc\xce%\xedc\xf6\x0b/R\xe1\xa1\xe8\xb9\x02
_Y\x01\xcd\xe4\xd1\x90@"`
**initialisation_vector--** b'>QT\xc3.A^E\x85\xc6c+'
**encrypted_password--** b"\xac\x0eT\xa3z\xe4\x94\xaa!\x83\x99?\x1e\x04\xdd\x1ad\xbc\x15\r\xf7\x18\x86-\x0bVi\xae\xe8\x0b\x12\x16\xf9+\xb5\xcb\xf8I\x8d\xa9\x1ez\x13\x1fwt\xa5\xb2\x8e\x1dW\xfd\xa1\x99;\xe1\xdc\x8cw[\x05\xd72z\xbfHNm\x05\xcb\x02\xc1\xf5\r\xdd\x9d\xfe\x80\r\x9a:\x81\xcc\x88\xd7-uj\xfe\x9c\xe1\x19\xeb\xbb?@ \x17\xa2\xd1\x87\xc6\xba\x8b\r'\xf1n\r\x1e\x0bK\xad-\xa3r\x1c4d\x93\x14\x9ae\xbca\xfc\xce%\xedc\xf6\x0b/R\xe1\xa1\xe8\xb9\x02
_Y\x01\xcd\xe4\xd1\x90@"`
预期的 xsrf 令牌:
v3eMK3iMPhdF8tumLxbjEHmSVqwT__4MFAhhFZH6-lP4ffXhfPvACW8oQYuxpQ8OZnI0y8-sVjwc4TobVGDjNTXFlrFI287t5B_SNah9yQSHVkppuRsT510cJXGT05IU9rlJibfMnu-iU4CRk--XyrZ7iOJSugxlKFukH8hDyJ--Gv_F6AUqIo6bFJ1i-10cu7bkZ4hLEvfhdk2q08tKyTn7e6oDfQ6nLicSoKIWjuPvEutpZIZQyK4li32GPirk_ysUKolUbLrl2AjwPVFrCZbgil8yHKyNrmAveY1b0lNNzsDljzsfF_TMLZazpYbbIvEarQuPe6wkL8sQuT-L9YpitNpeW8Wvfgyj96EWCbQ
有人可以指出缺少什么吗?或者edge/chrome更新了他们的加密?
目前加密更新似乎是从V20开始的,其中cookie加密标准自chrome版本127以来已从V10更新到V20。
我提到了链接:https://ohyicong.medium.com/how-to-hack-chrome-password-with-python-1bedc167be3d
TL;DR: 在 runassu 的 这个 Github 存储库 中,您可以找到有关如何解密 Chrome v20 cookie 的说明,包括 Python 脚本。
铬:
与 v10 cookie 解密的主要区别在于用于解密 cookie 的 AES 密钥的确定。对于 v10 cookie,加密密钥(在
Local StateJSON 文件中称为
encrypted_key
)只需使用 DPAPI 进行解密。对于 v20,需要对加密密钥(在 Local StateJSON 文件中称为
app_bound_encrypted_key
)进行三阶段解密:首先,必须使用系统帐户执行 DPAPI 解密,然后使用用户帐户,最后是 AES-GCM 解密。from pypsexec.client import Client
import os
import sys
import json
import base64
local_state_path = r'%LocalAppData%\Google\Chrome\User Data\Local State'
local_state_path = os.path.expandvars(local_state_path)
with open(local_state_path, "r") as f:
local_state = json.load(f)
app_bound_encrypted_key_b64 = local_state["os_crypt"]["app_bound_encrypted_key"]
app_bound_encrypted_key = base64.b64decode(app_bound_encrypted_key_b64)[4:] # Base64 decode and remove APPB prefix
arguments = "-c \"" + """from win32 import win32crypt
import base64
encrypted_key = win32crypt.CryptUnprotectData(base64.b64decode('{}'), None, None, None, 0)
print(base64.b64encode(encrypted_key[1]).decode())
""".replace("\n", ";") + "\""
c = Client("localhost")
c.connect()
try:
c.create_service()
# DPAPI decryption with SYSTEM account
decryption_sys_b64, stderr, rc = c.run_executable(
sys.executable,
arguments=arguments.format(base64.b64encode(app_bound_encrypted_key).decode().strip()),
use_system_account=True
)
# DPAPI decryption with User account
decryption_usr_b64, stderr, rc = c.run_executable(
sys.executable,
arguments=arguments.format(decryption_sys_b64.decode().strip()),
use_system_account=False
)
finally:
c.remove_service()
c.disconnect()
encrypted_content_key = base64.b64decode(decryption_usr_b64)[-60:] # Base64 decode; the last 60 bytes are the encrypted content key
encrypted_content_key
包含使用 AES-GCM 加密的内容密钥。它由解密数据的最后 60 个字节给出(前面的数据本质上包含了 Chrome 的安装路径)。在下一步中,必须使用 AES-GCM 解密该密钥。
encrypted_content_key
的格式为nonce|ciphertext|tag,其中nonce大小为12字节,密文大小为32字节,标签大小为16字节,即总共60字节。此加密所需的 AES 密钥是硬编码在elevation_service.exe中(下文称为elevation_key
)。 PyCryptodome 的可能实现是:
from Crypto.Cipher import AES
def decrypt_aes_gcm(encrypted_data, key):
nonce = encrypted_data[0:12]
ciphertext = encrypted_data[12:-16]
tag = encrypted_data[-16:]
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
return cipher.decrypt_and_verify(ciphertext, tag)
elevation_key = base64.b64decode("sxxuJBrIRnKNqcH6xJNmUc/7lE0UOrgWJ2vMbaAoR4c=") # hard-coded key from elevation_service.exe
content_key = decrypt_aes_gcm(encrypted_content_key, elevation_key)
content_key
现在是内容密钥,可以使用 AES-GCM 对 cookie 进行解密。从这里开始,解密类似于 v10 cookie 的解密。 Cookie 数据从索引位置 32 开始(前 32 个字节是 host_key
字段的 SHA256 哈希值,请参阅此处):
encrypted_cookie = bytes.fromhex('763230...')
encrypted_cookie_prefix_removed = encrypted_cookie[3:]
cookie = decrypt_aes_gcm(encrypted_cookie_prefix_removed, content_key)
cookie_content = cookie[32:] # decrypted cookie
边缘:
与 Chrome 相比,Edge 中加密密钥的解密只有两阶段,并且由双 DPAPI 解密组成。没有 AES-GCM 解密:
content_key = base64.b64decode(decryption_usr_b64)[-32:] # Base64 decode; the last 32 bytes are the content key
即双 DPAPI 解密数据的最后 32 个字节直接是内容密钥(同样,前面的数据本质上包含安装路径,现在是 Edge)。
此外,Local State文件和Cookies数据库的路径不同,即
%LocalAppData%\Microsoft\Edge\User Data\...
而不是%LocalAppData%\Google\Chrome\User Data\...
。