On May 5, 2025, security firm Aikido detected unauthorized malicious versions of the `rand-user-agent` npm package, a once-popular library (45k weekly downloads) used to generate randomized user-agent strings for web scraping and testing. Threat actors exploited its semi-abandoned status to inject a Remote Access Trojan (RAT) via versions `1.0.110`, `2.0.83`, and `2.0.84`, bypassing GitHub's source code repository and targeting npm artifacts directly.
Technical Anatomy of the Attack
1. Malicious Code Injection
- File: Obfuscated payload hidden in `dist/index.js`, visible only via horizontal scrolling on npm’s UI.
- Obfuscation Layers:
- String Shuffling: A custom `pHg` function rearranged characters to evade static analysis.
- Multi-Stage Execution: Decrypted malicious payloads via nested functions (`zlJ`, `fqw`).
- Dynamic Imports: Used `global["r"] = require` to bypass dependency checks.
2. Payload Execution
- Persistence Mechanism:
- Created `~/.node_modules` in the user’s home directory.
- Modified `module.paths` to prioritize this directory, enabling sideloading of malicious dependencies (`axios`, `socket.io-client`).
- C2 Infrastructure:
- Socket.IO Server: `http://85.239.62[.]36:3306` (command delivery).
- File Exfiltration: `http://85.239.62[.]36:27017/u/f` (HTTP POST).
- Data Harvesting: Transmitted system fingerprints:
Hostname: [Victim Hostname]
Username: [Current User]
OS Type: [Windows/Linux/macOS]
UUID: [Generated via crypto.randomBytes]
3. RAT Capabilities
| Command | Function |
|---|
| `cd <path>` | Change working directory. |
| `ss_dir` | Reset directory to the script’s original path. |
| `ss_fcd:<path>` | Force-change directory (bypass permissions). |
| `ss_upf:f,d` | Upload file `f` to destination `d` (e.g., `ss_upf:passwords.txt,/exfil`). |
| `ss_upd:d,dest` | Upload all files in directory `d` to `dest`. |
| `ss_stop` | Halt ongoing file transfers. |
| Any shell cmd | Execute arbitrary commands via `child_process.exec()`. |
- Windows-Specific Hijacking: Prepended `%LOCALAPPDATA%\Programs\Python\Python3127` to `PATH`, enabling execution of malicious binaries masquerading as Python tools.
Attack Vector: How the Package Was Compromised
- Compromised npm Token: Attackers used an outdated automation token from a maintainer, lacking 2FA, to publish malicious versions directly to npm.
- Version Spoofing: Incremented version numbers (`2.0.82` → `2.0.83/2.0.84`) to mimic legitimacy.
- GitHub Decoupling: Malicious code existed only in npm artifacts; GitHub repo remained untouched, delaying detection.
Indicators of Compromise (IoCs)
- Malicious Versions: `1.0.110`, `2.0.83`, `2.0.84`.
- Network Activity:
- `85.239.62.36:3306` (TCP, C2 socket).
- `85.239.62.36:27017/u/f` (HTTP POST, file uploads).
- File System Artifacts:
- `~/.node_modules` (hidden directory).
- `node_modules/rand-user-agent/dist/index.js` (obfuscated payload).
- Processes: Unusual `child_process.exec()` activity or Python3127-related paths in `PATH`.
Mitigation & Remediation: Immediate Actions
1. For Affected Systems
- Step 1: Identify installed versions:
If versions `1.0.110`, `2.0.83`, or `2.0.84` are present:
- Step 2: Uninstall the package:
npm uninstall rand-user-agent
- Step 3: Audit system for:
- Files under `~/.node_modules`.
- Unauthorized connections to `85.239.62.36`.
- Unusual processes spawned from `node` or `python`.
2. Long-Term Security Enhancements
npm profile enable-2fa auth-and-writes
- Scope Automation Tokens: Limit tokens to specific packages/IP ranges.
- Adopt Forked Alternatives: Switch to actively maintained forks like `random-user-agent-generator`.
Developer Statement: Lessons from the Breach
In a comment, the maintainers clarified: > “The attacker exploited an outdated token without 2FA. We’ve since invalidated all legacy tokens, enforced 2FA, and will implement automated npm-GitHub version parity checks.”