Katz and Mouse Game: MaaS Infostealers Adapt to Patched Chrome Defenses

Elastic Security Labs breaks down bypass implementations from the infostealer ecosystem’s reaction to Chrome 127's Application-Bound Encryption scheme.

20 min readMalware analysis
Katz and Mouse Game:  MaaS Infostealers Adapt to Patched Chrome Defenses

Introduction

In July, Google announced a new protection mechanism for cookies stored within Chrome on Windows, known as Application-Bound Encryption. There is no doubt this security implementation has raised the bar and directly impacted the malware ecosystem. After months with this new feature, many infostealers have written new code to bypass this protection (as the Chrome Security Team predicted) in order to stay competitive in the market and deliver capabilities that reliably retrieve cookie data from Chrome browsers.

Elastic Security Labs has been tracking a subset of this activity, identifying multiple techniques used by different malware families to circumvent App-Bound Encryption. While the ecosystem is still evolving in light of this pressure, our goal is to share technical details that help organizations understand and defend against these techniques. In this article, we will cover the different methods used by the following infostealer families:

  • STEALC/VIDAR
  • METASTEALER
  • PHEMEDRONE
  • XENOSTEALER
  • LUMMA

Key takeaways

  • Latest versions of infostealers implement bypasses around Google’s recent cookie protection feature using Application-Bound Encryption
  • Techniques include integrating offensive security tool ChromeKatz, leveraging COM to interact with Chrome services and decrypt the app-bound encryption key, and using the remote debugging feature within Chrome
  • Defenders should actively monitor for different cookie bypass techniques against Chrome on Windows in anticipation of future mitigations and bypasses likely to emerge in the near- to mid-term
  • Elastic Security provides mitigations through memory signatures, behavioral rules, and hunting opportunities to enable faster identification and response to infostealer activity

Background

Generically speaking, cookies are used by web applications to store visitor information in the browser the visitor uses to access that web app. This information helps the web app track that user, their preferences, and other information from location to location– even across devices.

The authentication token is one use of the client-side data storage structures that enables much of how modern web interactivity works. These tokens are stored by the browser after the user has successfully authenticated with a web application. After username and password, after multifactor authentication (MFA) via one-time passcodes or biometrics, the web application “remembers” your browser is you via the exchange of this token with each subsequent web request.

A malicious actor who gets access to a valid authentication token can reuse it to impersonate the user to that web service with the ability to take over accounts, steal personal or financial information, or perform other actions as that user such as transfer financial assets.

Cybercriminals use infostealers to steal and commoditize this type of information for their financial gain.

Google Chrome Cookie Security

Legacy versions of Google Chrome on Windows used the Windows native Data Protection API (DPAPI) to encrypt cookies and protect them from other user contexts. This provided adequate protection against several attack scenarios, but any malicious software running in the targeted user’s context could decrypt these cookies using the DPAPI methods directly. Unfortunately, this context is exactly the niche that infostealers often find themselves in after social engineering for initial access. The DPAPI scheme is now well known to attackers with several attack vectors; from local decryption using the API, to stealing the masterkey and decrypting remotely, to abusing the domain-wide backup DPAPI key in an enterprise environment.

With the release of Chrome 127 in July 2024, Google implemented Application-Bound Encryption of browser data. This mechanism directly addressed many common DPAPI attacks against Windows Chrome browser data–including cookies. It does this by storing the data in encrypted datafiles, and using a service running as SYSTEM to verify any decryption attempts are coming from the Chrome process before returning the key to that process for decryption of the stored data.

While it is our view that this encryption scheme is not a panacea to protect all browser data (as the Chrome Security Team acknowledges in their release) we do feel it has been successful in driving malware authors to TTPs that are more overtly malicious, and easier for defenders to identify and respond to.

Stealer Bypass Techniques, Summarized

The following sections will describe specific infostealer techniques used to bypass Google’s App-Bound Encryption feature as observed by Elastic. Although this isn’t an exhaustive compilation of bypasses, and development of these families is ongoing, they represent an interesting dynamic within the infostealer space showing how malware developers responded to Google’s recently updated security control. The techniques observed by our team include:

  • Remote debugging via Chrome’s DevTools Protocol
  • Reading process memory of Chrome network service process (ChromeKatz and ReadProcessMemory (RPM))
  • Elevating to SYSTEM then decrypting app_bound_encryption_key with the DecryptData method of GoogleChromeElevationService through COM

