How to bypass App-Bound Encryption?

Faced with the rise of stealers (malware designed to exfiltrate sensitive data such as cookies, passwords, or files of interest), Google introduced App-Bound Encryption (ABE) in Chromium.

Since version 127, they have implemented new protections on Windows that enhance DPAPI by providing application-bound encryption primitives. Instead of allowing any application running as a logged-in user to access this data, Chrome can now encrypt data tied to the application’s identity, similar to how the keychain works on macOS.

The plan is to migrate all types of secrets to this new system, starting with cookies in Chromium v127. So, is it truly secure? ☝🏻🤓


How it works in Chromium now (v127+)

The Chromium team began developing a new method for storing secrets on Windows. According to the technical documentation, the project was first discussed in early 2021.

Since then, the project, initially called Chrome Elevated Data Service and later renamed App-Bound Encryption, was approved and implemented for the first time in 2022. This feature was officially rolled out in July 2024 and first announced in a Google blog post titled “Improving the security of Chrome cookies on Windows.”

Image description

The diagram shows that instead of relying on user-scoped DPAPI, Google decided to redirect calls to its own elevated service, typically named “GoogleChromeElevationService.” Rather than calling CryptProtectData in Chrome, the main Chrome key is now encrypted using this dedicated service and stored in the Local State file as app_bound_encrypted_key.

%LOCALAPPDATA%\Google\Chrome\User Data\Local State

When DPAPI runs under an elevated service, it uses the system’s master key instead of the user’s master key, which is inaccessible to standard privileged processes. This means decrypting Chrome’s main key via CryptUnprotectData becomes impossible.

Additionally, to encrypt or decrypt the key as an elevated service, the service checks whether the application originates from the Chrome directory. An executable like this one on the desktop would not be able to interact with the elevated service, which would refuse the exchange:

C:\Users\Hacker\Desktop\NotChrome.exe

Moreover, an Event Log ID 257 is created, alerting administrators or the SOC that an unknown process attempted to exchange keys.

Image description

Simply Verify the Path?

Before the introduction of path validation, the security of App-Bound Encryption primarily relied on digital signatures to ensure the integrity and legitimacy of service calls. The process involved allowing calls from legitimate executables like Chrome to perform encryption and decryption operations via signature validation. Ultimately, it was decided to simplify this mechanism by introducing validation based on the execution path.


How to Bypass This New Security?

Chrome’s App-Bound Encryption secures sensitive data (currently only cookies, but it is expected to extend to other types of data) using an AES key encrypted via DPAPI and decrypted exclusively by an elevated COM service, the GoogleChromeElevationService. This service, used for sensitive operations, is identified and accessed via specific GUIDs: CLSID for COM classes and IID for their interfaces.

We leverage these GUIDs to establish a COM connection with the elevation service and retrieve the AES key in plaintext, effectively bypassing the protection.

The necessary CLSID and IID for the PoC are found in two critical Chromium project files:

  1. google_chrome_install_modes.cc: Defines the CLSIDs of elevation services for each Chrome version (Stable, Beta, Dev, Canary). It also includes the associated IIDs.
  2. elevation_service_idl.idl: Defines the IIDs for the COM interfaces exposed by the elevation service, including variants like IElevator and IElevatorChromium.

Here’s a table mapping each Chrome version to its CLSID and the IID of the interface used:

Chrome VersionCLSIDIID (Interface)
Chrome Stable{4dc3e181-e14b-4a21-b022-59fc669b0914}{B88C45B9-8825-4629-B83E-77CC67D9CEED} (IElevatorChromium)
Chrome Beta{c77c8482-92a3-4ff0-96e7-b86a38e75459}{463ABECF-410D-407F-8AF5-0DF35A005CC8} (IElevatorChrome)
Chrome Dev{a566df5a-4381-4d2c-b464-62f87880c2f5}{BB2AA26B-343A-4072-8B6F-80557B8CE571} (IElevatorChromeDev)
Chrome Canary{f601d0c5-2396-4a29-b019-d9b07fca1a30}{4F7CE041-28E9-484F-9DD0-61A8CACEFEE4} (IElevatorChromeCanary)

These GUIDs allow our PoC to specifically target the installed Chrome version (or another Chromium-based browser like Brave).

To communicate with this COM service, we use the DLLs ole32.dll and oleaut32.dll.


PoC

To interact with the COM service, we start by initializing COM using a call to CoInitializeEx with the COINIT_APARTMENTTHREADED mode. Then, we use CoCreateInstance to instantiate the IElevator interface, identified by its specific GUIDs (CLSID_Elevator and IID_IElevator). This interface exposes the necessary functions for encrypting and decrypting data via the Chrome Elevated Data service.

Before invoking methods on this interface, we configure a proxy blanket with CoSetProxyBlanket. This step sets the required authentication and impersonation levels to secure RPC calls.

The encrypted key is stored in Chrome’s Local State file, located in the user’s directory (%LOCALAPPDATA%\Google\Chrome\User Data\Local State). This JSON file contains an os_crypt object that holds the key in the app_bound_encrypted_key field. We read this file, extract the Base64-encoded value, and decode it. Once decoded, we verify the presence of the APPB prefix (used as a marker by Chrome) before isolating the encrypted key data.

The encrypted key is transformed into a BSTR, a data type used by COM interfaces to transmit Unicode strings. For this, we use the SysAllocStringByteLen function from the oleaut32.dll library. This BSTR is then passed to the DecryptData method exposed by the IElevator interface.

The DecryptData method takes the BSTR containing the encrypted key as input and returns another BSTR containing the decrypted key, along with an error code indicating the success or failure of the operation. Once the BSTR containing the plaintext key is returned by DecryptData, we use the SysStringByteLen function to determine the data length. The data is then copied into a byte array for further manipulation. Finally, the plaintext key is displayed.

Image description

A script (example in the previous article) that includes the App-Bound portion allows secrets to be retrieved in plaintext by specifying the decryption key.


Conclusion

Despite more than three years of development and numerous compromises that diminished the initially planned robustness, this solution only offers a slight increase in security by requiring administrator privileges to access data. However, it remains vulnerable to a relatively simple bypass.


References:

  1. Chromium Elevation Service IDL
  2. Google Security Blog
  3. Chrome app-bound encryption Service
  4. Microsoft COM Library