Skip to content
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

Open
thewh1teagle opened this issue Oct 25, 2024 · 9 comments
Open

Add support for Brave and Edge #7

thewh1teagle opened this issue Oct 25, 2024 · 9 comments

Comments

@thewh1teagle
Copy link

thewh1teagle commented Oct 25, 2024

Brave

local_state_path = rf"{user_profile}\AppData\Local\BraveSoftware\Brave-Browser\User Data\Local State"
cookie_db_path = rf"{user_profile}\AppData\Local\BraveSoftware\Brave-Browser\User Data\Default\Network\Cookies"

Edge

local_state_path = rf"{user_profile}\AppData\Local\Microsoft\Edge\User Data\Local State"
cookie_db_path = rf"{user_profile}\AppData\Local\Microsoft\Edge\User Data\Default\Network\Cookies"

Errors:

  File "D:\rookie\rookie\test.py", line 57, in <module>
    assert(decrypted_key[0] == 1)
           ^^^^^^^^^^^^^^^^^^^^^
AssertionError

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 key

decrypted_key = binascii.a2b_base64(decrypted_key_b64)[-61:]

aes_key = binascii.a2b_base64("sxxuJBrIRnKNqcH6xJNmUc/7lE0UOrgWJ2vMbaAoR4c=")

@thewh1teagle
Copy link
Author

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]))

@runassu
Copy link
Owner

runassu commented Oct 28, 2024

Thanks, I'll look into it later

@thewh1teagle
Copy link
Author

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]))

@blul1ghtz
Copy link

what method was used to get the original hardcoded key for aes decryption with chrome? ("sxxuJBrIRnKNqcH6xJNmUc/7lE0UOrgWJ2vMbaAoR4c=")

@thewh1teagle
Copy link
Author

@runassu

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!

@runassu
Copy link
Owner

runassu commented Oct 30, 2024

@thewh1teagle
@blul1ghtz

In the Chromium source code, you'll find Decrypt using the SYSTEM dpapi store and Decrypt using the user store.

  • SYSTEM DPAPI Store Decryption

https://github.com/chromium/chromium/blob/3abae3a11040d99cb863eedaec5b1730cb5978bc/chrome/elevation_service/elevator.cc#L169

  • User Store Decryption

https://github.com/chromium/chromium/blob/3abae3a11040d99cb863eedaec5b1730cb5978bc/chrome/elevation_service/elevator.cc#L186

The AES decryption step is part of the internal code, so reverse engineering of the release binary file is required.

  • Internal function NOT in source code

https://github.com/chromium/chromium/blob/3abae3a11040d99cb863eedaec5b1730cb5978bc/chrome/elevation_service/elevator.cc#L226

  • BUILD Script that including internal code

https://github.com/chromium/chromium/blob/3abae3a11040d99cb863eedaec5b1730cb5978bc/chrome/elevation_service/BUILD.gn#L99

After understanding the code above, I used Ghidra to locate the PostProcessData function in elevation_service.exe, then attached x64dbg to the process for further analysis.
However, if you're open to it, relying solely on static analysis or debugging is sufficient, as this program does not employ obfuscation. If you have experience in reverse engineering cryptographic functions, the characteristics of the AES function are easy to identify.

@blul1ghtz
Copy link

@runassu I was able to find PostProcessData function but couldn't find a way to extract any value.
definitely requires some experience in reverse engineering but thanks for letting us know

@jamiekarvans
Copy link

@thewh1teagle @blul1ghtz

In the Chromium source code, you'll find Decrypt using the SYSTEM dpapi store and Decrypt using the user store.

  • SYSTEM DPAPI Store Decryption

https://github.com/chromium/chromium/blob/3abae3a11040d99cb863eedaec5b1730cb5978bc/chrome/elevation_service/elevator.cc#L169

  • User Store Decryption

https://github.com/chromium/chromium/blob/3abae3a11040d99cb863eedaec5b1730cb5978bc/chrome/elevation_service/elevator.cc#L186

The AES decryption step is part of the internal code, so reverse engineering of the release binary file is required.

  • Internal function NOT in source code

https://github.com/chromium/chromium/blob/3abae3a11040d99cb863eedaec5b1730cb5978bc/chrome/elevation_service/elevator.cc#L226

  • BUILD Script that including internal code

https://github.com/chromium/chromium/blob/3abae3a11040d99cb863eedaec5b1730cb5978bc/chrome/elevation_service/BUILD.gn#L99

After understanding the code above, I used Ghidra to locate the PostProcessData function in elevation_service.exe, then attached x64dbg to the process for further analysis. However, if you're open to it, relying solely on static analysis or debugging is sufficient, as this program does not employ obfuscation. If you have experience in reverse engineering cryptographic functions, the characteristics of the AES function are easy to identify.

hey thanks for the info,
you have really good knowlege in programming and finding how things work out, can i ask you how long did it took you to become this good? and can i also know about how does this pays back to you in life? maybe an estimate of how much you earn or what job do you have and how much it pays you?

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.
thanks a lot if you consider to put the time and answer this, i apperciate it already.

@yourdataleaked
Copy link

Now we will try to sniff Edge and Yandex Browser for get elevation_keys
Will notify you or add PR for it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants