Tsundere Botnet — Node.js Binary

The Tsundere botnet, identified by Kaspersky Global Research and Analysis Team (GReAT) in July 2025, represents a sophisticated evolution in cross-platform malware campaigns orchestrated by a Russian-speaking threat actor known as “koneko.” Initially surfacing through a 2024 npm supply-chain attack involving 287 typosquatted Node.js packages, Tsundere has matured into an actively expanding botnet primarily targeting Windows systems. Leveraging blockchain technology for command-and-control (C2) resilience, it enables dynamic execution of arbitrary JavaScript payloads, posing risks of data exfiltration, cryptocurrency theft, and further compromise.

Note: I analyzed this sample on my own, after my analysis I identified the malware as being Tsundre Botnet, based on the activity Kaspersky reported on here: The Tsundere botnet uses the Ethereum blockchain to infect its targets | Securelist. I will be reviewing IOC’s and if the ones I observed are still consistent with what was previously reported.

SHA256: 16e0dbcc6670e7722f68620b6f305e2c4433ed6f7b25174a75480ed5c4b4fe42



This hash was initially reported yesterday, December 3, 2025, and there was no real indicators that this was malicious. Comments also reflected one person believing that this was a benign binary based on analysis from an automated sandbox.

Press enter or click to view image in full size

Sample.js

This JavaScript (JS) code is pretty heavily obfuscated. The obfuscation combines string encryption, control flow flattening, identifier mangling, and runtime decryption, making it challenging to analyze without deobfuscation tools or manual unpacking.

String Obfuscation via Custom Base64 + RC4 Decryption:

Nearly all strings (e.g., URLs, function names, console messages) are stored in encrypted arrays and decrypted at runtime using a hybrid Base64 decoder followed by an RC4 (ARC4) stream cipher. This prevents static string-based signatures (e.g., YARA rules) from triggering.

Implementation Details:

  • The core decoder is defined in the _0x2905 function (lines ~1–50). It initializes an RC4 key schedule using a user-provided key (_0x3495d6).

  • Step 1: Base64-like decoding. A mangled Base64 alphabet (‘abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=’) decodes input into a URI-encoded string (e.g., %XX format), then decodeURIComponent expands it.

  • Step 2: RC4 decryption. The decoded string is fed into an RC4 keystream generator

// Simplified pseudocode from _0x2905['SbIfNX']
for (let i = 0; i < 256; i++) sbox[i] = i;  // Initialize S-box
j = 0;
for (let i = 0; i < 256; i++) {
  j = (j + sbox[i] + key.charCodeAt(i % key.length)) % 256;
  swap sbox[i] and sbox[j];
}
// Keystream XOR with plaintext
for (let i = 0; i < plaintext.length; i++) {
  i_idx = (i + 1) % 256;
  j = (j + sbox[i_idx]) % 256;
  swap sbox[i_idx] and sbox[j];
  k = sbox[(sbox[i_idx] + sbox[j]) % 256];
  output += String.fromCharCode(plaintext.charCodeAt(i) ^ k);
}

Identifier Mangling with Hexadecimal Names

All variables, functions, and properties use randomized hexadecimal prefixes (e.g., _0x2905, _0x381842, _0x4f8a99). This is generated by tools like javascript-obfuscator, renaming ~90% of identifiers to meaningless shorts.

Implementation Details:

  • Functions like _0x4f8a() return the string array.

  • Nested helpers (e.g., _0x56f881(_0x575caf — -0xf5, _0x3c7903) ) compute indices via arithmetic offsets (e.g., arg — 0x17b).

  • Multiple layers: Outer _0x2905, inner _0x232dae, _0x5cb090, each with its own array and offset math.

Control Flow Flattening with While-Try-Catch Loops

Linear code is “flattened” into opaque predicates — while loops that shift/rotate an array until a computed checksum matches a magic value. This disrupts disassemblers and adds anti-debugging (e.g., infinite loops if tampered).

