我正在尝试解密Chrome的cookie SQLite DB,并将解密的cookie移动到另一台计算机(浏览器),并重新加密数据库,并复制会话。
这是我的计划:
从
Local State
中的
C:\Users\[username]\AppData\Local\Google\Chrome\User Data\Local State
C:\Users\[username]\AppData\Local\Google\Chrome\User Data\Default\Network\Cookies
Local State
中的相关条目。我有以下 2 个 Python 文件来执行上述操作:
encrypt.py
:
from win32.win32crypt import CryptProtectData
import base64
import sqlite3
import os
from Cryptodome.Cipher.AES import new, MODE_GCM # pip install pycryptodomex
import decrypt
import json
def encrypt_dpapi_blob(decrypted_blob):
encrypted_blob = CryptProtectData(decrypted_blob, DataDescr="Google Chrome", OptionalEntropy=None, Reserved=None, PromptStruct=None, Flags=0)
encrypted_blob = b'DPAPI' + encrypted_blob
encrypted_blob_base64 = base64.b64encode(encrypted_blob)
return encrypted_blob_base64
def encrypt_cookies(cookies_db, key):
sqlite3.enable_callback_tracebacks(True)
conn = sqlite3.connect(cookies_db)
query = "SELECT name, encrypted_value FROM cookies"
cursor = conn.execute(query)
query_res = cursor.fetchall()
for row in query_res:
cookie_name, decrypted_value = row
# print(f"Encrypting cookie: {cookie_name}")
if decrypted_value is None or len(decrypted_value) == 0:
# print("No decrypted value found.")
continue
aes_cipher = new(key=key, mode=MODE_GCM, nonce=decrypted_value[3:15])
encrypted_value = aes_cipher.encrypt(decrypted_value[15: -16])
# print(f"Encrypted cookie:\n {decrypt.bytes_to_hex(encrypted_value)}\n {encrypted_value}")
verification_tag = decrypted_value[-16:]
# print(f"Verification tag:\n {decrypt.bytes_to_hex(verification_tag)}\n {verification_tag}")
nonce = decrypted_value[3:15]
# print(f"Nonce:\n {decrypt.bytes_to_hex(nonce)}\n {nonce}")
encrypted_cookie = b'\x76\x31\x30' +\
nonce +\
encrypted_value +\
verification_tag
query = f"UPDATE cookies SET encrypted_value = ? WHERE name = \"{cookie_name}\""
params = [encrypted_cookie]
cursor.execute(query, params)
# print("")
conn.commit()
conn.close()
if __name__ == "__main__":
cookies_db = os.path.join(os.getcwd(), "Cookies")
# print(f"Decrypted key:\n {decrypt.bytes_to_hex(key)}\n {key}")
key = os.urandom(32)
encrypt_cookies(cookies_db, key)
encrypted_key = encrypt_dpapi_blob(key)
print(f"Encrypted key:\n {str(encrypted_key, 'utf-8')}")
local_state = json.load(open('Local State'))
local_state['os_crypt']['encrypted_key'] = encrypted_key.decode()
json.dump(local_state, open('Local State', 'w'))
decrypt.py
:
from win32.win32crypt import CryptUnprotectData
import base64
import sqlite3
import os
from Cryptodome.Cipher.AES import new, MODE_GCM # pip install pycryptodomex
import sys
import json
def decrypt_dpapi_blob(encrypted_blob):
encrypted_blob = base64.b64decode(encrypted_blob)[5:] # Leading bytes "DPAPI" need to be removed
decrypt_res = CryptUnprotectData(encrypted_blob, None, None, None, 0)
return decrypt_res
def decrypt_cookies(cookies_db, key):
sqlite3.enable_callback_tracebacks(True)
conn = sqlite3.connect(cookies_db)
query = "SELECT name, encrypted_value FROM cookies"
cursor = conn.execute(query)
query_res = cursor.fetchall()
for row in query_res:
cookie_name, encrypted_value = row
# print(f"Decrypting cookie: {cookie_name}")
if encrypted_value is None or len(encrypted_value) == 0:
# print("No encrypted value found.")
continue
aes_cipher = new(key=key, mode=MODE_GCM, nonce=encrypted_value[3:15])
decrypted_value = aes_cipher.decrypt(encrypted_value[15: -16])
# print(f"Decrypted cookie:\n {bytes_to_hex(decrypted_value)}\n {decrypted_value}")
if cookie_name == "BITBUCKETSESSIONID":
print(f"Decrypted cookie (bitbucket): {decrypted_value.decode()}")
verification_tag = encrypted_value[-16:]
# print(f"Verification tag:\n {bytes_to_hex(verification_tag)}\n {verification_tag}")
nonce = encrypted_value[3:15]
# print(f"Nonce:\n {bytes_to_hex(nonce)}\n {nonce}")
decrypted_cookie = b'\x76\x31\x30' +\
nonce +\
decrypted_value +\
verification_tag
query = f"UPDATE cookies SET encrypted_value = ? WHERE name = \"{cookie_name}\""
params = [decrypted_cookie]
cursor.execute(query, params)
# print("")
conn.commit()
conn.close()
def bytes_to_hex(byte_data):
return f"b'{''.join(f'\\x{byte:02x}' for byte in byte_data)}'"
if __name__ == "__main__":
encrypted_key_base64 = json.load(open('Local State'))['os_crypt']['encrypted_key']
# print(f"Encrypted key:\n {encrypted_key_base64}")
try:
decrypted_key = decrypt_dpapi_blob(encrypted_key_base64)[1]
print(f"Decrypted key:\n {bytes_to_hex(decrypted_key)}")
except Exception as e:
print("Decryption failed:", str(e))
sys.exit(1)
# get current working directory path
cookies_db = os.path.join(os.getcwd(), "Cookies")
decrypt_cookies(cookies_db, decrypted_key)
# print(f"Decrypted key:\n {bytes_to_hex(decrypted_key)}")
通过这些函数,我可以获取明文cookie并验证,如果我在Chrome中手动复制cookie文本,我可以获取目标会话。
if cookie_name == "BITBUCKETSESSIONID":
print(f"Decrypted cookie (bitbucket): {decrypted_value.decode()}")
解密和加密来回工作也没有问题。
但是,如果我替换修改后的
Cookies
文件和 Local State
文件,Chrome 将不会读取迁移的 cookie。
我可以知道这里出了什么问题吗?
根据 Topaco 在评论中的建议,我通过以下方式修改了我的函数:
nonce = os.urandom(12)
)encrypt
更改为 encrypt_and_digest
,将 decrypt
更改为 decrypt_and_verify
encrypt_and_digest
返回的新验证标签和随机数存储在 encrypted_cookie
...以下是新功能:
encrypt.py
:
from win32.win32crypt import CryptProtectData
import base64
import sqlite3
import os
from Cryptodome.Cipher.AES import new, MODE_GCM # pip install pycryptodomex
import decrypt
import json
from os.path import expandvars
def encrypt_dpapi_blob(decrypted_blob):
encrypted_blob = CryptProtectData(decrypted_blob, DataDescr="Google Chrome", OptionalEntropy=None, Reserved=None, PromptStruct=None, Flags=0)
encrypted_blob = b'DPAPI' + encrypted_blob
encrypted_blob_base64 = base64.b64encode(encrypted_blob)
return encrypted_blob_base64
def encrypt_cookies(cookies_db, key):
sqlite3.enable_callback_tracebacks(True)
conn = sqlite3.connect(cookies_db)
query = "SELECT name, encrypted_value FROM cookies"
cursor = conn.execute(query)
query_res = cursor.fetchall()
for row in query_res:
cookie_name, decrypted_value = row
# print(f"Encrypting cookie: {cookie_name}")
if decrypted_value is None or len(decrypted_value) == 0:
# print("No decrypted value found.")
continue
nonce = os.urandom(12)
aes_cipher = new(key=key, mode=MODE_GCM, nonce=nonce)
# encrypted_value = aes_cipher.encrypt(decrypted_value[15: -16]) # wrong
encrypted_value, verification_tag = aes_cipher.encrypt_and_digest(decrypted_value[15: -16])
# print(f"Encrypted cookie:\n {decrypt.bytes_to_hex(encrypted_value)}\n {encrypted_value}")
# verification_tag = decrypted_value[-16:] # wrong
# print(f"Verification tag:\n {decrypt.bytes_to_hex(verification_tag)}\n {verification_tag}")
# nonce = decrypted_value[3:15] # wrong
# print(f"Nonce:\n {decrypt.bytes_to_hex(nonce)}\n {nonce}")
encrypted_cookie = b'\x76\x31\x30' +\
nonce +\
encrypted_value +\
verification_tag
query = f"UPDATE cookies SET encrypted_value = ? WHERE name = \"{cookie_name}\""
params = [encrypted_cookie]
cursor.execute(query, params)
# print("")
conn.commit()
conn.close()
def get_local_state_key():
local_state = json.load(open(expandvars('%LOCALAPPDATA%/Google/Chrome/User Data/Local State')))
encrypted_key = local_state['os_crypt']['encrypted_key']
decrypted_key = decrypt.decrypt_dpapi_blob(encrypted_key)[1]
return decrypted_key
# Example usage
if __name__ == "__main__":
cookies_db = os.path.join(os.getcwd(), "Cookies")
# print(f"Decrypted key:\n {decrypt.bytes_to_hex(key)}\n {key}")
# key = os.urandom(32)
# Using existing key
key = get_local_state_key()
encrypt_cookies(cookies_db, key)
# encrypted_key = encrypt_dpapi_blob(key)
# print(f"Encrypted key:\n {str(encrypted_key, 'utf-8')}")
# wrong
# local_state = json.load(open('Local State'))
# local_state['os_crypt']['encrypted_key'] = encrypted_key.decode()
# json.dump(local_state, open('Local State', 'w'))
decrypt.py
:
from win32.win32crypt import CryptUnprotectData
import base64
import sqlite3
import os
from Cryptodome.Cipher.AES import new, MODE_GCM # pip install pycryptodomex
import sys
import json
import encrypt
def decrypt_dpapi_blob(encrypted_blob):
encrypted_blob = base64.b64decode(encrypted_blob)[5:] # Leading bytes "DPAPI" need to be removed
decrypt_res = CryptUnprotectData(encrypted_blob, None, None, None, 0)
return decrypt_res
def decrypt_cookies(cookies_db, key):
sqlite3.enable_callback_tracebacks(True)
conn = sqlite3.connect(cookies_db)
query = "SELECT name, encrypted_value FROM cookies"
cursor = conn.execute(query)
query_res = cursor.fetchall()
for row in query_res:
cookie_name, encrypted_value = row
# print(f"Decrypting cookie: {cookie_name}")
if encrypted_value is None or len(encrypted_value) == 0:
# print("No encrypted value found.")
continue
aes_cipher = new(key=key, mode=MODE_GCM, nonce=encrypted_value[3:15])
# decrypted_value = aes_cipher.decrypt(encrypted_value[15: -16]) # wrong
decrypted_value = aes_cipher.decrypt_and_verify(encrypted_value[15: -16], encrypted_value[-16:])
# print(f"Decrypted cookie:\n {bytes_to_hex(decrypted_value)}\n {decrypted_value}")
if cookie_name == "BITBUCKETSESSIONID":
print(f"Decrypted cookie (bitbucket): {decrypted_value.decode()}")
verification_tag = encrypted_value[-16:]
# print(f"Verification tag:\n {bytes_to_hex(verification_tag)}\n {verification_tag}")
nonce = encrypted_value[3:15]
# print(f"Nonce:\n {bytes_to_hex(nonce)}\n {nonce}")
decrypted_cookie = b'\x76\x31\x30' +\
nonce +\
decrypted_value +\
verification_tag
query = f"UPDATE cookies SET encrypted_value = ? WHERE name = \"{cookie_name}\""
params = [decrypted_cookie]
cursor.execute(query, params)
# print("")
conn.commit()
conn.close()
# Custom function to display all bytes in the \x[something] format
def bytes_to_hex(byte_data):
return f"b'{''.join(f'\\x{byte:02x}' for byte in byte_data)}'"
# Example usage
if __name__ == "__main__":
# encrypted_key_base64 = json.load(open('Local State'))['os_crypt']['encrypted_key']
# print(f"Encrypted key:\n {encrypted_key_base64}")
try:
decrypted_key = encrypt.get_local_state_key()
print(f"Decrypted key:\n {bytes_to_hex(decrypted_key)}")
except Exception as e:
print("Decryption failed:", str(e))
sys.exit(1)
# get current working directory path
cookies_db = os.path.join(os.getcwd(), "Cookies")
decrypt_cookies(cookies_db, decrypted_key)
# print(f"Decrypted key:\n {bytes_to_hex(decrypted_key)}")
根据Topaco在评论中的建议,并根据我自己的尝试,我通过以下方式修改了我的功能:
encrypt.py
:
import argparse
import json
import os
import base64
import sqlite3
from os.path import expandvars
from Cryptodome.Cipher.AES import new, MODE_GCM # pip install pycryptodomex
from win32.win32crypt import CryptProtectData # pip install pywin32
import decrypt
def encrypt_dpapi_blob(decrypted_blob):
encrypted_blob = CryptProtectData(decrypted_blob, DataDescr="Google Chrome", OptionalEntropy=None, Reserved=None, PromptStruct=None, Flags=0)
encrypted_blob = b'DPAPI' + encrypted_blob
encrypted_blob_base64 = base64.b64encode(encrypted_blob)
return encrypted_blob_base64
def encrypt_cookies(cookies_db, key):
sqlite3.enable_callback_tracebacks(True)
conn = sqlite3.connect(cookies_db)
query = "SELECT name, encrypted_value FROM cookies"
cursor = conn.execute(query)
query_res = cursor.fetchall()
for row in query_res:
cookie_name, decrypted_value = row
if decrypted_value is None or len(decrypted_value) == 0:
continue
nonce = os.urandom(12)
aes_cipher = new(key=key, mode=MODE_GCM, nonce=nonce)
encrypted_value, verification_tag = aes_cipher.encrypt_and_digest(decrypted_value)
encrypted_cookie = b'\x76\x31\x30' +\
nonce +\
encrypted_value +\
verification_tag
query = f"UPDATE cookies SET encrypted_value = ? WHERE name = '{cookie_name}'"
params = [encrypted_cookie]
cursor.execute(query, params)
conn.commit()
conn.close()
def get_local_state_key():
local_state = json.load(open(expandvars('%LOCALAPPDATA%/Google/Chrome/User Data/Local State')))
encrypted_key = local_state['os_crypt']['encrypted_key']
decrypted_key = decrypt.decrypt_dpapi_blob(encrypted_key)[1]
return decrypted_key
if __name__ == "__main__":
# Arg: cookies_db
parser = argparse.ArgumentParser()
parser.add_argument("--cookies", help="Name of the cookies database file", default="Cookies")
args = parser.parse_args()
cookies_db = os.path.join(os.getcwd(), args.cookies)
key = get_local_state_key()
encrypt_cookies(cookies_db, key)
decrypt.py
:
import argparse
import base64
import os
import sqlite3
import sys
from Cryptodome.Cipher.AES import new, MODE_GCM # pip install pycryptodomex
from win32.win32crypt import CryptUnprotectData # pip install pywin32
import encrypt
def decrypt_dpapi_blob(encrypted_blob):
encrypted_blob = base64.b64decode(encrypted_blob)[5:] # Leading bytes "DPAPI" need to be removed
decrypt_res = CryptUnprotectData(encrypted_blob, None, None, None, 0)
return decrypt_res
def decrypt_cookies(cookies_db, key):
sqlite3.enable_callback_tracebacks(True)
conn = sqlite3.connect(cookies_db)
query = "SELECT name, encrypted_value FROM cookies"
cursor = conn.execute(query)
query_res = cursor.fetchall()
for row in query_res:
cookie_name, encrypted_value = row
if encrypted_value is None or len(encrypted_value) == 0:
continue
verification_tag = encrypted_value[-16:]
aes_cipher = new(key=key, mode=MODE_GCM, nonce=encrypted_value[3:15])
decrypted_value = aes_cipher.decrypt_and_verify(ciphertext=encrypted_value[15: -16], received_mac_tag=verification_tag)
# if cookie_name == "BITBUCKETSESSIONID":
# print(f"Decrypted cookie (bitbucket):\n {decrypted_value.decode()}")
# print(f"Verification tag (bitbucket):\n {bytes_to_hex(verification_tag)}")
# print(f"Nonce (bitbucket):\n {bytes_to_hex(nonce)}")
query = f"UPDATE cookies SET encrypted_value = ? WHERE name = '{cookie_name}'"
params = [decrypted_value]
cursor.execute(query, params)
conn.commit()
conn.close()
if __name__ == "__main__":
try:
decrypted_key = encrypt.get_local_state_key()
except Exception as e:
print("Decryption failed:", str(e))
sys.exit(1)
# Arg: cookies_db
parser = argparse.ArgumentParser()
parser.add_argument("--cookies", help="Name of the cookies database file", default="Cookies")
args = parser.parse_args()
cookies_db = os.path.join(os.getcwd(), args.cookies)
decrypt_cookies(cookies_db, decrypted_key)
现在这些功能可用于在计算机之间迁移 cookie,不会出现错误。