Created
October 29, 2024 16:06
-
-
Save thewh1teagle/293e567b3063f05b6f46381b03a6a6e4 to your computer and use it in GitHub Desktop.
Chrome v20 decryption with Windows API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| 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])) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
OSError: RtlAdjustPrivilege failed with status: -3fffff9f