Skip to content
Malicious Browser Extension Kremlin Banking Malware

Malicious Browser Extension Kremlin Banking Malware

www.elastic.co September 15, 2026

Elastic Security Labs tracked this malicious browser extension across seven campaigns and 15 months, through Brazilian bank lures and the Ethereum smart contracts that hold its C2 configuration.

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 the operation is Russian. Lures impersonate twelve Brazilian banks; error messages and code 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.

The KREMLIN malware ecosystem employs multi-stage JavaScript loaders, custom C++ installers, and malicious browser extensions to steal credentials, session tokens, and sensitive data.

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.

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.

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.

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

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 binary stage.

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.

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 JavaScript stage using certutil, downloads Node.js to execute it, and finally beacons to one of its C2 servers.

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.

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 .

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.

Installs persistence through a scheduled task extracted from an embedded CAB archive.

Retrieves download locations from an Ethereum smart contract.

Retrieves download locations from an Ethereum smart contract.

Downloads the required binaries from those locations.

Downloads the required binaries from those locations.

Executes the third stage.

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.

It then retrieves its configuration from the Ethereum smart contract at 0xCD7360A83E5cdbBbbbcEB0e78748babA6740d07b by querying three parameters:

main-v2 : The URL of the main module.

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.

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.

sentinel : The URL of a JPEG carrier holding a CAB file, which contains a SentinelOne binary used to sideload the malware.

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.

At the time of the analysis, the payload URLs were as follows:

Smart contract parameter

Malicious browser extension installer payload

hxxps://granderevolucao[.]store/5c92d3b8734b4f498752f735a1ca0987/{campaignId}

hxxps://archive[.]org/download/hotelmoskva/hotelmoskva.jpg

SentinelMemoryScanner.exe : legit SentinelOne binary for side-loading

hxxps://ia601808[.]us[.]archive[.]org/5/items/sentinel_20260722_0435/Sentinel.jpg

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.

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.

Malware side-loading and entrypoint

To execute the 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 .

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.

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.

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.

VMware and VirtualBox artifact checks

The final check before the 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 : 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 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.

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.

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 .

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.

The final value KREMLIN retrieves is a seed extracted from %PROGRAMFILES%\Google\Chrome\Application\ \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. . 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:

KREMLIN malicious Chrome extension: capabilities and C2 protocol

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.

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 command. The initial exchanges are shown below:

The extension supports the following commands:

Captures the selected or active tab then uploads the compressed image.

Enumerates tabs, domains, and active state; uploads the list and returns it over WebSocket.

Get cookies and storage

Steals cookies, sessionStorage , and localStorage from the selected or active tab.

Collects up to 1,000 history entries from the 15 days. This likely fails because the manifest lacks the history permission.

Extracts and uploads the full HTML source of the selected or active page.

Downloads attacker-controlled HTML using scriptHash , substitutes dynamic variables, injects it into the page, and reports interactions through INJCLIST .

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:

Upload cookies and storage

Uploads LZ/Base64-compressed cookies, sessionStorage , localStorage , and page URL with the client ID.

Uploads compressed tab IDs, domains, and active-tab state with the client ID.

Uploads compressed browser history from the 15 days with the client ID.

Uploads a compressed JPEG screenshot, page URL, and client ID.

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 .

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 and 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 and 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

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 -br@protonmail[.]com .

The table below summarizes the seven campaigns described in this section.

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

f1d7b074-b81f-11ef-a763-1402ec3d56f0

JavaScript → Installer

Extension, PULSAR, or both

codecaudiog[.]site, codecvideowin[.]online, version.checkeligibitily.workers[.]dev

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

48502c50-a504-4811-aab8-ba978aeae237

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

donalurdesconfeitos[.]site, marialurdes[.]site, harialurdes[.]site, cremeb[.]com

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

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

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.

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.

Codecaudiog B campaign, June 2025: three JavaScript loader variants

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 stage directly from hxxps://codecaudiog[.]site/87b76a60ba7c474dbf8f689df2808e1a?payload=f1d7b074-b81f-11ef-a763-1402ec3d56f0 .

Direct: Downloads the 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 stage.

Internet Archive: Retrieves a text file hosted on the Internet Archive containing the URL of the stage.

Internet Archive with profiling: Performs the same retrieval process, but also profiles the host and sends the collected information to the -stage URL.

Internet Archive with profiling: Performs the same retrieval process, but also profiles the host and sends the collected information to the -stage URL.

The profiler loader collects the machine ID, the computer name, and the AV installed.

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 campaign.

Acrobat campaign, August 2025: PULSAR RAT only, no extension

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 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.

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.

In 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.

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 .

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

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 stage:

Reverse, then Base64-decode

Reverse, then Base64-decode

Base64-decode, then reverse

Base64-decode, then reverse

RC4-encrypted payload embedded in a JPEG carrier

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 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.

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 .

Cremeb campaign, April 2026: QR-code extension and Node.js loaders

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 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 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 suggests that a development interface was left exposed, further supporting the hypothesis of a rushed implementation.

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 , 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.

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

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 .

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.

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.

