Quantcast
Channel: Active questions tagged python - Stack Overflow
Viewing all articles
Browse latest Browse all 23276

Decrypt & Re-Encrypt Chrome cookies

$
0
0

I'm trying to decrypt Chrome's cookie SQLite DB, and move the decrypted cookies to another computer (browser), and re-encrypt the DB, and replicate sessions.

Here is what I plan:

  1. Decrypt AES key from Local State in C:\Users\[username]\AppData\Local\Google\Chrome\User Data\Local State using DPAPI
  2. Use decrypted key to decrypt Cookie DB in C:\Users\[username]\AppData\Local\Google\Chrome\User Data\Default\Network\Cookies
  3. Copy the decrypted Cookie DB to another computer
  4. Generate random AES key/nonce and encrypt the plaintext Cookie DB transferred on the other computer. Substitute original Cookies DB on the other computer.
  5. Encrypt AES key using DPAPI and substitute associated entry in Local State on the other computer.

And I have the following 2 Python files to do things described above:

encrypt.py:

from win32.win32crypt import CryptProtectDataimport base64import sqlite3import osfrom Cryptodome.Cipher.AES import new, MODE_GCM # pip install pycryptodomeximport decryptimport jsondef 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_base64def 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 CryptUnprotectDataimport base64import sqlite3import osfrom Cryptodome.Cipher.AES import new, MODE_GCM # pip install pycryptodomeximport sysimport jsondef 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_resdef 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)}")

With these functions, I can get the plaintext cookie and verified that, if I manually copy the cookie text in Chrome, I can get the target session.

if cookie_name == "BITBUCKETSESSIONID":            print(f"Decrypted cookie (bitbucket): {decrypted_value.decode()}")

Decrypting & Encrypting back and forth work without problem as well.

However, if I substitute the modified Cookies file and Local State file, Chrome will not read the migrated cookie.

May I know what is wrong here?


As suggested by Topaco in the comments, I modified my functions in the following ways:

  1. Using existing local AES key on the other computer
  2. Generate new random nonce (nonce = os.urandom(12))
  3. Change encrypt to encrypt_and_digest, and decrypt to decrypt_and_verify
  4. Store new verification tag returned by encrypt_and_digest& nonce in encrypted_cookie

... and here are the new functions:encrypt.py:

from win32.win32crypt import CryptProtectDataimport base64import sqlite3import osfrom Cryptodome.Cipher.AES import new, MODE_GCM # pip install pycryptodomeximport decryptimport jsonfrom os.path import expandvarsdef 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_base64def 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 usageif __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 CryptUnprotectDataimport base64import sqlite3import osfrom Cryptodome.Cipher.AES import new, MODE_GCM # pip install pycryptodomeximport sysimport jsonimport encryptdef 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_resdef 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] formatdef bytes_to_hex(byte_data):    return f"b'{''.join(f'\\x{byte:02x}' for byte in byte_data)}'"# Example usageif __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)}")

Viewing all articles
Browse latest Browse all 23276

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>