while(!![]){  // Infinite loop
  try {
    const _0x1b1805 = parseInt(_0x56f881(0x238, ...)) / 1 + ...;  // Compute sum of obfuscated ints
    if (_0x1b1805 === _0xad14cc) break;  // Magic: 0x704f6 (~458086)
    else _0x596a8d['push'](_0x596a8d['shift']());  // Rotate array
  } catch { _0x596a8d['push'](_0x596a8d['shift']()); }
}

Multi-Layered Obfuscation and Runtime Code Execution

Obfuscation is nested (e.g., _0x2905 calls _0x232dae, which calls _0x5cb090). Dynamic new Function() executes decrypted payloads, enabling further evasion.

Implementation Details:

  • Layers: 4+ decoders (_0x2905, _0x232dae, _0x5cb090, _0x31de0e). Each has its own array (e.g., _0x41cd62 with 70+ hex keys).

  • Runtime Eval: In onMessage (lines ~600+), decrypts incoming WebSocket data, then

const _0x33324b = new Function('require', 'global', ..., decrypted_code);
_0x33324b(require, global, ...);  // Executes arbitrary JS
  • Payloads include overrides like global.serverSend for C2 callbacks.

  • AES Encryption: Outbound/inbound messages use AES-256-CBC (crypto module) with runtime keys/IVs (16-byte IV check).

Anti-Analysis and Evasion Techniques

  • Dynamic Imports: require(‘ws’), require(‘crypto’), require(‘os’), require(‘ethers’) — delays footprint.

  • Error Handling: Broad try-catch swallows exceptions, logging minimally

  • Timing/Polling: Ping-pong over WebSocket (30s interval), reconnects on failure (15s timeout).

  • Platform-Specific: Windows-focused (e.g., wmic queries for UUID, GPU via PowerShell). Collects sysinfo (MAC, BIOS, volume serial) hashed into UUID.

System Fingerprinting

The malware collects victim data:

  • Username (os.userInfo())

  • Hostname (os.hostname())

  • Platform/Architecture

  • CPU information

  • GPU information (WMI: Win32_VideoController)

  • MAC Address (first non-internal interface)

  • Total Memory

  • Node.js Version

  • Windows Edition (WMI: Win32_OperatingSystem)

  • Volume Serial Number (vol command)

  • BIOS Information (WMI: SystemBIOS)

  • System UUID (Registry: MachineGuid)

All data is hashed (SHA256) to create a unique userId in UUID format.

Press enter or click to view image in full size

Connections and Host Enumeration

CIS Country Kill Switch (Ukraine being an exception)

  • hy (Armenian)

  • hy-AM (Armenian — Armenia)

  • az (Azerbaijani)

  • be (Belarusian)

  • be-BY (Belarusian — Belarus)

  • kk (Kazakh)

  • ky (Kyrgyz)

  • ky-KG (Kyrgyz — Kyrgyzstan)

  • ru (Russian)

  • ru-RU (Russian — Russia)

  • ru-BY (Russian — Belarus)

  • ru-KG (Russian — Kyrgyzstan)

  • ru-MD (Russian — Moldova)

  • ru-UA (Russian — Ukraine)

  • tg (Tajik)

  • uk (Ukrainian)

  • uk-UA (Ukrainian — Ukraine)

  • uz (Uzbek)

Remote Code Exection (RCE)

When receiving a message with id=1, the malware:

1. Creates a new Function() with the received code

2. Provides access to: require, global, console

3. Executes the code in a try-catch wrapper

4. Sends results back via serverSend() callback

This allows the C2 server to push arbitrary Node.js code for execution.

Registry Queries:

  • HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid (System UUID)

  • HKLM\SYSTEM\CurrentControlSet\Control\SystemInformation\SystemBIOSVersion

When I execute the JS binary in cmd.exe:

Press enter or click to view image in full size

We see it making connections to RPC at multiple different Ethereum Wallet Endpoints. It also gathers host enumeration details as defined earlier in the code review.

The Process Tree:

Press enter or click to view image in full size

Process Tree Breakdown