STEALC/VIDAR

Our team observed new code introduced to STEALC/VIDAR related to the cookie bypass technique around September 20th. These were atypical samples that stood out from previous versions and were implemented as embedded 64-bit PE files along with conditional checks. Encrypted values in the SQLite databases where Chrome stores its data are now prefixed with v20, indicating that the values are now encrypted using application-bound encryption.

STEALC was introduced in 2023 and was developed with “heavy inspiration” from other more established stealers such as RACOON and VIDAR. STEALC and VIDAR have continued concurrent development, and in the case of App-Bound Encryption bypasses have settled on the same implementation.

During the extraction of encrypted data from the databases the malware checks for this prefix. If it begins with v20, a child process is spawned using the embedded PE file in the .data section of the binary. This program is responsible for extracting unencrypted cookie values residing in one of Chrome's child processes.

This embedded binary creates a hidden desktop via OpenDesktopA / CreateDesktopA then uses CreateToolhelp32Snapshot to scan and terminate all chrome.exe processes. A new chrome.exe process is then started with the new desktop object. Based on the installed version of Chrome, the malware selects a signature pattern for the Chromium feature CookieMonster, an internal component used to manage cookies.

We used the signature patterns to pivot to existing code developed for an offensive security tool called ChromeKatz. At this time, the patterns have been removed from the ChromeKatz repository and replaced with a new technique. Based on our analysis, the malware author appears to have reimplemented ChromeKatz within STEALC in order to bypass the app-bound encryption protection feature.

Once the malware identifies a matching signature, it enumerates Chrome’s child processes to check for the presence of the --utility-sub-type=network.mojom.NetworkService command-line flag. This flag indicates that the process is the network service responsible for handling all internet communication. It becomes a prime target as it holds the sensitive data the attacker seeks, as described in MDSec’s post. It then returns a handle for that specific child process.

Next, it enumerates each module in the network service child process to find and retrieve the base address and size of chrome.dll loaded into memory. STEALC uses CredentialKatz::FindDllPattern and CookieKatz::FindPattern to locate the CookieMonster instances. There are 2 calls to CredentialKatz::FindDllPattern.

In the first call to CredentialKatz::FindDllPattern, it tries to locate one of the signature patterns (depending on the victim’s Chrome version) in chrome.dll. Once found, STEALC now has a reference pointer to that memory location where the byte sequence begins which is the function net::CookieMonster::~CookieMonster, destructor of the CookieMonster class.

The second call to CredentialKatz::FindDllPattern passes in the function address for net::CookieMonster::~CookieMonster(void) as an argument for the byte sequence search, resulting in STEALC having a pointer to CookieMonster’s Virtual Function Pointer struct.

The following method used by STEALC is again, identical to ChromeKatz, where it locates CookieMonster instances by scanning memory chunks in the chrome.dll module for pointers referencing the CookieMonster vtable. Since the vtable is a constant across all objects of a given class, any CookieMonster object will have the same vtable pointer. When a match is identified, STEALC treats the memory location as a CookieMonster instance and stores its address in an array.

For each identified CookieMonster instance, STEALC accesses the internal CookieMap structure located at an offset of +0x30, and which is a binary tree. Each node within this tree contains pointers to CanonicalCookieChrome structures. CanonicalCookieChrome structures hold unencrypted cookie data, making it accessible for extraction. STEALC then initiates a tree traversal by passing the first node into a dedicated traversal function.

For each node, it calls ReadProcessMemory to access the CanonicalCookieChrome structure from the target process’s memory, then further processing it in jy::GenerateExfilString.

STEALC formats the extracted cookie data by converting the expiration date to UNIX format and verifying the presence of the HttpOnly and Secure flags. It then appends details such as the cookie's name, value, domain, path, and the HttpOnly and Secure into a final string for exfiltration. OptimizedString structs are used in place of strings, so string values can either be the string itself, or if the string length is greater than 23, it will point to the address storing the string.

METASTEALER

METASTEALER, first observed in 2022, recently upgraded its ability to steal Chrome data, bypassing Google’s latest mitigation efforts. On September 30th, the malware authors announced this update via their Telegram channel, highlighting its enhanced capability to extract sensitive information, including cookies, despite the security changes in Chrome's version 129+.

