admin管理员组

文章数量:1332865

Problem: To read the chrome / edge cookies to extract the XSRF-Token and .AspNet.Cookies values of an user in the browser launched from a desktop application and save it in the registry keys

I used the below code and it was working fine for last 2 months and all of sudden a day back, Secret key was able to extract like asusual, but initialisation vector and encrypted password seems to be different than earlier, both the keys seems to be extracting , but faced with the issue : 'utf-8' codec can't decode byte 0xf0 in position 0: invalid continuation byte

due to the inproper value extraction, please refer to the values after the code.

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()

I think the issue is with the lines below :

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

Below are the values that's extracted :

Name: XSRF-TOKEN, encrypted value: 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@"`

Expected xsrf-token : v3eMK3iMPhdF8tumLxbjEHmSVqwT__4MFAhhFZH6-lP4ffXhfPvACW8oQYuxpQ8OZnI0y8-sVjwc4TobVGDjNTXFlrFI287t5B_SNah9yQSHVkppuRsT510cJXGT05IU9rlJibfMnu-iU4CRk--XyrZ7iOJSugxlKFukH8hDyJ--Gv_F6AUqIo6bFJ1i-10cu7bkZ4hLEvfhdk2q08tKyTn7e6oDfQ6nLicSoKIWjuPvEutpZIZQyK4li32GPirk_ysUKolUbLrl2AjwPVFrCZbgil8yHKyNrmAveY1b0lNNzsDljzsfF_TMLZazpYbbIvEarQuPe6wkL8sQuT-L9YpitNpeW8Wvfgyj96EWCbQ

Can someone please point out what's am missing? or the edge/chrome updated their encryption ?

Right now the encryption update seems to be start with V20 in which the cookie encryption standard has been updated from V10 to V20 since chrome version 127.

I referred the links :

Problem: To read the chrome / edge cookies to extract the XSRF-Token and .AspNet.Cookies values of an user in the browser launched from a desktop application and save it in the registry keys

I used the below code and it was working fine for last 2 months and all of sudden a day back, Secret key was able to extract like asusual, but initialisation vector and encrypted password seems to be different than earlier, both the keys seems to be extracting , but faced with the issue : 'utf-8' codec can't decode byte 0xf0 in position 0: invalid continuation byte

due to the inproper value extraction, please refer to the values after the code.

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()

I think the issue is with the lines below :

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

Below are the values that's extracted :

Name: XSRF-TOKEN, encrypted value: 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@"`

Expected xsrf-token : v3eMK3iMPhdF8tumLxbjEHmSVqwT__4MFAhhFZH6-lP4ffXhfPvACW8oQYuxpQ8OZnI0y8-sVjwc4TobVGDjNTXFlrFI287t5B_SNah9yQSHVkppuRsT510cJXGT05IU9rlJibfMnu-iU4CRk--XyrZ7iOJSugxlKFukH8hDyJ--Gv_F6AUqIo6bFJ1i-10cu7bkZ4hLEvfhdk2q08tKyTn7e6oDfQ6nLicSoKIWjuPvEutpZIZQyK4li32GPirk_ysUKolUbLrl2AjwPVFrCZbgil8yHKyNrmAveY1b0lNNzsDljzsfF_TMLZazpYbbIvEarQuPe6wkL8sQuT-L9YpitNpeW8Wvfgyj96EWCbQ

Can someone please point out what's am missing? or the edge/chrome updated their encryption ?

Right now the encryption update seems to be start with V20 in which the cookie encryption standard has been updated from V10 to V20 since chrome version 127.

I referred the links : https://ohyicong.medium/how-to-hack-chrome-password-with-python-1bedc167be3d

Share edited Nov 23, 2024 at 17:10 Topaco 49.6k4 gold badges45 silver badges79 bronze badges asked Nov 22, 2024 at 7:18 DhuruvanDhuruvan 9112 bronze badges 4
  • Please be more specific than "isn't happening". – Gaberocksall Commented Nov 22, 2024 at 7:28
  • @Gaberocksall I have updated the question, let me know if any more details is required, will add it to the question. – Dhuruvan Commented Nov 22, 2024 at 7:46
  • On surfing, In reference to the link security.googleblog/2024/07/… , I believe this is due to App-bound encryption update from chrome and edge, is there new way to implement the same solution by decrypting the app bound encryption. – Dhuruvan Commented Nov 22, 2024 at 8:03
  • @Topaco Where is your comment regarding v20 cookies in Microsoft Edge? Did you delete it? Because it is useful. Could you publish again? – Rougher Commented Jan 16 at 10:35
Add a comment  | 

1 Answer 1

Reset to default 1

Chrome:

The main difference to the decryption of v10 cookies is the determination of the AES key for decrypting the cookies.
For v10 cookies, the encrypted key (referred to as encrypted_key in the Local State JSON file) only had to be decrypted with DPAPI.
For v20, a three-stage decryption of the encrypted key (referred to as app_bound_encrypted_key in the Local State JSON file) is required: First, a DPAPI decryption must be performed using the system account, then a DPAPI decryption using the user account and finally an AES-GCM decryption.
There are various implementations on the web, e.g. here is a Python script for decrypting Chrome cookies (note the dependencies: pywin32, pypsexec and pycryptodome):

# from https://github/runassu/chrome_v20_decryption/blob/main/decrypt_chrome_v20_cookie.py
import os
import json
import sys
import binascii
from pypsexec.client import Client
from Crypto.Cipher import AES
import sqlite3
import pathlib