node.exe (8072)
├── “C:\Program Files\nodejs\node.exe” …16e0dbcc6670e7722f68620b6f305e2c4433ed6f7b25174a75480ed5c4b4fe42.js

├── cmd.exe (3812) → powershell.exe “[System.Globalization.CultureInfo]::InstalledUICulture.Name”
│ └── [CIS LOCALE CHECK — Kill Switch]

├── cmd.exe (7164) → powershell.exe “Get-WmiObject Win32_VideoController | Select-Object -ExpandProperty Name”
│ └── [GPU FINGERPRINTING]

├── cmd.exe (7252) → reg query “HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion” /v ProductName
│ └── [WINDOWS EDITION]

├── reg.exe (4356) → reg query “HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion” /v ProductName
│ └── [WINDOWS EDITION — direct call]

├── cmd.exe (4440) → cmd.exe /d /s /c “vol”
│ └── [VOLUME SERIAL NUMBER]

├── cmd.exe (7008) → reg query “HKLM\HARDWARE\DESCRIPTION\System\BIOS”
│ └── [BIOS FINGERPRINTING]

├── reg.exe (8068) → reg query “HKLM\HARDWARE\DESCRIPTION\System\BIOS”
│ └── [BIOS — direct call]

├── cmd.exe (832) → reg query “HKLM\SOFTWARE\Microsoft\Cryptography” /v MachineGuid
│ └── [SYSTEM UUID — Unique identifier]

├── reg.exe (7432) → reg query “HKLM\SOFTWARE\Microsoft\Cryptography” /v MachineGuid
│ └── [SYSTEM UUID — direct call]

├── cmd.exe (1496) → powershell.exe “[System.Globalization.CultureInfo]::InstalledUICulture.Name”
│ └── [SECOND LOCALE CHECK — possibly in reconnect loop]

└── powershell.exe (4) → “[System.Globalization.CultureInfo]::InstalledUICulture.Name”
└── [THIRD LOCALE CHECK]

DNS Query to one of the Ethereum Wallet Endpoints

DNS Query to a second Ethereum Wallet Endpoint

Shodan

Example of the Websocket Handshake Get Request:

CLIENT REQUEST:

— — — — — — — -

GET / HTTP/1.1

Sec-WebSocket-Version: 13

Sec-WebSocket-Key: gcG7wVAmx5B2IhtwTdv9WQ==

Connection: Upgrade

Upgrade: websocket

Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

Host: 193.24.123.68:3011

Traffic Flow Summary:

  • 13808 551.95 →C2 227 WebSocket handshake request

  • 13815 552.12 ←C2 129 101 Switching Protocols

  • 13816 552.12 ←C2 34 AES-256 Key (binary)

  • 13818 552.13 →C2 6 ACK

  • 13822 552.29 ←C2 18 AES IV (binary)

  • 13923 553.41 →C2 520 Encrypted victim fingerprint

  • 13997 553.63 ←C2 50 Encrypted ack: “Connected”

IOCs

Network:

  • 193.24.123.68:3011 (WebSocket C2)

  • rpc.flashbots.net (Ethereum RPC)

  • rpc.mevblocker.io (Ethereum RPC)

  • eth.llamarpc.com (Ethereum RPC)

  • eth.merkle.io (Ethereum RPC)

  • eth.drpc.org (Ethereum RPC)

Also, these are all new IOC’s compared to the Kaspersky report:


Encryption

  • AES Key: e5f4e1b5d1065b0ecd6b3ef972d451e1c63ecd8da4d73b82cf429d70d13d166f

  • AES IV: 4e3d21a0941bb92632c4997fd4a582b1

Thank you! Critque welcome and appreciated!

August Vansickle


Twitter: @LunchM0n3ey9090

Linkedin: August Vansickle | LinkedIn

References:

The Tsundere botnet uses the Ethereum blockchain to infect its targets | Securelist

Previous
Previous

Announcing “UpdateHub RAT”

Next
Next

100 Days of Yara - Day 15 2025