0x8a711333899C173A1DC1a3523335e52Becce9A44

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

2025-06-20–2026-08-04

0x25a6a4fe0cc0f8ebf19836ad50fe104c3cbc9d6a

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

2025-08-29–2025-09-09

0x56eddb7aa87536c09ccc2793473599fd21a8b17f

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0xb2b7e8403b4534d43b477d4d4bd6f829437463c8

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0xdd3d72c53ff982ff59853da71158bf1538b3ceee

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0x28c6c06298d514db089934071355e5743bf21d60

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0x1AD4436893850Cc1dA180b2488e764bEB9E2A379

2025-06-20–2025-10-06

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0x737A8DeA4Db63B3b24f19698AF9e5Bc6f08DE8EE

2026-01-09–2026-08-21

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0x77e2d84e79D65CE84C2dB606E984380A88F4594f

2026-07-22–2026-08-24

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0x5b3f4643d012ad6caca0a392b1a54b142b59aba5

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0xAC0a95225938E1D85C1E41e35495563eF733947a

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0x81ffb6c5f72e934a79b46a867063bff5a7a222b1

2025-08-26–2025-08-29

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0xd3d8d6b0e6d0f8dd3705247b54b8fe55f1c77567

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0x7e27a030b8879cea5e92e3da650eba0098116908

2026-07-15–2026-07-20

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0x8236a0bcf102db910df27190dccc57a75e9faa8b

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:

The following values were observed:

0x05ddd2131556d71352f5a10213fb1705b81dbd8d2f86fbe528a8a4be1de7d2e9

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0x05ddd2131556d71352f5a10213fb1705b81dbd8d2f86fbe528a8a4be1de7d2e9

0x05ddd2131556d71352f5a10213fb1705b81dbd8d2f86fbe528a8a4be1de7d2e9

0xcc0ba092c4721c69801ece58a144a0aa668feed26da4993b1fbcbcb2e7a570f9

hxxps://cremeb[.]com/kremlin?p=########-####-####-####-############&prefix=NF&prefix_count=10&output=base64

0x13b457af75e7cc0c7963b454d879800d8fdfbd3ebc9fe3ec34bd8ca8cf44701d

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:

0xb86787588fe43d9bc6a419c94450b620fa5b60918dda9b242efd05ec73d0e013

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

0xb86787588fe43d9bc6a419c94450b620fa5b60918dda9b242efd05ec73d0e013

0xb86787588fe43d9bc6a419c94450b620fa5b60918dda9b242efd05ec73d0e013

0x1b189e5ce3dbee52106de5c1a8508091ea2acc2cbace7252763b53be01af3109

connection.timesmaluku[.]com

0x49ee0b041878c64c9253955d1de44f3832bb2b96b891cd3fd85c136bbbae0f71

granderevolucao[.]store

0x645c1701acd8a5f2c9364ac947718863ab450a706c0aabfca85376e9374ccb45

connection.upgradeonline[.]site

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.

The 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 .

The following values are used:

Date / time span (UTC)

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

granderevolucao[.]store

0x66b730…83ef4 , 0x4bbe79…7a99bd , 0x94ca4a…42c6f2

2026-06-16 22:51:23 to 22:58:47

connection.upgradeonline[.]site

0xbb017d…062e00 , 0xa8e9ae…f4c87a

2026-06-16 22:56:11 to 22:56:23

config["steganography"]

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:

0x5C32A09873be70a92fd8bB5A9fED7967dE06BdE6

granderevolucao[.]store

connection.upgradeonline[.]site

config["steganography"]

config["steganography-stg"]

config["steganography-stg"]

config["binary_chunks"]

californicationdetroit[.]com

granderevolucao[.]store

californicationdetroit[.]com

Infrastructure, victimology, and Brazilian bank targeting

Analysis of the associated domains confirms earliest activity back to June 2025 . The reconstructed timeline is shown below:

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:

First Campaign / DNS Observation (UTC)

version.checkeligibitily.workers.dev

version.checkeligibitily.workers.dev

version.checkeligibitily.workers.dev

2606:4700:3035::6815:44fa

version.checkeligibitily.workers.dev

2606:4700:3037::ac43:c886

affordableonline.online

orange-sun-195a.checkeligibitily.workers.dev

version.checkeligibitily.workers.dev

version.checkeligibitily.workers.dev

donalurdesconfeitos.site

find-postman.ddesdokww.workers.dev

granderevolucao.store

connection.timesmaluku.com

connection.upgradeonline.site

seguranca.versionnova.site

seguranca.versionnova.site

graph.checkeligibitily.workers.dev

graph.checkeligibitily.workers.dev

graph.checkeligibitily.workers.dev

2606:4700:3035::6815:44fa

graph.checkeligibitily.workers.dev

2606:4700:3037::ac43:c886

californi...

Extracted Entities

Attack Types (1)

Campaigns (1)

Email Addresses (1)

eth (19)

Industries (1)

Platforms (2)