Author: Valli-Nayagam Chokkalingam
App-Bound Encryption only checks where you’re standing, not who you are. Drop a binary in Chrome’s folder and it hands over the master key.

Contents
Introduction
Chrome 127 introduced App-Bound Encryption to kill cookie theft. The pitch: cookies and passwords get encrypted with a master key that only Chrome’s SYSTEM-level elevation service can decrypt. Any other process asking for the key gets rejected.
It works. Mostly.
There’s one gap in the design. The elevation service validates the path of the caller, not its identity. Put a binary in C:\Program Files\Google\Chrome\Application\ and the service hands over the key like you’re Chrome itself.
This technique is public. CyberArk Labs published the analysis in June 2025. The original CVE (CVE-2025-34092) was later rejected by the NVD — the technique itself still works.
xaitax’s ChromElevator implements a stealthier injected version. What I couldn’t find anywhere was a clear explanation of the three failure modes you hit if you try to build it yourself. That’s what this post is.
The Setup
Chrome stores everything it saves under a single 32-byte AES-256 master key:
- Cookies
- Passwords
- Credit cards
- IBANs
- Autofill
One key, five databases. The key itself lives in Local State, wrapped twice with DPAPI. The outer layer is SYSTEM, so only the elevation service can unwrap it. The inner layer is the user’s context, so it stays tied to your account.
The service that does the unwrapping exposes a COM interface called IElevator. Chrome calls DecryptData on it at startup, gets the key, and uses it to decrypt everything on demand.
The plan: call that same interface ourselves.
Error One: Wrong Neighborhood
First version ran from C:\testing\. Straight rejection:
DecryptData failed (HRESULT: 0x8004a004)
That HRESULT is Chrome’s path validation failure. The elevation service normalizes the caller’s executable path and compares it against a value baked into the encrypted blob. It trims the filename and version directories, so chrome.exe running from Application\ normalizes to C:\Program Files\Google\Chrome. My binary in C:\testing\ normalized to C:\testing. No match.
Fix: copy the tool into Chrome’s directory and run it from there. The service accepts the call.
Error Two: No Handshake
Second attempt, right directory, still failing:
DecryptData failed (HRESULT: 0x8004a003)LastError: 5
0x8004a003 is EPT_S_NOT_REGISTERED. LastError 5 is ERROR_ACCESS_DENIED. Both point at the same thing: the RPC channel between my process and the elevation service was unauthenticated.
COM calls that cross process boundaries need the proxy configured with an authentication level. Without it, the service sees a call from “nobody” and refuses.
The fix is one function:
CoSetProxyBlanket(elevator, RPC_C_AUTHN_DEFAULT, RPC_C_AUTHZ_DEFAULT, COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // encrypt the RPC RPC_C_IMP_LEVEL_IMPERSONATE, // let the server impersonate us nullptr, EOAC_DYNAMIC_CLOAKING); // use our token, not the parent's
Every serious writeup glosses over this. It’s the reason most custom PoCs stall at 0x8004a003.
Error Three: Wrong User
Third attempt, right directory, proxy configured correctly, still ERROR_ACCESS_DENIED.
This one stung. DPAPI is bound to the user’s logon token. The master key was encrypted under the normal user’s context. I was running the tool from an elevated prompt as Administrator. Different token, different DPAPI context, decryption refused.
The fix is counterintuitive:
- Copy the exe into Chrome’s folder as Administrator (file permission needed)
- Run it as the normal user (DPAPI context needed)
Two privilege requirements, two separate steps. If you use CreateProcess from the copy step, the child inherits your elevated token and it still fails. Do it manually.
The Working Flow
Once those three were sorted, the whole thing ran in under two seconds.
Read app_bound_encrypted_key from Local State. Base64-decode it. Strip the APPB magic. Hand the remaining blob to IElevator::DecryptData. The service unwraps it, validates the caller’s path, and returns the 32-byte master key.
Then it’s mechanical:
- Open
Network\Cookies(SQLite) SELECT host_key, name, encrypted_value FROM cookies- For each row, check the blob starts with
v20 - AES-256-GCM decrypt with the master key
- Repeat for
Login DataandWeb Data
The v20 format is [v20][nonce 12][ciphertext][tag 16]. Windows BCrypt handles it with three calls. The GCM tag means decryption is self-validating, so you never get garbage output — only the real value or nothing.
Output lands in a JSON file with plaintext cookies, passwords, cards, and the master key.
What I Actually Got
[*] Cookies: 92 total, 92 decrypted[*] Passwords: 1 total, 1 decrypted[*] Cards: 1 total, 1 decrypted...........

Figure 1. End-to-end run against a lab profile.

Figure 2. Plaintext dump at C:\testing\abe_dump.json.
Once you have the master key, the databases are open books. Copy them to a USB stick, walk to another machine, decrypt offline with a Python script and the cryptography library. No Chrome, no COM, no service dependency.
That’s the real problem with ABE. It’s a single point of failure. Compromise the key once and every secret in the profile falls.
Defender for Business Results
I ran the same PoC against a Windows 11 lab machine with Defender for Business enabled with no exclusions.
| Stage | Result |
|---|---|
| Copy to Chrome’s install dir | allowed |
| First execution from Chrome’s folder | allowed |
CoCreateInstance(CLSID_Elevator) | not detected |
Reads of Local State and DB files | not detected |
| End-to-end run | completed |
Alert generated: None
Takeaway: Defender for Business produced no alerts or any kind of prevention at any stage of the execution chain.

Figure 3. Defender for Business result on the same machine.
The Flaw
The whole design hinges on the elevation service being able to tell who’s calling. It checks a path string. A directory name is not an identity.
Chrome’s crypto is fine. AES-256-GCM does its job. The problem is the access control wrapped around it — one folder check guarding a class of attacks worth billions to infostealer operators. It held for about two years. The proper fix is moving decryption into Chrome’s own process where no external caller can reach it, which is underway in newer builds.
Detection
If you’re defending this, look for:
- Non-
chrome.exebinaries inC:\Program Files\Google\Chrome\Application\ - Any process outside Chrome instantiating the
IElevatorCOM object - Reads of
Local State,Login Data, andWeb Datafrom the same unexpected process - Sysmon Event 1 for process creation in Chrome’s install directory
None of these alone is damning. Together, they’re a clean signature.
Companion Repository
The full PoC — source, build instructions, and the Defender test results referenced above — is at github.com/AdversaryCraft/chrome-abe-extract. For authorized lab use only.
References
- CyberArk Labs – C4 Bomb: Blowing Up Chrome’s AppBound Cookie Encryption (June 2025): https://web.archive.org/web/20251116044735/https://www.cyberark.com/resources/threat-research-blog/c4-bomb-blowing-up-chromes-appbound-cookie-encryption
- xaitax – ChromElevator (DLL injection variant): https://github.com/xaitax/Chrome-App-Bound-Encryption-Decryption
- Gen Digital – VoidStealer: Debugging Chrome to Steal Its Secrets (March 2026): https://www.gendigital.com/blog/insights/research/voidstealer-abe-bypass
- Google Security Blog – Improving the security of Chrome cookies on Windows (July 2024): https://security.googleblog.com/2024/07/improving-security-of-chrome-cookies-on.html
- NVD – CVE-2025-34092 (rejected): https://nvd.nist.gov/vuln/detail/CVE-2025-34092
- Companion repository – chrome-abe-extract: https://github.com/AdversaryCraft/chrome-abe-extract



















































































