I'm trying to use Chromium cookies in Python, because Chromium encrypts its cookies using AES (with CBC) I need to reverse this.
I can recover the AES key from OS X's Keychain (it's stored in Base 64):
security find-generic-password -w -a Chrome -s Chrome Safe Storage
# From Python:
python -c 'from subprocess import PIPE, Popen; print(Popen(['security', 'find-generic-password', '-w', '-a', 'Chrome', '-s', 'Chrome Safe Storage'], stdout=PIPE).stdout.read().strip())'
Here's the code I have, all I'm missing is decrypting the cookies:
from subprocess import PIPE, Popen
from sqlite3 import dbapi2
def get_encryption_key():
cmd = ['security', 'find-generic-password', '-w', '-a', 'Chrome', '-s', 'Chrome Safe Storage']
return Popen(cmd, stdout=PIPE).stdout.read().strip().decode('base-64')
def get_cookies(database):
key = get_encryption_key()
with dbapi2.connect(database) as conn:
conn.rollback()
rows = conn.cursor().execute('SELECT name, encrypted_value FROM cookies WHERE host_key like ".example.com"')
cookies = {}
for name, enc_val in rows:
val = decrypt(enc_val, key) # magic missing
cookies[name] = val
return cookies
I tried a bunch of things with pyCrypto's AES module but:
enc_val
is not a multiple of 16 in lengthHere are some links that seem useful:
Can you help me figure this out?
Decrypt the cookie and check the digest: Decrypt de key of the cookie: do Base64 decoding, then decrypt it using your institution's private RSA key. Decrypt the data using the decrypted AES key. Check the digest using secutix public certificate. The following example in java will show you how to proceed.
A recent change to Chromium has the consequence that all desktop versions of Google Chrome will encrypt stored cookies in the near future (while Chrome OS and Android use fully encrypted profiles already). This only affects new cookies saved to the system after Chrome is updated with the new implementation.
You're on the right track! I've been working on this for a few days and finally figured it out. (Many thanks to the OP for the helpful links to the Chromium source.)
I've put up a post with a little more detail and a working script, but here is the basic idea:
#! /usr/bin/env python3
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
# Function to get rid of padding
def clean(x):
return x[:-x[-1]].decode('utf8')
# replace with your encrypted_value from sqlite3
encrypted_value = ENCRYPTED_VALUE
# Trim off the 'v10' that Chrome/ium prepends
encrypted_value = encrypted_value[3:]
# Default values used by both Chrome and Chromium in OSX and Linux
salt = b'saltysalt'
iv = b' ' * 16
length = 16
# On Mac, replace MY_PASS with your password from Keychain
# On Linux, replace MY_PASS with 'peanuts'
my_pass = MY_PASS
my_pass = my_pass.encode('utf8')
# 1003 on Mac, 1 on Linux
iterations = 1003
key = PBKDF2(my_pass, salt, length, iterations)
cipher = AES.new(key, AES.MODE_CBC, IV=iv)
decrypted = cipher.decrypt(encrypted_value)
print(clean(decrypted))
@n8henrie's answer worked for me, but in my environment with Ubuntu, Chrome no longer use 'peanuts' as password, instead it's stored in gnome keyring. I managed to get the password for decryption using secretstorage package like this:
import secretstorage
bus = secretstorage.dbus_init()
collection = secretstorage.get_default_collection(bus)
for item in collection.get_all_items():
if item.get_label() == 'Chrome Safe Storage':
MY_PASS = item.get_secret()
break
else:
raise Exception('Chrome password not found!')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With