The first sample observed in the wild by our team was discovered on September 30th, the same day the authors promoted the update. Despite claims that the malware operates without needing Administrator privileges, our testing revealed it does require elevated access, as it attempts to impersonate the SYSTEM token during execution.

As shown in the screenshots above, the get_decryption method now includes a new Boolean parameter. This value is set to TRUE if the encrypted data (cookie) begins with the v20 prefix, indicating that the cookie is encrypted using Chrome's latest encryption method. The updated function retains backward compatibility, still supporting the decryption of cookies from older Chrome versions if present on the infected machine.

The malware then attempts to access the Local State or LocalPrefs.json files located in the Chrome profile directory. Both files are JSON formatted and store encryption keys (encrypted_key) for older Chrome versions and app_bound_encrypted_key for newer ones. If the flag is set to TRUE, the malware specifically uses the app_bound_encrypted_key to decrypt cookies in line with the updated Chrome encryption method.

In this case, the malware first impersonates the SYSTEM token using a newly introduced class called ContextSwitcher.

It then decrypts the key by creating an instance via the COM of the Chrome service responsible for decryption, named GoogleChromeElevationService, using the CLSID 708860E0-F641-4611-8895-7D867DD3675B. Once initialized, it invokes the DecryptData method to decrypt the app_bound_encrypted_key key which will be used to decrypt the encrypted cookies.

METASTEALER employs a technique similar to the one demonstrated in a gist shared on X on September 27th, which may have served as inspiration for the malware authors. Both approaches leverage similar methods to bypass Chrome's encryption mechanisms and extract sensitive data.

PHEMEDRONE

This open-source stealer caught the world’s attention earlier in the year through its usage of a Windows SmartScreen vulnerability (CVE-2023-36025). While its development is still occurring on Telegram, our team found a recent release (2.3.2) submitted at the end of September including new cookie grabber functionality for Chrome.

The malware first enumerates the different profiles within Chrome, then performs a browser check using function (BrowserHelpers.NewEncryption) checking for the Chrome browser with a version greater than or equal to 127.

If the condition matches, PHEMEDRONE uses a combination of helper functions to extract the cookies.

By viewing the ChromeDevToolsWrapper class and its different functions, we can see that PHEMEDRONE sets up a remote debugging session within Chrome to access the cookies. The default port (9222) is used along with window-position set to -2400,-2400 which is set off-screen preventing any visible window from alerting the victim.

Next, the malware establishes a WebSocket connection to Chrome’s debugging interface making a request using deprecated Chrome DevTools Protocol method (Network.getAllCookies).

The cookies are then returned from the previous request in plaintext, below is a network capture showing this behavior:

XENOSTEALER

XENOSTEALER is an open-source infostealer hosted on GitHub. It appeared in July 2024 and is under active development at the time of this publication. Notably, the Chrome bypass feature was committed on September 26, 2024.

The approach taken by XENOSTEALER is similar to that of METASTEALER. It first parses the JSON file under a given Chrome profile to extract the app_bound_encrypted_key. However, the decryption process occurs within a Chrome process. To achieve this, XENOSTEALER launches an instance of Chrome.exe, then injects code using a helper class called SharpInjector, passing the encrypted key as a parameter.

The injected code subsequently calls the DecryptData method from the GoogleChromeElevationService to obtain the decrypted key.

LUMMA

In mid-October, the latest version of LUMMA implemented a new method to bypass Chrome cookie protection, as reported by @g0njxa.

We analyzed a recent version of LUMMA, confirming that it managed to successfully recover the cookie data from the latest version of Google Chrome (130.0.6723.70). LUMMA first creates a visible Chrome process via Kernel32!CreateProcessW.

This activity was followed up in the debugger with multiple calls to NtReadVirtualMemory where we identified LUMMA searching within the Chrome process for chrome.dll.

Once found, the malware copies the chrome.dll image to its own process memory using NtReadVirtualMemory. In a similar fashion to the ChromeKatz technique, Lumma leverages pattern scanning to target Chrome’s CookieMonster component.

Lumma uses an obfuscated signature pattern to pinpoint the CookieMonster functionality:

3Rf5Zn7oFA2a????k4fAsdxx????l8xX5vJnm47AUJ8uXUv2bA0s34S6AfFA????kdamAY3?PdE????6G????L8v6D8MJ4uq????k70a?oAj7a3????????K3smA????maSd?3l4

Below is the YARA rule after de-obfuscation:

rule lumma_stealer
{
  meta:
    author = "Elastic Security Labs"
  strings:
    $lumma_pattern = { 56 57 48 83 EC 28 89 D7 48 89 CE E8 ?? ?? ?? ?? 85 FF 74 08 48 89 F1 E8 ?? ?? ?? ?? 48 89 F0 48 83 C4 28 5F 5E C3 CC CC CC CC CC CC CC CC CC CC 56 57 48 83 EC 38 48 89 CE 48 8B 05 ?? ?? ?? ?? 48 31 E0 48 89 44 24 ?? 48 8D 79 ?? ?? ?? ?? 28 E8 ?? ?? ?? ?? 48 8B 46 20 48 8B 4E 28 48 8B 96 ?? ?? ?? ?? 4C 8D 44 24 ?? 49 89 10 48 C7 86 ?? ?? ?? ?? ?? ?? ?? ?? 48 89 FA FF 15 ?? ?? ?? ?? 48 8B 4C 24 ?? 48 31 E1}
  condition:
    all of them
}

After decoding and searching for the pattern in chrome.dll, this leads to the CookieMonster destructor (net::CookieMonster::~CookieMonster).

The cookies are then identified in memory and dumped out in clear text from the Chrome process.

Once completed, LUMMA sends out the cookies along with the other requested data as multiple zip files (xor encrypted and base64 encoded) to the C2 server.

Detection

Below are the following behavioral detections that can be used to identify techniques used by information stealers:

Additionally, the following queries can be used for hunting diverse related abnormal behaviors:

Cookies access by an unusual process

This query uses file open events and aggregate accesses by process, then looks for ones that are observed in unique hosts and with a low total access count:

FROM logs-endpoint.events.file-default*
| where event.category == "file" and event.action == "open" and file.name == "Cookies" and file.path like "*Chrome*"
| keep file.path, process.executable, agent.id
| eval process_path = replace(to_lower(process.executable), """c:\\users\\[a-zA-Z0-9\.\-\_\$]+\\""", "c:\\\\users\\\\user\\\\")
| stats agents_count = COUNT_DISTINCT(agent.id), access_count= count(*) by process_path
| where agents_count <= 2 and access_count <=2

Below example of matches from diverse information stealers including the updated ones with new Chrome cookies stealing capabilities:

METASTEALER behavior tends to first terminate all running chrome instances then calls CoCreateInstance to instantiate the Google Chrome elevation service, this series of events can be expressed with the following EQL query:

sequence by host.id with maxspan=1s
[process where event.action == "end" and process.name == "chrome.exe"] with runs=5
[process where event.action == "start" and process.name == "elevation_service.exe"]

The previous hunt indicates suspicious agents but doesn't identify the source process. By enabling registry object access auditing through event 4663 on the Chrome Elevation service CLSID registry key {708860E0-F641-4611-8895-7D867DD3675B}, we can detect unusual processes attempting to access that key:

FROM logs-system.security-default* | where event.code == "4663" and winlog.event_data.ObjectName == "\\REGISTRY\\MACHINE\\SOFTWARE\\Classes\\CLSID\\{708860E0-F641-4611-8895-7D867DD3675B}" and not winlog.event_data.ProcessName in ("C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe") and not winlog.event_data.ProcessName like "C:\\\\Program Files\\\\Google\\\\Chrome\\\\Application\\\\*\\\\elevation_service.exe" | stats agents_count = COUNT_DISTINCT(agent.id), access_count= count(*) by winlog.event_data.ProcessName | where agents_count <= 2 and access_count <=2

Below is an example of matches on the METASTEALER malware while calling CoCreateInstance (CLSID_Elevator):

The PHEMEDRONE stealer uses the known browser debugging method to collect cookies via Chromium API, this can be observed in the following screenshot where we can see an instance of NodeJs communicating with a browser instance with debugging enabled over port 9222:

The following EQL query can be used to look for unusual processes performing similar behavior:

sequence by host.id, destination.port with maxspan=5s
[network where event.action == "disconnect_received" and
 network.direction == "ingress" and
 process.executable in~ ("C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
"C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe") and
 source.address like "127.*" and destination.address like "127.*"]
[network where event.action == "disconnect_received" and network.direction == "egress" and not
 process.executable in~ ("C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
"C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe") and source.address like "127.*" and destination.address like "127.*"]

Chrome Browser Spawned from an Unusual Parent

The STEALC sample that uses ChromeKatz implementation spawns an instance of Google Chrome to load the user default profile, while looking for normal parent executables, it turns out it’s limited to Chrome signed parents and Explorer.exe, the following ES|QL query can be used to find unusual parents:

FROM logs-endpoint.events.process-*
| where event.category == "process" and event.type == "start" and to_lower(process.name) == "chrome.exe" and process.command_line like  "*--profile-directory=Default*"
| eval process_parent_path = replace(to_lower(process.parent.executable), """c:\\users\\[a-zA-Z0-9\.\-\_\$]+\\""", "c:\\\\users\\\\user\\\\")
| stats agents_count = COUNT_DISTINCT(agent.id), total_executions = count(*) by process_parent_path
| where agents_count == 1 and total_executions <= 10

Untrusted Binaries from Chrome Application folder

Since the Chrome elevation service trusts binaries running from the Chrome program files folder, the following queries can be used to hunt for unsigned or untrusted binaries executed or loaded from there:

Unsigned DLLs loaded from google chrome application folder

FROM logs-endpoint.events.library*
| where event.category == "library" and event.action == "load" and to_lower(dll.path) like "c:\\\\program files\\\\google\\\\chrome\\\\application\\\\*" and not (dll.code_signature.trusted == true)
| keep process.executable, dll.path, dll.hash.sha256, agent.id
| stats agents_count = COUNT_DISTINCT(agent.id), total_executions = count(*) by process.executable, dll.path, dll.hash.sha256
| where agents_count == 1 and total_executions <= 10

Unsigned executable launched from google chrome application folder

FROM logs-endpoint.events.process*
| where event.category == "library" and event.type == "start" and (to_lower(process.executable) like "c:\\\\program files\\\\google\\\\chrome\\\\application\\\\*" or to_lower(process.executable) like "c:\\\\scoped_dir\\\\program files\\\\google\\\\chrome\\\\application\\\\*")
and not (process.code_signature.trusted == true and process.code_signature.subject_name == "Goole LLC")
| keep process.executable,process.hash.sha256, agent.id
| stats agents_count = COUNT_DISTINCT(agent.id), total_executions = count(*) by process.executable, process.hash.sha256
| where agents_count == 1 and total_executions <= 10

Conclusion

Google has raised the bar implementing new security controls to protect cookie data within Chrome. As expected, this has caused malware developers to develop or integrate their own bypasses. We hope Google will continue to innovate to provide stronger protection for user data.

Organizations and defenders should consistently monitor for unusual endpoint activity. While these new techniques may be successful, they are also noisy and detectable with the right security instrumentation, processes, and personnel.

Stealer Bypasses and MITRE ATT&CK

Elastic uses the MITRE ATT&CK framework to document common tactics, techniques, and procedures that threats use against enterprise networks.

Tactics

Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.

Techniques

Techniques represent how an adversary achieves a tactical goal by performing an action.

YARA

Elastic Security has created YARA rules to identify this activity.

Observations

All observables are also available for download in both ECS and STIX format.

The following observables were discussed in this research.

ObservableTypeNameReference
27e4a3627d7df2b22189dd4bebc559ae1986d49a8f4e35980b428fadb66cf23dSHA-256num.exeSTEALC
08d9d4e6489dc5b05a6caa434fc36ad6c1bd8c8eb08888f61cbed094eac6cb37SHA-256HardCoreCrack.exePHEMEDRONE
43cb70d31daa43d24e5b063f4309281753176698ad2aba9c557d80cf710f9b1dSHA-256Ranginess.exeMETASTEALER
84033def9ffa70c7b77ce9a7f6008600c0145c28fe5ea0e56dfafd8474fb8176SHA-256LUMMA
b74733d68e95220ab0630a68ddf973b0c959fd421628e639c1b91e465ba9299bSHA-256XenoStealer.exeXENOSTEALER

References

The following were referenced throughout the above research: