The extension you never installed: KREMLIN forges Chrome's own integrity checks to steal banking sessions
Elastic details KREMLIN, a Brazilian banking malware whose malicious Chrome/Edge extension forges Chromium integrity checks to steal sessions.
Elastic Security Labs tracked operation REF9334 and its KREMLIN toolkit, a Brazilian banking malware campaign impersonating twelve banks across seven campaigns since May 2025. The infection chain uses multi-stage obfuscated JavaScript loaders with sandbox checks (file and process counting), scheduled-task persistence, and an Ethereum smart contract acting as a dead-drop resolver for C2 and payload URLs, abusing Archive.org for hosting. The malicious browser extension bypasses Chromium integrity mechanisms by manipulating Secure Preferences and regenerating required HMACs and App-Bound encrypted hashes. Threat Command disrupted over 1,500 infections by registering the kill-switch canary domain.
- Malicious extension forges Secure Preferences HMACs and App-Bound hashes to bypass Chromium integrity checks
- Ethereum smart contract used as dead-drop resolver to rotate C2 and payload hosting URLs
- Targets customers of twelve Brazilian banks; lures and code comments in Portuguese
- Elastic disrupted 1,500+ infections by registering the network canary kill-switch domain
- Sideloading abuse of a legit SentinelOne binary and Node.js runtime download in the chain
Indicators of compromiseauto-extracted · verify before use · export allAll →
| Type | Indicator | Context |
|---|---|---|
| domain | archive.org | 35a1ca0987/{campaignId} .NET PE Injector sub-module hxxps://archive[.]org/download/hotelmoskva/hotelmoskva.jpg SentinelMemoryScanne |
| domain | connection.upgradeonline.site | passing the campaign ID. In this sample, the URL is hxxps://connection[.]upgradeonline[.]site . Loader beacons to C2 Second stage: persistence and th |
| domain | granderevolucao.store | licious browser extension installer payload main-v2 hxxps://granderevolucao[.]store/5c92d3b8734b4f498752f735a1ca0987/{campaignId} .NET PE Inj |
| domain | ia601808.us.archive.org | legit SentinelOne binary for side-loading sentinel hxxps://ia601808[.]us[.]archive[.]org/5/items/sentinel_20260722_0435/Sentinel.jpg After the |
| domain | volmira.site | abA6740d07b . The extension and main-v2 parameters returned volmira[.]site and zaviro[.]online , respectively. Notably, the main-v2 |
| domain | www.creamp1eonlyfans.net | ting to download a page from the unregistered domain hxxp://www[.]creamp1eonlyfans[.]net . Because this domain should not return any content, a |
| domain | zaviro.online | xtension and main-v2 parameters returned volmira[.]site and zaviro[.]online , respectively. Notably, the main-v2 value was updated on |
| md5 | 5c92d3b8734b4f498752f735a1ca0987 | n installer payload main-v2 hxxps://granderevolucao[.]store/5c92d3b8734b4f498752f735a1ca0987/{campaignId} .NET PE Injector sub-module hxxps://archive[.] |
| sha256 | 106eac79396a3ff77b8f375c391260ce422be2ae4d55d3aa75b2635cbdc0fa42 | tection For this analysis, we examine the following script: 106eac79396a3ff77b8f375c391260ce422be2ae4d55d3aa75b2635cbdc0fa42 . The obfuscation is fairly basic: function names are repla |
| sha256 | 5ece7fd3766b0b7f8aadefa562313cea6c3c94f9398658dd389910e5be44f552 | ful pivot for finding additional first-stage samples (e.g., 5ece7fd3766b0b7f8aadefa562313cea6c3c94f9398658dd389910e5be44f552 ). The sandbox-detection heuristic consists of two checks. |
| sha256 | c8c38634dd44d7c6162c66174a6ee23ee404265125166e8d757681bdd66a4268 | ugging. For this analysis, we examine the following binary: c8c38634dd44d7c6162c66174a6ee23ee404265125166e8d757681bdd66a4268 . KREMLIN string decryption algorithm As noted at the begin |
| url | https://archive[ | 498752f735a1ca0987/{campaignId} .NET PE Injector sub-module hxxps://archive[.]org/download/hotelmoskva/hotelmoskva.jpg SentinelMemorySca |
| url | https://connection[ | dpoint, passing the campaign ID. In this sample, the URL is hxxps://connection[.]upgradeonline[.]site . Loader beacons to C2 Second stage: |
| url | https://granderevolucao[ | r URL Malicious browser extension installer payload main-v2 hxxps://granderevolucao[.]store/5c92d3b8734b4f498752f735a1ca0987/{campaignId} .NET P |
| url | https://ia601808[ | er.exe : legit SentinelOne binary for side-loading sentinel hxxps://ia601808[.]us[.]archive[.]org/5/items/sentinel_20260722_0435/Sentinel |
| url | https://volmira[ | intained. After retrieving the domains, the malware queries hxxps://volmira[.]site/api/ext/version to obtain the extension version. The |
| url | https://zaviro[ | two C2 endpoints: hxxps://volmira[.]site//api/savecreds and hxxps://zaviro[.]online//api/v1/fingerprint . The following POST request wa |
| url | http://www[ | attempting to download a page from the unregistered domain hxxp://www[.]creamp1eonlyfans[.]net . Because this domain should not re |
Full article2,701 words · extracted from elastic.co · click to collapse
Elastic Security Labs has tracked REF9334, a Brazilian banking malware operation, since May 2025. Its toolkit is called KREMLIN (as named by the malware author, Kr3mlin4rt1st), though nothing about the operation is Russian. Lures impersonate twelve Brazilian banks; error messages and code comments are written in Portuguese, and the operators' Ethereum transactions cluster during São Paulo working hours. Over 15 months and seven campaigns, they built a malicious browser extension that installs itself in Chrome and Edge, and the browser then loads it as though the user approved it. This post covers the infection chain, the extension internals, all seven campaigns, and the wallet trail connecting them.
Key takeaways
The KREMLIN malware ecosystem employs multi-stage JavaScript loaders, custom C++ installers, and malicious browser extensions to steal credentials, session tokens, and sensitive data.
Attacker infrastructure leverages Ethereum smart contracts as dead-drop resolvers to dynamically update C2 endpoints and payload hosting locations.
Malicious browser extensions bypass Chromium integrity mechanisms by manipulating Secure Preferences and regenerating required HMACs and App-Bound encrypted hashes.
Campaign artifacts, naming conventions, and transaction patterns indicate a primary targeting focus on Brazilian banking users and financial institutions.
- Threat Command temporarily disrupted over 1,500 (and counting) infections in this reported campaign by registering the network canary (kill switch) domain
KREMLIN JavaScript loader: multi-stage infection chain analysis
Infection chain diagram
The KREMLIN infection chain begins with a JavaScript file masquerading as a banking, invoice, or company document, which the user manually executes. The payload is a slightly obfuscated multi-stage loader that first checks whether the script is running in a sandbox or virtual machine. It then downloads and installs malicious binaries from several sources before executing the next binary stage.
Loader file VirusTotal detection
For this analysis, we examine the following script: 106eac79396a3ff77b8f375c391260ce422be2ae4d55d3aa75b2635cbdc0fa42.
The obfuscation is fairly basic: function names are replaced with generic identifiers (e.g itemXX), strings are retrieved from a lookup table by index, and object methods are called using bracket notation with string keys. However, we can easily deobfuscate this script using an LLM.
Loader string obfuscation mechanism
First stage: sandbox evasion and Node.js runtime download
The first stage displays an error message to make the user believe that the lure failed to open. It then checks whether it is running in a sandbox, decodes and extracts the next JavaScript stage using certutil, downloads Node.js to execute it, and finally beacons to one of its C2 servers.
First stage downloads Node.js and executes the next stage
To display the error message, the malware creates another JavaScript file that invokes shell.Popup and then deletes itself. The file follows the naming pattern popup_{date}_{random}.js and provides a useful pivot for finding additional first-stage samples (e.g., 5ece7fd3766b0b7f8aadefa562313cea6c3c94f9398658dd389910e5be44f552).
The sandbox-detection heuristic consists of two checks. First, it counts the files on the user's desktop. Second, it uses a WMI query to count the processes running on the machine. If there are fewer than five files or fewer than 50 processes, the malware assumes it is running in a sandbox and aborts execution.
Sandbox detection using process counting and files
Before terminating, the loader contacts its infrastructure through the /api/log_loader?hash= API endpoint, passing the campaign ID. In this sample, the URL is hxxps://connection[.]upgradeonline[.]site.
Loader beacons to C2
Second stage: persistence and the Ethereum dead drop resolver
The second stage runs four steps:
Installs persistence through a scheduled task extracted from an embedded CAB archive.
Retrieves download locations from an Ethereum smart contract.
Downloads the required binaries from those locations.
Executes the third stage.
Execution begins by establishing persistence. The script extracts an embedded CAB archive containing a scheduled task. One minute after the user logs on, the task instructs Windows to run conhost.exe --headless node.exe from the directory containing the malicious script. It is registered as MicrosoftNodeRuntimeUpdater with the description "Node.js V8 Runtime is the JavaScript engine responsible for compiling and executing Node.js applications using Google's high-performance V8 engine.", making it appear legitimate at first glance.
Persistence task
It then retrieves its configuration from the Ethereum smart contract at 0xCD7360A83E5cdbBbbbcEB0e78748babA6740d07b by querying three parameters:
main-v2: The URL of the main module.sub-module: The URL of a JPEG carrier containing a .NET process-injection kit (RunPE), though this was not observed in use here.sentinel: The URL of a JPEG carrier holding a CAB file, which contains a SentinelOne binary used to sideload the malware.
Loader queries module from ETH smart contract
The payload-hosting infrastructure combines attacker-controlled domains with abuse of the public Archive.org service. The main payload is encoded as a reversed Base64 string (base64.b64decode(payload[::-1])). The JPEG carriers’ payloads are Base64-encoded and delimited by start- and end-of-file markers.
Payload Base64 extraction logic
At the time of the analysis, the payload URLs were as follows:
Module | Smart contract parameter | URL |
|---|---|---|
Malicious browser extension installer payload | main-v2 |
|
.NET PE Injector | sub-module |
|
| sentinel |
|
After the modules download and the CAB archive extracts, the files in the installation directory specified by items.json are replaced. This file is included in the CAB archive containing the SentinelOne binary, providing the malware with an update mechanism. After the process completes, SentinelMemoryScanner.exe executes.
KREMLIN malicious browser extension installer analysis
KREMLIN's main binary is a 2.10 MB program written in C++ and compiled for x64, designed to install malicious browser extensions. It appears to be under active development and is not obfuscated. It is statically linked against numerous libraries, accounting for its large size. Only some strings are encrypted for unknown reasons, and the malware uses indirect syscalls to interact with the kernel. Despite its extensive infrastructure, KREMLIN shows several signs of immaturity compared with modern malware. Its overreliance on open-source libraries significantly bloats the binary, while string and API obfuscation appears to be applied manually rather than through automated tooling. Debug strings remain, and both the installer and the malicious extension enable debugging.
For this analysis, we examine the following binary: c8c38634dd44d7c6162c66174a6ee23ee404265125166e8d757681bdd66a4268.
KREMLIN string decryption algorithm
As noted at the beginning of this section, the malware encrypts only some of its strings. The binary appears to contain several versions of the decryption algorithm, but these are actually the same algorithm adapted to different string lengths rather than separate implementations for each string. The algorithm is shown in the following code snippet:
Indirect syscalls and SSN resolution from NTDLL
At startup, KREMLIN builds a map of API-name hashes to System Service Numbers (SSNs). Rather than parsing individual Nt* or Zw* stubs, it correlates ntdll.dll exports with the address-ordered RUNTIME_FUNCTION entries in the exception directory (.pdata). Because the syscall stubs in ntdll.dll are arranged in SSN order, KREMLIN can derive each SSN by counting the Zw* exports that precede the target syscall. We linked the malware to the open source PigSyscall library through a distinctive string in the GetSyscallNumber function. Although the string differs slightly, the function's behavior matches the implementation observed in the binary.
GetSyscallNumber implementation matching PigSyscall
Once the table is initialized, the malware uses the target export hash to look up the corresponding SSN. It then issues the syscall through an existing syscall; ret sequence in ntdll.dll. If it finds no suitable sequence, it falls back to a hard-coded syscall stub embedded in the binary.
Indirect syscall execution with fallback logic
Malware side-loading and entrypoint
To execute the next stage, KREMLIN abuses the legitimate SentinelOne binary SentinelMemoryScanner.exe to sideload its unsigned main payload, which masquerades as SentinelAgentCore.dll. Symantec first documented this technique in Seedworm: Iran-Linked Hackers Breached Korean Electronics Maker.
Execution begins with creating the malware's primary worker thread. If the host process is SentinelMemoryScanner.exe, KREMLIN locates the internal LdrpLoaderLock critical section and the LdrpWorkInProgress global variable in ntdll.dll. By releasing the loader lock and clearing this variable, the malware bypasses loader synchronization and allows the new thread to start before DllMain returns. Under normal conditions, CreateThread can be called from DllMain, but the new thread's entry point does not execute until DLL initialization completes. Waiting for that thread from DllMain would therefore deadlock. A complete implementation of this technique is available in LdrLockLiberator.
KREMLIN disable loader lock to create a thread in DLLMain
Sandbox evasion and anti-VM checks
Like the JavaScript payload, the malware performs a series of checks to determine whether it is running in a virtual machine or sandbox. Interestingly, it makes no attempt to detect debugging or hooks. These checks examine the host process name, running processes, and the system's memory and disk properties. Most positive detections cause the malware to call the invalid address 0x1337, deliberately triggering an access violation, although it ignores some check results.
Access violation trigger on sandbox detection
Running process checks for analysis and sandbox tools
The malware retrieves the list of running processes using ZwQuerySystemInformation, then searches it for process names associated with sandboxing software and security analysis tools. The full list is shown below.
Sandbox: powershell.exe, malware.exe, sandbox.exe, bot.exe, test.exe, myapp.exe, testapp.exe, joeboxcontrol.exe, joeboxserver.exe, proc_analyzer.exe, sysanalyzer.exe, sniff_hit.exe, fakenet.exe
Security tools: klavme.exe, ollydbg.exe, ollyice.exe, processhacker.exe, tcpview.exe, autoruns.exe, autorunsc.exe, filemon.exe, procmon.exe, regmon.exe, procexp.exe, idaq.exe, ida.exe, ida64.exe, idaq64.exe, immunitydebugger.exe, wireshark.exe, dumpcap.exe, hookexplorer.exe, importrec.exe, petools.exe, lordpe.exe, sysinspector.exe, systeminformer.exe, windbg.exe, resourcehacker.exe, x32dbg.exe, x64dbg.exe, fiddler.exe, httpdebugger.exe, cheatengine-i386.exe, cheatengine-x86_64.exe, cheatengine-x86_64-SSE4-AVX2.exe, frida-helper-32.exe, frida-helper-64.exe, ghidra.exe, radare2.exe, r2.exe, cutter.exe, dnspy.exe, dnspyex.exe, ilspy.exe, hxd.exe, detectiteasy.exe, dbgview64.exe
Username blacklist check
KREMLIN also compares the user's account name, retrieved through GetUserNameW, against a blacklist. The complete list of account names it checks for is shown below.
Account names: CurrentUser, Sandbox, Emily, HAPUBWS, HongLee, ITADMIN, Johnson, Miller, milozs, PeterWilson, timmy, sandbox, malware, maltest, testuser, virus, JohnDoe
CPU, RAM, and disk hardware checks
The malware also checks several properties related to the machine hardware. The machine must have more than 2 CPUs, and its RAM capacity must exceed 3 GB to pass the test. The malware also checks disk space; however, our sample discards the result, potentially due to conditional compilation.
Network canary check
In addition to its hardware and process checks, the malware performs a network canary check by attempting to download a page from the unregistered domain hxxp://www[.]creamp1eonlyfans[.]net. Because this domain should not return any content, a valid response likely indicates that a sandbox is simulating network connectivity. The malware then deliberately crashes.
Network canary check targeting unregistered domain
VMware and VirtualBox artifact checks
The final check before the next stage searches for processes and files, including drivers and DLLs, associated with VMware and VirtualBox. Process detection resolves a PID for each targeted process name, while file existence is checked using ZwQueryAttributesFile. In the sample analyzed, however, the caller ignores the results of both checks for both products, possibly because of conditional compilation.
VMware processes: VGAuthService.exe, vmacthlp.exe, Vmwaretrat.exe, Vmwareuser.exe, vmtoolsd.exe
VMware files: vmhgfs.sys, vmmemctl.sys, vmmouse.sys, vmrawdsk.sys, vm3dgl.dll, vm3dver.dll, vmtray.dll, vmtoolshook.dll, vmmousever.dll, vmhgfs.dll, vmguestlib.dll, vmguestlibjava.dll, driversvmhgfs.dll, vmdum.dll
VirtualBox processes: VBoxService.exe, VBoxTray.exe
VirtualBox files: VBoxMouse.sys, VBoxGuest.sys, VBoxSF.sys, VBoxVideo.sys
Campaign tracking markers: customer ID and mutex
Once all checks have passed, the malware loads two strings: a customer ID that appears to identify the customer associated with the campaign under analysis, and a Portuguese string used as the mutex name. This suggests that the malware operators are distinct from its developers. Both strings can serve as reliable campaign-tracking markers.
Customer ID and Mutex logging
Customer-id: 98d8049e-804f-11f1-b79f-ae3a8bb85d01
Mutex: ClarinhoQueSim-XEDA2O
Downloading the malicious browser extension
Before downloading the malicious extension, KREMLIN queries the same Ethereum smart contract used earlier, 0xCD7360A83E5cdbBbbbcEB0e78748babA6740d07b. The extension and main-v2 parameters returned volmira[.]site and zaviro[.]online, respectively. Notably, the main-v2 value was updated on August 13, 2026, after we retrieved the previous value while analyzing the JavaScript payload, indicating that the infrastructure is actively maintained.
After retrieving the domains, the malware queries hxxps://volmira[.]site/api/ext/version to obtain the extension version. The response contains a JSON object with the extension's version and ID:
Before downloading the archive, the malware checks whether the extension is already installed and, if so, compares the installed version with the version reported by the server. It downloads the extension only when no local installation is found, or the versions differ.
Local extension installation and version validation check
It then downloads the Chrome extension as a ZIP archive from the following URL, passing the Customer ID through the p query parameter: hxxps://volmira[.]site/api/ext?p=98d8049e-804f-11f1-b79f-ae3a8bb85d01.
How KREMLIN installs a Chrome extension without the Web Store
After downloading the extension, KREMLIN installs it in Chromium-based browsers, specifically Chrome and Edge.
To do so, KREMLIN uses a documented technique rarely observed in malware: it manually copies the extension into the browser's profile directories and registers it in the Secure Preferences file. Because Chromium protects these entries with cryptographic integrity checks, the malware must retrieve the required keys and regenerate the associated HMACs and encrypted hashes. Synacktiv describes this technique in detail in The Phantom Extension: Backdooring Chrome through Uncharted Pathways.
Before modifying the browser profile, KREMLIN waits until the browser is closed or the user has been inactive for at least two minutes, polling GetLastInputInfo. If the browser remains open, it force-terminates it with TerminateProcess. This likely prevents concurrent access to profile files while making the shutdown less noticeable. KREMLIN can then launch a fresh browser instance under a debugger to recover the App-Bound key.
KREMLIN waits for IDLE Chrome before closing it
Recovering Chrome's OSCrypt and App-Bound encryption keys
KREMLIN first retrieves Chrome's legacy OSCrypt key from %LOCALAPPDATA%\Google\Chrome\User Data\Local State. The DPAPI-protected key is stored as a Base64-encoded value in the os_crypt.encrypted_key field. After decoding it, the malware removes the five-byte DPAPI prefix and passes the remaining blob to the Windows API CryptUnprotectData. The malware can later use this legacy key to decrypt sensitive profile data during exfiltration.
To recover the newer App-Bound OSCrypt key, the malware launches the browser under a debugger with the --no-startup-window option. It then processes debug events until it receives a LOAD_DLL_DEBUG_EVENT and checks whether the loaded module is chrome.dll or msedge.dll.
KREMLIN process Chrome’s debug events
Once the target module is loaded, KREMLIN scans its .rdata section for the string OSCrypt.AppBoundProvider.Decrypt.ResultCode. It then searches .text for a RIP-relative LEA instruction referencing that string. From this cross-reference, it applies additional byte-pattern matching to locate the key-buffer pointer, then uses ReadProcessMemory to read the key from the browser process.
KREMLIN memory-scans Chrome process
The final value KREMLIN retrieves is a seed extracted from %PROGRAMFILES%\Google\Chrome\Application\<VERSION>\resources.pak. Chromium uses this seed to generate both legacy HMACs and newer encrypted hashes.
Finally, the ZIP archive containing the extension is extracted into each browser profile whose Secure Preferences file can be read.
To activate the extension, KREMLIN modifies the Secure Preferences file. It enables developer mode through extensions.ui.developer_mode and account_values.extensions.ui.developer_mode, then registers the extension under extensions.settings.<extension_id>. To satisfy Chromium's integrity checks, it updates protection.macs with both the legacy HMACs and the corresponding OSCrypt-encrypted SHA-256 hashes (*_encrypted_hash) for these preferences.
The following is an example of the modifications required to activate the malicious extension:
To generate these integrity values, KREMLIN uses the seed extracted from resources.pak to compute the legacy HMACs. On newer Chromium versions (>= 144), it also hashes this seed together with the relevant data, then encrypts the resulting digest using the OSCrypt key recovered from the debugged browser process.
Browser data exfiltration and session token theft
Once the extension is installed, KREMLIN begins exfiltrating browser data. For each browser profile, it adds the following files and directories to a ZIP archive:
KREMLIN also adds a keys.json file to the archive. It contains two OSCrypt keys: the v10 key retrieved from Local State and the v20 key recovered from browser memory during debugging. These keys allow later decryption of encrypted fields in the exfiltrated databases.
The ZIP archive is then encrypted with RC4 through the undocumented SystemFunction032 API, using the SHA-256 digest of the plaintext archive as the encryption key. The encrypted ZIP archive, Customer ID, and SHA-256 digest used as the RC4 key are sent to the following two C2 endpoints: hxxps://volmira[.]site//api/savecreds and hxxps://zaviro[.]online//api/v1/fingerprint. The following POST request was captured on our server:
Data exfiltration POST requests
KREMLIN malicious Chrome extension: capabilities and C2 protocol
KREMLIN malicious extension
The extension sample analyzed in this research has the following SHA-256 hash: 223be3f8648bf6998c4a58b972522e5fda8d9d0a57b4e163811930de66c3f7ca.
The extension consists of several JavaScript files and a manifest (manifest.json). It masquerades as legitimate software named AVSync and requests access to browser tabs, cookies, storage, and the webRequest API. Its functionality is split between a background service worker and two content scripts injected into every page the victim visits.
Unlike the loader, the extension's JavaScript code is unobfuscated and uses descriptive variable and function names. The configuration specifies the endpoint from which the extension retrieves its final C2 address. Like other KREMLIN components, it can use a smart contract for this purpose. The misspelling in the ENDPOINT_DINAMIC field name is particularly noteworthy and can serve as a pivot for identifying additional extension samples.
KREMLIN malicious configuration
The extension first resolves its C2 endpoint using the URL or smart contract specified by ENDPOINT_DINAMIC. In the sample analyzed, it sends a request to hxxps://graph.checkeligibitily.workers[.]dev/x01aab878f25420380b3?op=98d8049e-804f-11f1-b79f-ae3a8bb85d01. The op parameter contains the same Customer ID embedded in the installer, linking both components to the same toolset. The endpoint returns the following response:
Before sending its first request to the C2 server, the script generates a victim identifier that it includes in subsequent communications. This identifier is persisted in the browser's storage.
After generating the victim identifier, the extension establishes a WebSocket connection to the C2 server through the /google_ws/ route. The identifier, extension version, and a tag are passed as query parameters. Once connected, the extension polls the C2 server once per second for the next command. The initial exchanges are shown below:
KREMLIN malicious extension communications
The extension supports the following commands:
Command ID | Name | Description |
|---|---|---|
| Screenshot | Captures the selected or active tab then uploads the compressed image. |
| Get tabs | Enumerates tabs, domains, and active state; uploads the list and returns it over WebSocket. |
| Get cookies and storage | Steals cookies, |
| Get history | Collects up to 1,000 history entries from the previous 15 days. This likely fails because the manifest lacks the |
| Get source | Extracts and uploads the full HTML source of the selected or active page. |
| Inject HTML | Downloads attacker-controlled HTML using |
| Update config | Refreshes domain targeting, redirect, keylogging, request-body, and request-header interception rules. |
Alongside its WebSocket channel, the extension periodically polls /google_api/ for configuration data controlling its interception and keylogging features. These requests masquerade as CSS file fetches, with each path mapped server-side to a specific configuration or command. For example, the interception configuration is retrieved from /google_api/81d47cb6.css.
The extension supports the following commands:
Endpoint ID | Name | Description |
|---|---|---|
| Upload cookies and storage | Uploads LZ/Base64-compressed cookies, |
| Upload tabs | Uploads compressed tab IDs, domains, and active-tab state with the client ID. |
| Upload history | Uploads compressed browser history from the previous 15 days with the client ID. |
| Upload screenshot | Uploads a compressed JPEG screenshot, page URL, and client ID. |
| Upload page source | Uploads compressed full-page HTML, page URL, and client ID. |
| Upload intercepted request | Uploads the matching request URL and method, plus either the request body or request headers. Matching uses domain hash, URL substring, and HTTP method rules. |
| Fetch targeting config | Sends the client ID and retrieves domain targeting, keylogging, redirect, and HTTP interception rules. |
| Fetch redirect config | Sends the client ID and retrieves selector-based automatic redirect rules. |
Across all communication channels, the client and server compress and Base64-encode exchanged data using the lz-string package.
The configuration retrieved from 81d47cb6.css contains a list of objects, each identifying a target domain by its MD5 hash. To exercise these features, we used an LLM to build a playground with forms and buttons that trigger the interception logic and expose the resulting server-side messages. For this test, we enabled keylogging by setting "b": 1 and configured the extension to intercept POST requests to /api/probe/checkout.
Playground platform for testing the malicious extension features
After retrieving this configuration, KREMLIN requests a98cb43d.css to obtain its redirection rules. Each object defines a target domain, an HTML element selector (such as a class or ID), an event type (such as click), and the destination URL.
Once the page has loaded, if keylogging is enabled for the domain (b = 1), the extension registers an input event listener on every <input> and <textarea> element. Whenever the user modifies one of these fields, such as by typing a message or password, the extension sends the captured data to the server. To capture dynamically added fields, it also uses a MutationObserver to monitor the DOM and attach the same listener to new <input> and <textarea> elements.
The demo below shows how we use our playground website to trigger the functionality injected by the malware. It captures the initialization of communication between the extension and the server, the polling messages ({'action': 'ping'}), and the resulting keylogging, request interception, and redirection behavior.
Brazilian banking malware campaigns and infrastructure
By correlating the identified files, relationships, and domains, we assess that the actors have been active since at least May-June 2025. Their operations include installing malicious browser extensions and distributing PULSAR and REMCOS RAT. Shared staging and C2 infrastructure across campaigns suggests that the same actors control the entire infection chain. Their TTPs gradually evolved until they emerged with a toolkit explicitly named KREMLIN. References to an author and version numbers may indicate broader, potentially open-source distribution, although we found no public trace of the toolkit on GitHub, GitLab, or social media.
The actors have used the Internet Archive to host payloads since the earliest observed campaigns. All identified uploads originate from the same account, Radduxx. The earliest file, output_image_202505.jpg, was uploaded on May 21, 2025, and masquerades as an image of the FC Barcelona team. We identified it as an early RunPE module executed through the command line rather than loaded as a DLL through PowerShell. This confirms that RunPE was already in use and distributed through the same JPEG-based packaging method. The associated metadata lists the uploader's email address as facebook-br@protonmail[.]com.
Malicious Barcelona team JPEGs hosted on web archive
The table below summarizes the seven campaigns described in this section.
Campaign | Period | Customer ID | Execution chain | Payloads | Key infrastructure |
Codecaudiog A | June 2025 | 991589b0-4cc9-11f0-b9f4-1402ec3d56f0 | PowerShell → RunPE → Installer | Extension, DonutLoader, PULSAR 1.6.6 / 1.7.3 | codecaudiog[.]site, codecvideowin[.]online, 185.221.23[.]133 |
Codecaudiog B | June 2025 | f1d7b074-b81f-11ef-a763-1402ec3d56f0 | JavaScript → Installer | Extension, PULSAR, or both | codecaudiog[.]site, codecvideowin[.]online, version.checkeligibitily.workers[.]dev |
Acrobat | August 2025 | 618ec809-f08e-4068-a54c-654478811510 | JavaScript → PowerShell → RunPE → DonutLoader | PULSAR 1.7.1 / 1.7.2, no extension | acrobat-updater[.]com, 144.172.112[.]239, 45.90.13[.]210 |
Framesync | September 2025 | 48502c50-a504-4811-aab8-ba978aeae237 | Not recovered | Extension only (FrameSync Driver / Plugin) | lojinhadoluiz[.]online, orange-sun-195a.checkeligibitily.workers[.]dev |
Donalurdesconfeitos to Cremeb | December 2025 to March 2026 | 991589b0-4cc9-11f0-b9f4-1402ec3d56f0, 48502c50-a504-4811-aab8-ba978aeae237 | JavaScript → PowerShell → RunPE → Installer | Extension only | donalurdesconfeitos[.]site, marialurdes[.]site, harialurdes[.]site, cremeb[.]com |
Cremeb | April 2026 | 48502c50-a504-4811-aab8-ba978aeae237 | LNK → PowerShell → JavaScript (WSH) → JavaScript (Node.js) → RunPE → DLL installer | QR extension, PULSAR 2.4.5 | cremeb[.]com, 37.16.74[.]100, 37.16.74[.]34 |
Ethereum transition | May 2026 to present | 98d8049e-804f-11f1-b79f-ae3a8bb85d01 | JavaScript → Node.js → RunPE → Installer, and SentinelOne sideload variant | Extension, REMCOS RAT | granderevolucao[.]store, volmira[.]site, zaviro[.]online, 178.92.162[.]38 |
Codecaudiog A campaign, June 2025: PULSAR RAT and extension delivery
Codecaudiog A campaign diagram
The earliest campaign we observed dates back to June 16, 2025, based on the first VirusTotal submission of the Internet Archive URL pointing to the JPEG file containing the RunPE module. The JPEG itself was submitted the following day, on June 17. The execution chain is PowerShell -> RunPE -> Installer -> Malicious extension & DonutLoader -> PULSAR. The campaign ID is 991589b0-4cc9-11f0-b9f4-1402ec3d56f0.
In this campaign, the loader downloads the installer from hxxps://codecaudiog[.]site/generate?domain=codecvideowin[.]online&payload=991589b0-4cc9-11f0-b9f4-1402ec3d56f0&prefix=NF&prefix_count=10&output=base64, while the RunPE module is retrieved from archive[.]org/download/caramelov/caramelov.jpg. The installer reproduces the complete malicious-extension installation process described in our KREMLIN analysis. It retrieves the extension ID from hxxps://codecvideowin[.]online/f9e95a1e1fa3f3aebfc802c6c8e6a2eb and downloads the extension archive from hxxps://codecvideowin[.]online/af15d5f?p=991589b0-4cc9-11f0-b9f4-1402ec3d56f0, establishing a direct lineage with KREMLIN.
In parallel, the installer delivers PULSAR through RC4-encoded DonutLoader shellcode injected into explorer.exe. Although we could not recover the extension itself, the configurations of PULSAR versions 1.6.6 and 1.7.3 reveal the tags ChromBallRat and Rat, and the C2 address 185.221.23[.]133, initially using port 4782 and later port 443.
The PowerShell loader retrieves the RunPE module from the Internet Archive, loads it as a .NET assembly using [System.Reflection.Assembly]::Load, and invokes its VAI method.
PowerShell loader loads and executes the RUNPE assembly
Earlier in our analysis, we did not examine the RunPE module in detail because the latest version executes the installer through DLL sideloading. In this older version of the RunPE module, the VAI method is only executing the payload via manual mapping and calling its entrypoint.
RunPE executes payload via manual mapping
Codecaudiog B campaign, June 2025: three JavaScript loader variants
Codecaudiog B campaign diagram
A second campaign ran concurrently with the first; we estimate it started on June 17, 2025, and used the Customer ID f1d7b074-b81f-11ef-a763-1402ec3d56f0. It followed the same TTPs and used two installer variants: one that deployed both the malicious extension and PULSAR, and another that installed only the extension, matching the sample examined in our main analysis.
The execution chain is JavaScript -> Installer, followed by PULSAR, the malicious extension, or both. We identified three JavaScript loader variants:
Direct: Downloads the next stage directly from
hxxps://codecaudiog[.]site/87b76a60ba7c474dbf8f689df2808e1a?payload=f1d7b074-b81f-11ef-a763-1402ec3d56f0.Internet Archive: Retrieves a text file hosted on the Internet Archive containing the URL of the next stage.
Internet Archive with profiling: Performs the same retrieval process, but also profiles the host and sends the collected information to the next-stage URL.
The profiler loader collects the machine ID, the computer name, and the AV installed.
Loader profiles victim machine
The installer contacts codecvideowin[.]online or version.checkeligibitily.workers[.]dev to download the malicious extension. We were unable to recover the extension archive. However, the PULSAR version and configuration match those observed in the previous campaign.
Acrobat campaign, August 2025: PULSAR RAT only, no extension
Acrobat campaign diagram
We estimate that this campaign began on August 14, 2025. It uses the following customer ID 618ec809-f08e-4068-a54c-654478811510. It follows the sequence JavaScript -> PowerShell -> RunPE -> DonutLoader and only delivers PULSAR versions 1.7.1 and 1.7.2, with no malicious extension.
As in the previous campaign, the user is tricked into downloading and executing a JavaScript file. In this case, the lure masquerades as an Adobe plugin distributed from acrobat-updater[.]com. The loader retrieves a domain list from an Internet Archive file named acrobat-updater.com.txt, then attempts to download the second stage from each domain through /5f109e7bb3df4dea81946f2f853da288.
The second stage Base64-decodes the third-stage payload and executes it through PowerShell.
JavaScript loader decodes and executes the PowerShell stage
The third-stage PowerShell script downloads the final payload from hxxps://acrobat-updater[.]com/generate and retrieves the RunPE module from Internet Archive, concealed inside tragira.jpg. It then loads the RunPE assembly into the PowerShell process and invokes its VAI method.
Third stage loader invokes RunPE VAI method
In previous campaigns, RunPE was used only to manually map and execute the payload. In this version, it also establishes persistence by creating a Windows scheduled task. Based on the parameters passed by the third stage, the task is named AcrobatBrowserExtension and is triggered both at user logon and every 30 minutes. It downloads and executes a JavaScript payload from hxxps://acrobat-updater[.]com/api/v2/acrobat/latest, allowing the malware to reinstall or relaunch as needed.
RunPE installs persistence via a scheduled task
In this campaign, the PULSAR payloads communicate with the following C2 endpoints: 144.172.112[.]239:4782 and 45.90.13[.]210:443.
Framesync campaign, September 2025: earliest recovered extensions
We estimate that this campaign began on September 2, 2025. We recovered only the malicious extensions, but their analysis confirms that they belong to the same family as the extension examined in our main analysis. Although these are earlier versions, they already implement the full feature set described previously. This establishes a direct link between the KREMLIN name and the toolkit used during this period. We assess that the toolkit may have been known as CHROMEBALLRAT at the time, based on the tag observed in PULSAR samples and its use in other JavaScript loaders.
These extensions masquerade as FrameSync Driver System V16.9.7 and FrameSync Plugin Project V1.19.16. The associated extension ID is djodclnjknbpambeaaapadmdfhmbpeog. Its configuration reports version 12.0.0 and contains the domain lojinhadoluiz[.]online and customer ID 48502c50-a504-4811-aab8-ba978aeae237.
Framesync malicious extension’s configuration
This version contains no DINAMIC_HOST variable and does not use smart-contract-based resolution. The domain configured in ENDCENT is also unused. Instead, as in the recent version, the actual C2 endpoints are resolved dynamically by querying hxxps://orange-sun-195a.checkeligibitily.workers[.]dev, which returns separate WebSocket and HTTP endpoints for the two communication channels.
Donalurdesconfeitos to Cremeb campaign, December 2025: first KREMLIN branding
Donalurdesconfeitos to Cremeb campaign diagram
We estimate that this campaign began in December 2025. Between December and March, the actors migrated their infrastructure from donalurdesconfeitos[.]site to cremeb[.]com, using marialurdes[.]site and harialurdes[.]site as intermediate domains. This campaign marks the first observed use of the KREMLIN name and is associated with two customer IDs: 991589b0-4cc9-11f0-b9f4-1402ec3d56f0 and 48502c50-a504-4811-aab8-ba978aeae237.
In this campaign, we observed only the installer used to deploy the malicious extension. The execution chain is similar to those described previously: JavaScript -> PowerShell -> RunPE -> Installer -> Extension. We identified four loader variants based on how they decode the next stage:
Reversed data
Reverse, then Base64-decode
Base64-decode, then reverse
RC4-encrypted payload embedded in a JPEG carrier
The final loader variant is the most interesting. In some cases, we found it as a JSE file using Microsoft's proprietary JScript.Encode format. The loader contains a comment header identifying the malware as KREMLIN, crediting Kr3mlin4rt1st as its author, and specifying version 1.33, dated February 8, 2026. This is the earliest observed use of the full KREMLIN name and author attribution. The version and copyright information suggest that the script may have been distributed to other operators. However, we found no public trace of it on GitHub or GitLab; if shared, it was likely distributed through private or underground channels.
KREMLIN header describing author and version
Unlike the other three variants, this loader retrieves its payload using the same JPEG carrier technique repeatedly observed across the KREMLIN toolkit. The embedded blob is located using the marker kremlin-moscow-russia and then decrypted with RC4 using the key kr3ml1n.
Loader decrypts the next stage from the JPEG carrier with RC4
Cremeb campaign, April 2026: QR-code extension and Node.js loaders
Cremeb campaign diagram
Beginning in April 2026, we observed two concurrent campaigns using the Cremeb infrastructure and the Customer ID 48502c50-a504-4811-aab8-ba978aeae237. For the first campaign, we could not conclusively identify the initial loader. Its installer delivers PULSAR through DonutLoader alongside a new QR-themed malicious extension. The second campaign introduces the infection chain LNK -> PowerShell -> JavaScript (WSH) -> JavaScript (Node.js) -> RunPE -> DLL installer -> PULSAR & Extension. This is the earliest campaign in which we observed the loader downloading the Node.js runtime to execute the next stage, a behavior retained in the current version. Another significant change affects RunPE: rather than manually mapping the payload into its own process, it injects it into explorer.exe using Early Cascade Injection.
The QR-themed extension delivered by the first branch has the ID cdgcjghdeinagopbaobhmaefigoafaaa. It presents itself as a tool that displays an overlay containing a QR code generated from a supplied string. Clicking the extension icon opens a menu exposing exactly this functionality. During our tests, the overlay displayed a Portuguese message asking the user to reauthenticate with the current application, in this case our locally hosted playground, by scanning the QR code. The user-accessible menu suggests that a development interface was left exposed, further supporting the hypothesis of a rushed implementation.
Malicious QR-code extension
Source-code analysis confirms that this extension relies on QR-based social engineering rather than data interception. When a user visits a page matching web.whatsapp.com or www.sicoob.com.br, the extension retrieves QR-code content from hxxps://cremeb[.]com/qrcode/api/v1/read?domain=${encodeURIComponent(domain)} and displays it in an overlay. The code is unobfuscated and contains patterns consistent with LLM generation, suggesting rushed development. We could not determine what the QR code encodes, but it likely facilitates account compromise on the targeted services.
Extension fetches QR code data from C2
This campaign deploys PULSAR version 2.4.5, which communicates with the following C2 endpoints: 37.16.74[.]100:443 and 37.16.74[.]34:443.
Ethereum transition campaign, May 2026: smart contract configuration and REMCOS RAT
Ethereum transition campaign diagram
Our Ethereum blockchain analysis, presented in the following section, shows that the first smart contracts appeared in May 2026. They initially pointed to the Cremeb infrastructure (0x902EDbFECFF38f285Bf26283fB9cEB3700061873), then to granderevolucao[.]store (0x64Def0A6099c4DE9C413B108EAae85A3C7457615), before the migration to 0xCD7360A83E5cdbBbbbcEB0e78748babA6740d07b, which remains in use at the time of writing. We found no samples associated with the first two contracts, but the on-chain data confirms that the adoption of Ethereum as a dead-drop mechanism dates back to this period.
In these newer campaigns, actors use the Ethereum blockchain as a dead drop, with smart contracts storing configuration that different components of the infection chain consume. As detailed earlier, this configuration contains URLs for the various stages and payloads. We identified two chains during this period. The first, observed in June 2026, follows the sequence JavaScript -> Node.js -> RunPE -> Installer -> Extension & REMCOS RAT. The shift from PULSAR to REMCOS RAT is particularly noteworthy. As documented in our four-part analysis series, REMCOS provides broader capabilities than PULSAR. This change may represent an upgrade as the operation matures and gains resources. The second branch is the one analyzed in detail throughout this report. It uses the legitimate signed SentinelOne binary to sideload the main malicious DLL. In this configuration, the RunPE module is unused because process injection is unnecessary: the payload already executes within a trusted, signed process, helping conceal its activity. Both branches deploy the same extension analyzed in this report: the latest version of the interception-focused variant.
REMCOS RAT communicates with the C2 endpoint 178.92.162[.]38:443.
During the same period, we identified a parallel branch using a distinct infrastructure variant. Separate actors may operate this branch using a modified version of the KREMLIN toolkit. It relies on seguranca.versionnova[.]site and comprises three distinct waves:
Wave A. ba80216c960977fa45e317f00dcf31e96acab29904a737cbc0bf86e929c3be5f First observed on VirusTotal on June 22, 2026, it downloads and executes a PowerShell script from /news/connect_api.txt.
Wave B. cb15cbf3f01a92e609e4c2bc26155e667e96c5d04770e83abba66ee07bcecea0 First observed on June 26, 2026, it adds a fake Portuguese error message and a RAM-based virtual-machine check. It retrieves its payload through /nodks/connect_api.txt/.
Wave C. 170dffb37e05f525f735bc9ad84b3908a488f7ce43fcb07739a10e4331e15a2c Active from July 3 to July 14, 2026, it resembles the main lineage by retrieving its payload through /serve.php?l= and beaconing to its C2 through /testar_nova_versao/log_receiver.php, using the header X-Log-Token: MichelleMignon171.
C2 beaconing with “MichelleMignon171” token
The descriptive Portuguese route name testar_nova_versao ("test new version") and the hard-coded token provide useful attribution clues. Combined with the campaign's focus on Brazilian institutions and its repeated use of Portuguese-language artifacts, they strongly support the hypothesis that the REF9334 operators are Brazilian. The token MichelleMignon171 also appears to reference Brazilian DJ Michelle Mignon, whom the actors may be fans of.
We identified a PowerShell reimplementation of the native extension installer 42a3e2bb135fb46b11b127f45a266b3a4d9dff4aa1cf75433f93fe69ba51a9b9. It mirrors the same functionality by downloading the extension from connection.upgradeonline[.]site, deploying it into Chromium-based browser profiles, and generating the integrity values required to activate it. Although we cannot conclusively link this script to the preceding loader, VirusTotal's relationship graph connects it to both seguranca.versionnova[.]site and connection.upgradeonline[.]site. Based on this overlap, we assess with high confidence that the JavaScript loader downloaded and executed this script.
VirusTotal detection of the PowerShell installer implementation
The complete list of indicators is available in the Indicators of Compromise section.
Ethereum blockchain analysis: malicious smart contract and wallet activity
Analyzing transactions associated with the Ethereum smart contract reveals a single wallet responsible for deploying the contracts and updating the malware configuration, while also conducting several financial transactions.
The wallet was already financially active before deploying its first contract. Between June 19th 2025, and August 24th 2026, we identified 82 USDT transfers, totaling 20,778.967228 USDT received and 19,016.959182 USDT sent. Its use to deploy the malware's smart contracts suggests it is controlled by a developer or campaign operator. Although no individual transfer can be directly linked to development funding, the transaction volume makes this a valuable lead. The table below details these transfers.
Direction | Source | Destination | Transactions | Total value | First–last UTC |
|---|---|---|---|---|---|
Incoming |
|
| 9 |
| 2025-06-20–2026-08-04 |
Incoming |
|
| 2 |
| 2025-08-29–2025-09-09 |
Incoming |
|
| 1 |
| 2025-09-15 |
Incoming |
|
| 1 |
| 2025-06-19 |
Incoming |
|
| 1 |
| 2025-08-26 |
Incoming |
|
| 1 |
| 2025-09-15 |
Outgoing |
|
| 6 |
| 2025-06-20–2025-10-06 |
Outgoing |
|
| 3 |
| 2026-01-09–2026-08-21 |
Outgoing |
|
| 40 |
| 2026-07-22–2026-08-24 |
Outgoing |
|
| 1 |
| 2025-10-15 |
Outgoing |
|
| 2 |
| 2025-09-10 |
Outgoing |
|
| 2 |
| 2025-08-26–2025-08-29 |
Outgoing |
|
| 3 |
| 2026-07-14 |
Outgoing |
|
| 9 |
| 2026-07-15–2026-07-20 |
Outgoing |
|
| 1 |
| 2025-09-05 |
The developer deployed their first test smart contract, named UserName, on May 16, 2026. It contains boilerplate code likely used to experiment with smart contract development. The author then deployed several similar contracts and submitted multiple transactions calling the SetName method, which modifies a value stored in the contract. We extracted the following values from these transactions: Medina, Filosofo, Danone1555IBIZA.
The first malicious smart contract linked to KREMLIN's infrastructure was deployed on May 19, 2026. It is a slightly modified version of the UserName test contract used previously. This version adds two string variables, main and extension, which store the download URLs for the installer and malicious extension. Its transactions reveal the following values:
Smart-contract variables
The following values were observed:
Transaction | Date (UTC) | Variable-name | Value |
|---|---|---|---|
0x05ddd2131556d71352f5a10213fb1705b81dbd8d2f86fbe528a8a4be1de7d2e9 | 2026-05-19 01:38:23 |
|
|
0x05ddd2131556d71352f5a10213fb1705b81dbd8d2f86fbe528a8a4be1de7d2e9 | 2026-05-19 01:39:27 |
|
|
0x05ddd2131556d71352f5a10213fb1705b81dbd8d2f86fbe528a8a4be1de7d2e9 | 2026-05-19 01:39:27 |
|
|
0xcc0ba092c4721c69801ece58a144a0aa668feed26da4993b1fbcbcb2e7a570f9 | 2026-05-19 02:53:47 |
|
|
0x13b457af75e7cc0c7963b454d879800d8fdfbd3ebc9fe3ec34bd8ca8cf44701d | 2026-05-19 19:21:11 |
|
|
The second malicious smart contract was deployed on June 9, 2026. In this version, the main variable was renamed to domain. Its transactions set the following values:
Transaction | Date (UTC) | Variable-name | Value |
|---|---|---|---|
0xb86787588fe43d9bc6a419c94450b620fa5b60918dda9b242efd05ec73d0e013 | 2026-06-09 20:40:59 |
|
|
0xb86787588fe43d9bc6a419c94450b620fa5b60918dda9b242efd05ec73d0e013 | 2026-06-09 20:40:59 |
| Empty string |
0xb86787588fe43d9bc6a419c94450b620fa5b60918dda9b242efd05ec73d0e013 | 2026-06-09 20:40:59 |
| Empty string |
0x1b189e5ce3dbee52106de5c1a8508091ea2acc2cbace7252763b53be01af3109 | 2026-06-09 20:45:11 |
|
|
0x49ee0b041878c64c9253955d1de44f3832bb2b96b891cd3fd85c136bbbae0f71 | 2026-06-09 20:46:35 |
|
|
0x645c1701acd8a5f2c9364ac947718863ab450a706c0aabfca85376e9374ccb45 | 2026-06-16 04:13:59 |
|
|
This is the first observed use of the connection. subdomain. In these cases, the parent domains appear to host legitimate websites that the actors compromised. Using a separate subdomain lets them host malicious infrastructure alongside the original website without disrupting it.
Hijacked news website
The next iteration of the contract, deployed on June 16, 2026, replaces the individual configuration variables with a single mapping. It exposes explicit methods for updating and querying this mapping: setConfig and getConfig.
Smart-contract getConfig method
The following values are used:
Transaction(s) | Date / time span (UTC) | Count | Variable-name | Value |
|---|---|---|---|---|
2026-06-16 22:41:23 | 1 |
|
| |
2026-06-16 22:48:47 | 1 |
|
| |
2026-06-16 22:51:23 to 22:58:47 | 3 |
|
| |
2026-06-16 22:56:11 to 22:56:23 | 2 |
|
|
The latest contract, which remains active at the time of writing, was deployed on June 16, 2026. This version introduces main-v2, which likely corresponds to the installer that uses DLL sideloading, replaces steganography with sub-module, and adds sentinel for the legitimate SentinelOne binary. The following values were observed:
Transaction | Date (UTC) | Count | Variable-name | Value |
|---|---|---|---|---|
2026-06-16 23:02:23 | 1 |
|
| |
2026-06-16 23:04:35 | 1 |
|
| |
2026-06-16 23:04:59 | 1 |
|
| |
2026-06-16 23:05:35 | 1 |
|
| |
2026-06-26 04:17:47 | 1 |
|
| |
2026-06-26 04:42:47 | 1 |
|
| |
2026-06-27 15:47:35 | 1 |
|
| |
2026-06-27 15:56:23 | 1 |
|
| |
2026-07-07 02:19:59 | 1 |
|
| |
2026-07-22 03:08:59 | 1 |
|
| |
2026-07-22 03:09:59 | 1 |
|
| |
2026-07-22 03:27:23 | 1 |
|
| |
2026-07-22 04:36:47 | 1 |
|
| |
2026-07-23 06:16:47 | 1 |
|
| |
2026-07-23 07:33:23 | 1 |
|
| |
2026-07-23 07:33:59 | 1 |
|
| |
2026-08-10 17:35:23 | 1 |
|
| |
2026-08-13 01:53:23 | 1 |
|
|
Infrastructure, victimology, and Brazilian bank targeting
Analysis of the associated domains confirms earliest activity back to June 2025. The reconstructed timeline is shown below:
Reconstructed domains timeline
Geolocation analysis of the IP addresses associated with the domains provides no additional insight into the actors' location. The addresses span multiple countries, and many domains are proxied through Cloudflare. The table below summarizes these geolocations:
Domain / IP | First Campaign / DNS Observation (UTC) | Resolved IP | Geo |
|---|---|---|---|
| 2025-06-17 |
| Cloudflare |
| 2025-06-30 |
| Finland |
| 2025-07-30 |
| Cloudflare |
| 2025-07-31 |
| Cloudflare |
| 2025-07-31 |
| Cloudflare |
| 2025-07-31 |
| Cloudflare |
| 2025-08-12 |
| NA |
| 2025-08-12 |
| NA |
| 2025-08-12 |
| NA |
| 2025-08-12 |
| NA |
| 2025-08-14 |
| United States |
| 2025-08-14 |
| Cloudflare |
| 2025-08-19 |
| Cloudflare |
| 2025-09-02 |
| Cloudflare |
| 2025-09-03 |
| Netherlands |
| 2025-09-12 |
| Russia |
| 2025-09-12 |
| Russia |
| 2025-09-13 |
| Cloudflare |
| 2025-09-13 |
| Cloudflare |
| 2025-11-18 |
| Russia |
| 2025-12-02 |
| NA |
| 2026-02-19 |
| NA |
| 2026-02-23 |
| NA |
| 2026-04-10 |
| Netherlands |
| 2026-04-17 |
| Netherlands |
| 2026-04-17 |
| Cloudflare |
| 2026-04-30 |
| Switzerland |
| 2026-04-30 |
| Switzerland |
| 2026-05-04 |
| Cloudflare |
| 2026-05-04 |
| Cloudflare |
| 2026-05-28 |
| Netherlands |
| 2026-06-09 |
| NA |
| 2026-06-16 |
| Germany |
| 2026-06-30 |
| Cloudflare |
| 2026-06-30 |
| Cloudflare |
| 2026-07-10 |
| Cloudflare |
| 2026-07-10 |
| Cloudflare |
| 2026-07-10 |
| Cloudflare |
| 2026-07-10 |
| Cloudflare |
| 2026-07-16 |
| Brazil |
| 2026-07-22 |
| Cloudflare |
| 2026-07-22 |
| Cloudflare |
| 2026-08-05 |
| Switzerland |
| 2026-08-05 |
| Switzerland |
| 2026-08-07 |
| Canada |
| 2026-08-07 |
| Canada |
| 2026-08-07 |
| Canada |
| 2026-08-07 |
| Canada |
| 2026-08-10 |
| Germany |
| 2026-08-22 |
| Cloudflare |
| 2026-08-22 |
| Cloudflare |
| 2026-08-25 |
| United States |
| NA |
| United States |
| NA |
| United States |
| NA |
| United States |
| NA |
| Netherlands |
Several clues suggest that the actors are Portuguese-speaking and possibly based in Brazil, matching the campaign's geographic focus. Multiple domains and files use Portuguese names, but the strongest clue comes from the QR extension: its code shows signs of LLM generation, and the generated comments are also written in Portuguese, likely reflecting the language used in the prompts.
LLM-generated comments in Portuguese
Because Portuguese is spoken across several regions, we analyzed the Ethereum transaction timestamps for a potential geographic pattern. In UTC-3, only around ten fall within late-night hours, without extending particularly far into the early morning. Assuming the operators are more likely to work late than wake before dawn, this distribution aligns most closely with São Paulo time and supports Brazil as a plausible location for the operators.
ETH wallets activity time analysis
The victim's geography is easier to establish. User-facing text and lure filenames are written in Portuguese, indicating that the campaigns target Portuguese-speaking users. Many JavaScript loaders use filenames crafted to resemble legitimate documents. Most follow the pattern <PortugueseTheme><InstitutionOrQualifier>_DD-MM-YYYY.<10Digits>.js and impersonate Brazilian banks and payment services, including Banco do Brasil, Caixa, Bradesco, Sicoob, C6 Bank, Inter, BTG, Safra, PagBank, PicPay, Santander, and Mercado Pago. Combined with the fake Portuguese error messages, this focus on Brazilian brands strongly indicates that the victims are primarily located in Brazil.
We reconstructed the following timeline showing how these naming conventions evolved:
Observed span | Portuguese template/examples | English meaning |
|---|---|---|
2026-07-22–08-03 |
| receipt / proof of payment |
2026-07-23–08-03 |
| bank statement |
2026-07-23–08-10 |
| PIX instant-payment record |
2026-07-23–07-31 |
| payment |
2026-07-23–07-31 |
| TED bank transfer |
2026-07-24–07-30 |
| bank transfer |
2026-07-24–07-29 |
| bank document |
2026-07-24–08-04 |
| receipt / private receipt |
2026-07-24–07-31 |
| PDF / access PDF |
2026-08-06–08-13 |
| location/security/public regulation |
2026-08-06–08-10 |
| security/operating document |
2026-08-06 |
| location certificate |
2026-08-07–08-10 |
| public/operating permit or license |
Caging the canary
Threat Command researchers decided to register the network canary domain and observe how implants interacted with the previously unregistered domain. Once we registered the domain and pointed to our webhost, we were able to see infected systems checking into the network canary domain with a single GET request.
Now that the domain can be reached, the loader assumes it is in a sandbox and crashes itself to prevent analysis; which also means their infections have not moved past the initial access.
At the time of this writing, we have observed 1,515 infected systems attempting to check into the network canary domain - 98.75% are from Brazil, confirming earlier reporting on victimology. The number of infected systems is accelerating rapidly, indicating this campaign was just beginning.
Map showing infected hosts attempting to check into the canary domain
So while these systems are still infected with the final component of KREMLIN, this has temporarily degraded and manipulated the campaign's defense mechanisms and could provide defenders with additional time to identify and remediate infected endpoints.
Indicators of compromise
These indicators are also available for download here.
MITRE ATT&CK tactics and techniques
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.
Text extracted automatically; images, tables and formatting may be missing. Original: https://www.elastic.co/security-labs/threat-command/malicious-browser-extension-kremlin-banking-malware