user_profile = os.environ['USERPROFILE']
local_state_path = rf"{user_profile}\AppData\Local\Google\Chrome\User Data\Local State"
cookie_db_path = rf"{user_profile}\AppData\Local\Google\Chrome\User Data\Default\Network\Cookies"

with open(local_state_path, "r") as f:
    local_state = json.load(f)

app_bound_encrypted_key = local_state["os_crypt"]["app_bound_encrypted_key"]

arguments = "-c \"" + """import win32crypt
import binascii
encrypted_key = win32crypt.CryptUnprotectData(binascii.a2b_base64('{}'), None, None, None, 0)
print(binascii.b2a_base64(encrypted_key[1]).decode())
""".replace("\n", ";") + "\""

c = Client("localhost")
c.connect()

try:
    c.create_service()

    assert(binascii.a2b_base64(app_bound_encrypted_key)[:4] == b"APPB")
    app_bound_encrypted_key_b64 = binascii.b2a_base64(
        binascii.a2b_base64(app_bound_encrypted_key)[4:]).decode().strip()

    # decrypt with SYSTEM DPAPI
    encrypted_key_b64, stderr, rc = c.run_executable(
        sys.executable,
        arguments=arguments.format(app_bound_encrypted_key_b64),
        use_system_account=True
    )

    # decrypt with user DPAPI
    decrypted_key_b64, stderr, rc = c.run_executable(
        sys.executable,
        arguments=arguments.format(encrypted_key_b64.decode().strip()),
        use_system_account=False
    )

    decrypted_key = binascii.a2b_base64(decrypted_key_b64)[-61:]
    assert(decrypted_key[0] == 1)

finally:
    c.remove_service()
    c.disconnect()

# decrypt key with AES256GCM
# aes key from elevation_service.exe
aes_key = binascii.a2b_base64("sxxuJBrIRnKNqcH6xJNmUc/7lE0UOrgWJ2vMbaAoR4c=")

# [flag|iv|ciphertext|tag] decrypted_key
# [1byte|12bytes|variable|16bytes]
iv = decrypted_key[1:1+12]
ciphertext = decrypted_key[1+12:1+12+32]
tag = decrypted_key[1+12+32:]

cipher = AES.new(aes_key, AES.MODE_GCM, nonce=iv)
key = cipher.decrypt_and_verify(ciphertext, tag)
print(binascii.b2a_base64(key))

# fetch all v20 cookies
con = sqlite3.connect(pathlib.Path(cookie_db_path).as_uri() + "?mode=ro", uri=True)
cur = con.cursor()
r = cur.execute("SELECT host_key, name, CAST(encrypted_value AS BLOB) from cookies;")
cookies = cur.fetchall()
cookies_v20 = [c for c in cookies if c[2][:3] == b"v20"]
con.close()

# decrypt v20 cookie with AES256GCM
# [flag|iv|ciphertext|tag] encrypted_value
# [3bytes|12bytes|variable|16bytes]
def decrypt_cookie_v20(encrypted_value):
    cookie_iv = encrypted_value[3:3+12]
    encrypted_cookie = encrypted_value[3+12:-16]
    cookie_tag = encrypted_value[-16:]
    cookie_cipher = AES.new(key, AES.MODE_GCM, nonce=cookie_iv)
    decrypted_cookie = cookie_cipher.decrypt_and_verify(encrypted_cookie, cookie_tag)
    return decrypted_cookie[32:].decode('utf-8')

for c in cookies_v20:
    print(c[0], c[1], decrypt_cookie_v20(c[2]))

Successfully tested on Windows 11 Pro, 24H2 with Google Chrome, v132.0.6834.83.


Edge:

The same applies to Edge, apart from the following differences:

  • The decryption process of the key (for decrypting the cookies) is not three-stage, but only two-stage, i.e. there are only the two DPAPI decryptions, the AES/GCM decryption is omitted.
  • The paths for Local State file and Cookies database are different and must be adapted.

Therefore, the following changes are necessary in the above Python code:

...
user_profile = os.environ['USERPROFILE']
local_state_path = rf"{user_profile}\AppData\Local\Microsoft\Edge\User Data\Local State"
cookie_db_path = rf"{user_profile}\AppData\Local\Microsoft\Edge\User Data\Default\Network\Cookies"
...
try:
    c.create_service()

    assert(binascii.a2b_base64(app_bound_encrypted_key)[:4] == b"APPB")
    app_bound_encrypted_key_b64 = binascii.b2a_base64(
        binascii.a2b_base64(app_bound_encrypted_key)[4:]).decode().strip()

    # decrypt with SYSTEM DPAPI
    encrypted_key_b64, stderr, rc = c.run_executable(
        sys.executable,
        arguments=arguments.format(app_bound_encrypted_key_b64),
        use_system_account=True
    )

    # decrypt with user DPAPI
    decrypted_key_b64, stderr, rc = c.run_executable(
        sys.executable,
        arguments=arguments.format(encrypted_key_b64.decode().strip()),
        use_system_account=False
    )

finally:
    c.remove_service()
    c.disconnect()

key = binascii.a2b_base64(decrypted_key_b64)[-32:]
print(binascii.b2a_base64(key))

# fetch all v20 cookies
...

Successfully tested on Windows 11 Pro, 24H2 with Microsoft Edge, v131.0.2903.146.


Another approach is to use Chromium's remote debugger port to bypass the appbound encryption. This is described in detail here, a Python script can be found e.g. here. However, it can't be ruled out that this approach will no longer work at some point due to additional protections.

This also works for Edge if the paths are adapted.

本文标签: Cookies Couldn39t able to extract chromeedge v20 cookies using AES Decryption in pythonStack Overflow