Open VSX "Evil Twin" Extension Campaign

77 counterfeit VS Code-compatible extensions published to Open VSX 2026-07-26 → 2026-08-01, all beaconing to mangorbit[.]com. 19 exfiltrate Git and CI/CD identity. Packages pulled 2026-08-03; the collector infrastructure is still live and installed copies still beacon.
Threat
Open VSX Evil Twin / mangorbit
Severity
HIGH
Type
Developer supply chain / registry squatting
Access
Extension install (human or automated by name)
Version
v0.1 · 2026-08-08
Author
HuntPack
Confidence
High (primary research + 4 corroborating sources)
01

Executive Summary

Between 2026-07-26 and 2026-08-01, 77 counterfeit extensions were published to the Open VSX registry. Each one republished the name, namespace and description of a real VS Code Marketplace extension at a deliberately low version (almost always 0.0.1) from an account that did not own the namespace. The bundled extension.js was replaced with a beacon. All 77 contact mangorbit[.]com, a domain registered 2026-07-15, eleven days before the first upload, through a registrar that redacts registrant details.

The attacker objective is organisational reconnaissance of software engineering estates, not code execution or credential theft. 58 packages ("payload A") are 1.6–3.3 KB and send little more than a hostname. The other 19 ("payload B") are roughly 10 KB and, four to five seconds after editor activation, assemble a single request containing the hostname and OS username, editor name/version/host kind/machine ID, platform, architecture, locale, timezone, workspace folder name and full filesystem path; then they read .git/config and .git/HEAD for the origin and upstream remote host plus organisation, the domain portion of the configured commit email, the current branch and the HEAD commit SHA; then they read CI identity values — GITHUB_REPOSITORY, CI_PROJECT_PATH, the Azure DevOps collection URI, the Buildkite organisation slug, the CircleCI project username, the Codespace name and the Gitpod workspace context URL. On a build runner or a cloud development environment that is the full private repository path, not just the organisation. Up to 60 installed extension IDs, the proxy hostname and the editor's telemetry opt-out state go with it, and the data is sent regardless of that opt-out.

The listings carried a "Telemetry" section that described most of the collection accurately, which is what let the packages pass a human skim. One claim is false: the listing says CI data is marker names only, never values. The code sends both. The listings are also silent on three behaviours the code performs — reporting whether the workspace's own devcontainer.json or .vscode/extensions.json pulled the extension in, reading the editor telemetry preference, and querying a DNS TXT record at _beacon.<domain> for a replacement collector base URL when every hardcoded endpoint fails.

Two properties make this a durable hunt rather than a one-week IOC sweep. First, removal does not un-provision: Open VSX pulled the packages on 2026-08-03, but any image, golden workstation, devcontainer or provisioning script already pinned to one of these names keeps the code, and the beacon fires on every editor start with a retry schedule that persists for seven days. Second, there is nothing for a scanner to match — no eval, no spawned shell, no obfuscation, no write to disk. The signal is behavioural: an extension whose advertised job is inserting file-header comments has no reason to read .git/config, enumerate CI environment variables, and open a connection to an eleven-day-old domain seconds after startup.

Defender priority: block *.mangorbit[.]com at DNS and egress today — Manifold confirms all 77 samples reference it — then inventory what your fleet and your CI images actually have installed, not what your configs request. Q1 and Q11 answer "were we hit"; Q3, Q4 and Q7 are the queries that will still work when the operator moves collectors over the _beacon TXT channel.

02

Source Review & Web Hunter Notes

TierSourceKey findingCarry forward
1 · PrimaryManifold Security — 77 "evil twin" Open VSX extensions (Ax Sharma, Cody Nash, 2026-08-04)Original research. Full indicator set, the two payload classes, retry schedule, TXT failover, and a 77-row table with extension ID, version, payload class, beacon hosts, first-seen date and VSIX SHA-256.Yes — every atomic IOC in this pack traces to this source
2 · CorroboratingThe Hacker News — Open VSX Removes 77 Malicious Evil Twin Extensions (2026-08-05)Independent confirmation of dates, counts, the shared exfil domain and the collected field set. Names the 19 recon extension IDs explicitly.Yes — corroboration for the 19 recon IDs
2 · CorroboratingBleepingComputer — 77 Open VSX extensions found harvesting developer info (2026-08-04)Confirms the four collector subdomains, the per-package tracking identifier, the 7-day retry, the TXT-record fallback, and that no source code, credentials, tokens, SSH material or browser data were accessed.Yes — negative scope (what is not stolen) shapes the severity call
2 · CorroboratingCyber Security News — 77 Evil Twin Open VSX Extensions Exfiltrate Private Git Repository and CI Data (2026-08-05)Carries a defanged IOC table: domain, four hosts, the three URI paths, the _beacon TXT mechanism with its base=https:// response prefix, and the User-Agent.Yes — second independent copy of the network IOC set
2 · CorroboratingGBHackers — Fake Open VSX Extensions Hijack AMD, Azure, Salesforce and Government NamespacesSame indicator table; adds the detail that collector subdomains answer "ok" to any request and that the code treats any HTTP response, including an error status, as delivery success.Yes — informs the "no payload returned" detection assumption
— · DroppedSC World briefReturned HTTP 403 to both the fetcher and the crawler; no text could be snapshotted.No — an uncaptured source cannot back an indicator, so it is not cited and contributes nothing

Web-hunter decisions

  • Prompt-injection screen: all five captured sources were scanned for agent-directed text (instructions to run commands, fetch further URLs "for the real IOCs", claims of authorization, or attempts to address the reader as an automated agent). None found. All fetched content was treated as data.
  • Second failover domain deliberately excluded. Manifold states a subset of samples carry a third endpoint on a second registered domain and explicitly declines to publish it. It is therefore not in this pack. Q3 and Q4 are the queries that cover it: they hunt the behaviour (a code editor resolving or contacting a rare external destination) rather than the name.
  • Randomised subdomain, one observed value. One sample used a randomised label under cb.mangorbit[.]com. Exactly one such host appears in the published data and it ships as an IOC; the general case is covered by the suffix regex in Q1 and by blocking the whole zone.
  • Payload class carried through. The A/B split is not cosmetic — payload B is the one that leaks private repository paths, so it drives severity, containment scope and which IDs go into the high-fidelity Q5 regex.
  • Legitimate publishers are victims here. The extension IDs below are the counterfeits' IDs, which reuse real projects' names and namespaces without those projects' knowledge. The genuine extensions are unaffected. Do not treat the vendor names in the ID strings as compromised parties.
03

Hunt Brief & Attack Chain

Attack chain

#StepTelemetryHunt angle
1Operator registers mangorbit[.]com (2026-07-15) with redacted registrant, three-year term to 2029-07-15; stands up pulse, pulse2, api and randomised cb collectors that answer any request with "ok".Passive DNS, WHOIS, newly-registered-domain feedsNewly-registered-domain enrichment on any destination first contacted by a developer endpoint
277 counterfeit packages published to Open VSX from accounts that do not own the namespace, at version 0.0.1–0.0.6, reusing real extension names, namespaces and descriptions.Registry/mirror download logs; internal Open VSX mirror auditMirror logs for the 77 IDs; any extension pulled at a suspiciously low version under a well-known namespace
3Install by name — a human choosing from a listing, or an agent / devcontainer.json / .vscode/extensions.json / provisioning script resolving the name with no publisher check.ProcessRollup2 (--install-extension), ZipFileWritten (VSIX), repo-config file writesQ5 CLI install of a counterfeit ID · Q6 VSIX written · Q8 repo manifests
4On editor activation the extension renders a status-bar checkmark and registers one "it is active" command. It provides no advertised functionality.None directly observable in EDRGAP — no endpoint telemetry distinguishes a no-op extension from a working one
5Payload A (58 samples) sends hostname, sometimes workspace folder name and editor version, plus a per-package tracking ID, by POST to /api/v1/metrics or /api/v1/events or by GET query string.DnsRequest, NetworkConnectIP4, proxy/SWG logsQ1 collector DNS · Q4 editor egress · Q9/Q10 UA and URI paths
6Payload B (19 samples) waits 4–5 s, then reads .git/config and .git/HEAD, enumerates CI environment values, lists up to 60 installed extension IDs, reads the proxy hostname and the telemetry opt-out flag, and ships it all in one request.FileOpenInfo on .git\config, DnsRequest/NetworkConnectIP4 from the extension hostQ7 editor reads .git\config · Q3 rare domain from an editor process
7Delivery resilience: multiple endpoints in one array, any HTTP response (including an error status) counted as success, retries at ~15 min, ~50 min, ~3.5 h then every 7–8 h, resuming on every editor restart, giving up only after 7 days.DnsRequest/NetworkConnectIP4 cadenceBeaconing-cadence shape on the surviving destinations from Q4
8Takedown contingency: if every endpoint fails, query the DNS TXT record at _beacon.<domain> and take a replacement base URL from a base=https:// prefixed response.DnsRequest (TXT)Q2 — the one query that survives the operator moving infrastructure

Hunt hypotheses (ordered by fidelity)

#HypothesisMITREFalcon eventsExpected FPConf
H1A developer or CI endpoint has resolved mangorbit[.]com or any subdomain of it.T1071.004, T1041DnsRequestThreat-intel tooling detonating samples; researcher workstationsHIGH
H2A VSIX matching one of the 77 published SHA-256 hashes exists on an endpoint or in a build image.T1195.002, T1554ZipFileWritten, ProcessRollup2Malware-analysis storageHIGH
H3One of the 19 recon extension IDs was installed from a command line or pinned in a repo config.T1176.002, T1204ProcessRollup2, file writesDeliberate research installsHIGH
H4An endpoint has issued a DNS TXT lookup for a _beacon.* label — the operator's post-takedown relocation channel.T1071.004, T1568DnsRequestVery low; no common tooling uses this labelMEDIUM
H5A code editor or its extension host resolves a rare external domain that few or no other hosts in the fleet resolve.T1176.002, T1071.001DnsRequestLanguage servers, registries, AI assistants, vendor telemetryMEDIUM
H6A code editor or extension-host process opens an external connection to a destination almost nothing else in the fleet talks to.T1041NetworkConnectIP4Same as H5, plus CDN churnMEDIUM
H7The editor or extension host reads .git\config / .git\HEAD and opens an external connection within seconds.T1005, T1083FileOpenInfo + NetworkConnectIP4High — the built-in Git extension and GitLens do this constantlyMEDIUM
H8Repository or container configs in the estate pin an extension ID from the counterfeit list, so every rebuild re-installs it.T1195.002File writes; source-control searchHigh as a standalone signal; low once cross-referenced with the ID listLOW
H9Outbound HTTP carries the User-Agent vscode-ext-metrics/1.0 or hits the collector URI paths.T1071.001Proxy / SWG logs in NG-SIEMLow for the UA; medium for the generic REST paths aloneHIGH
04

Consolidated IOC Table

Network & behavioural indicators

TypeValueConfActionContextExpiry
Domainmangorbit[.]comhighdetect / blockApex. Registered 2026-07-15, expires 2029-07-15, registrar redacts registrant. Apex serves a generic landing page. All 77 samples reference the zone.2027-02-08
Hostpulse.mangorbit[.]comhighdetect / blockPrimary collector. Answers "ok" to any request.2027-02-08
Hostpulse2.mangorbit[.]comhighdetect / blockSecond collector, paired with pulse in the endpoint array of most samples.2027-02-08
Hostapi.mangorbit[.]comhighdetect / blockUsed by one observed sample (wghats.vscode-nxunit-test-adapter).2027-02-08
Hostcb.mangorbit[.]comhighdetect / blockParent of randomised collector labels. Block the whole zone rather than enumerating children.2027-02-08
Hostd9j3drkeib4sa01kk82014tt97wxnndam.cb.mangorbit[.]comhighhuntThe single published randomised label, used by lego-education.ev3-micropython 0.0.2.2026-11-08
URI path/api/v1/metricsmediumhuntPOST collector path. Generic on its own — pair with the host or the User-Agent.2026-11-08
URI path/api/v1/eventsmediumhuntAlternate POST collector path.2026-11-08
URI path/t/<24-hex tracking id>mediumhuntPer-package tracking endpoint; the 24-hex value identifies which counterfeit extension is installed.2026-11-08
DNSTXT lookup for _beacon.<domain>highdetectTakedown contingency. Response is prefixed base=https:// and supplies a replacement collector base URL. Survives any domain block.2027-02-08
User-Agentvscode-ext-metrics/1.0highdetectDistinctive and not used by any legitimate editor component.2027-02-08
File nameextension.jslowenrichThe swapped file inside every counterfeit VSIX. Present in essentially every legitimate extension too — context only, never alert on it.2026-09-08
BehaviourExtension reads .git/config + .git/HEAD then egresses within ~5 s of activationhighhuntThe core recon-variant signature. Domain-independent; survives infrastructure rotation.n/a — behavioural
BehaviourBeacon retries at ~15 min / ~50 min / ~3.5 h, then every 7–8 h, for 7 days, resuming on every editor restarthighhuntCadence fingerprint for the recon variant. Use for beaconing analysis on surviving destinations.n/a — behavioural

All 77 counterfeit packages

Payload A = lightweight beacon (58, roughly 1.6–3.3 KB). Payload B = reconnaissance payload (19, roughly 10 KB, Git + CI collection). "First seen" is the date Manifold's systems archived the sample. Scroll the table sideways for the VSIX SHA-256.

#Extension ID (counterfeit)VerPayloadBeacon hostsFirst seenSHA-256 (VSIX)
1lego-education.ev3-micropython0.0.2A · lightd9j3drkeib4sa01kk82014tt97wxnndam.cb.mangorbit[.]com2026-07-26b8cd87d5e36f167f51ed6a6d2e824f76dc1b3792de9c10091e46f87ad07f5d5a
2better-ts-errors.better-ts-errors0.0.1A · lightpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-278763bda130fe4305c4d13c25584c981424dc47d3de3df46ae0ef05466b05526a
3groksrc.ruby0.0.1A · lightpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-27415cef17b6560643c0e4dddccd956ae6887c369d84c7f45be638a76dc4930dc1
4maptz.regionfolder0.0.1A · lightpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-277342f26ff97c0f8dc29edb1c5a5460bafcaacbc84356020a785628b36156ab6e
5mitsuhiko.insta0.0.1A · lightpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-275d62cfb190145bd21f930d4c21da01e8dc951e4112992790361e2fd4b09e66f6
6SBSnippets.pytorch-snippets0.0.1A · lightpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-27d51e331ae110eb37d453e78ac4cf3063d4606ebd2357d8ee4342fd8f06933e20
7slb235.vscode-coffeelint0.0.1A · lightpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-270ccd62eb6c1155c252248a98c8ff8e1a45a5cc721d6725511effb770037690d5
8amd.gaia-vscode0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-281b775ad3ca61addf5bf3e326951314d80ab526cbab0d745edb3e2bd85de2fcd9
9artsy.artsy-studio-extension-pack0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-28a3dbc0a35a781c61c34bf41e9d21eadbf2e77d18531236d1d9c1e622aaf418ef
10configcat.configcat-feature-flags0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-28941e9cf4fb7a1cdadb1a0e64fce3262426847f167ad255a3f724d77f2d0ee103
11iotaledger.iota-move0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-28fde634f337ab81b3d6bfa320047ff4fe450556fd90f44a30612d549b996edc86
12marketplace.visualstudio0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-289f8a69c193c3c4b83db18149408a9687d1cb6ca149ffa3a1ca6d72e95e3b6954
13obyte.oscript-vscode-plugin0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-287179f11710f242a578bd50187dadd7fa2ba0062e11e1a631edcd75417bda3f7e
14openeuphoria.vscode-euphoria0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-280e99e23329f3ca908d7a88ccf102e4b10bd9b67f24fc79408aae15d5d10dfd0a
15oss.sfmc-devtools-vscode0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-28fc3a32f59300c7e8b3e486f23146224beb658d10ae04098a9e27faf341356fbe
16rumbledb.jsoniq-vscode0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-28498b0c9632959eb37cc278f35561f0736c91884b370251dfebae07ff9a942bc3
17ssagov.uef-snippets0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-2895fbdc4c81e14a46dd8f3eb5c4207567baae99712e3ccf8644af09680f3caf5e
18taskfile.vscode-task0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-282526c160b6a08377d02e035fc166e91e8029b997fa6c91169640c2ad2c2ec60b
19doi.fileheadercomment0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-3015cd6a4909161c406b8428100d688999377e399ae13aa4da49bb73744933807d
20MengsiCode.vscode-django-boilerplate0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-30d35b690502fd3b73312aacf62dc700ac1e43bbc01b53d129096c2c0cdf92a1c4
21move.move-analyzer0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-303b407a548040927802f2cfa05b81eabf48f8b03785ba9fd69a3caa3981a7bc22
22uavcan.dsdl0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-30d19c076fc8213617f889a313a3da1565d8364e35c53290fa96400183127b1187
23vs-publisher-988541.apexsql-power-tools0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-309e148da1d338c32286b54a102ba3a42c7c2627855c3cc820122cff2ee4fc0d90
24365businessdevelopment.bdev-al-xml-doc0.0.1A · lightpulse.mangorbit[.]com2026-07-3103d81fce32d898e4791672e8e324ca8854d4667f56131f9c2b9d13d66faade4f
25AcademiaDosDevs.javafx0.0.1A · lightpulse.mangorbit[.]com2026-07-315a1fb5758a6f4b1635d1e1d3fd83c2c13376f466508a949cdad23114af82db45
26AlDuncanson.react-hooks-snippets0.0.1A · lightpulse.mangorbit[.]com2026-07-31508d3fb15d317846239e8d1f89ab5a5f31242d159f102602cfbf671103658bd3
27Alex-Chen.gitee-code-settings-sync0.0.1A · lightpulse2.mangorbit[.]com2026-07-317dc683fc3e0acf757cb48091c259bd71a7e3fe985f83d5b40468fa5b58e70804
28AndenetAlexander.vim-cheatsheet0.0.1A · lightpulse.mangorbit[.]com2026-07-313f97f02073caab6208d103693b13105f9dd42daca379e07953e433bd50ec5d3a
29AndreyVolosovich.monokai-st30.0.1A · lightpulse.mangorbit[.]com2026-07-313e9c96b913b54af9d50966b27b878750c3ae9d8e7944c4437a40d5a5cbc5ef21
30angelo-breuer.license-header-manager0.0.2A · lightpulse.mangorbit[.]com2026-07-31aac1efa84293929006436160f77e58e70d9025fc0b320ecc64953e066785a0fa
31AshhadDevLab.customtkinter-snippets0.0.1A · lightpulse.mangorbit[.]com2026-07-318e90e637bd7f3a2eaee077f216610248bf4d41c8d1f7880a2d2623be13be339f
32AzureADB2CTools.aadb2c0.0.1A · lightpulse2.mangorbit[.]com2026-07-31410e6b62b5e62cd8603b374f34e5ec6dad62bb95435fa320e08a850fa359f8c5
33AzurePolicy.azurepolicyextension0.0.1A · lightpulse.mangorbit[.]com2026-07-31805c0c50271afbe94301d376cc5eb191feb7a409ea05f7771dcb4a07dcb0442a
34BartmanAbyss.amiga-debug0.0.1A · lightpulse2.mangorbit[.]com2026-07-319afe7dd8f3eece28719edac1572ca7adbf307fdb29580535cbc109dec2e44da0
35BDZNH.c-cpp-compile-run-windows0.0.1A · lightpulse2.mangorbit[.]com2026-07-311a1211418b048678ff5a219d29587cb4beea251615bd8319b2a39caca0ac9cf9
36bradymholt.pgformatter0.0.1A · lightpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-3167d23c83dfe5199815aeb471bf468bb99faa6103e7238484e26e106194548642
37BretDoyle.javascript-extensions-pack---js-essentials0.0.1A · lightpulse2.mangorbit[.]com2026-07-31cf3593f41cb52c4b52386b539bc1b5095e6123de3d6bfda6f85f98b4570af6a4
38casualjim.gotemplate0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-31abcdee583b02c827fdd746acad814545f9e823d5c7859743d090e176e0f344f4
39drewbourne.vscode-remark-lint0.0.3A · lightpulse2.mangorbit[.]com2026-07-3148976edf057d4064db52c8ccd5d2b70769008556072cc411617dc6eeb28cbd53
40garaemon.vscode-emacs-tab0.0.3A · lightpulse.mangorbit[.]com2026-07-31573072c5107fb2fc749225caeee8ddabe7b1b6c9b45463a9e3247d4658870d33
41jakeboone02.cypher-query-language0.0.2A · lightpulse.mangorbit[.]com2026-07-31796d0536a0de16cd09f768b24d56763bdf8b36535c4ff946a754ff1f16857a65
42jcamp.dotnet-test-provider-view0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-313d734eb54a45f18ffa885f21240c95fccbd4a222368044ae01920176a3666d0e
43madhavd1.javadoc-tools0.0.2A · lightpulse2.mangorbit[.]com2026-07-31db253057b1d54c0d4bf2ea5d358e33043292c0e1780989a030f489a194d83cb5
44miclo.sort-typescript-imports0.0.3A · lightpulse.mangorbit[.]com2026-07-31e25513c12e71784067a36db09670c244eb2f22272a5cf4589cb46bc6a27b6fb7
45Mukundan.python-docs0.0.3A · lightpulse2.mangorbit[.]com2026-07-311a5b4ea5cb768aec7bbba28d881774a124aeb84ee8827d56aa0213ecf59a77a1
46npxms.hide-gitignored0.0.2A · lightpulse.mangorbit[.]com2026-07-3152a54dd4b72d7c113b22c54a3fe48131d064215f1987589b4ce5aac13573c96d
47qiu.llvm-ir-language-support0.0.6A · lightpulse.mangorbit[.]com2026-07-31b0e0615140a5aebf3ef3e99693e11f3fcc2c2c0c8a60e92fd4152a8e29c418a5
48superposition.supertoml-analyzer0.0.1B · reconpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-31b7655dd07d18bfb6f9b724647fd393ce2f312b2a874a046e2f568aa9c1eec32f
49tamuratak.vscode-lezer0.0.1A · lightpulse.mangorbit[.]com, pulse2.mangorbit[.]com2026-07-31a27ef5dc929616d46ca1bb3fc12e25195e42726db9135b5946e53036992aa3a5
50tgreen7.vs-code-node-require0.0.1A · lightpulse2.mangorbit[.]com2026-07-31fd8d100ee32bf3f23f9cf82a16c43a61c701380d443d246c34da5aa1d4352e84
51yardensachs.copy-python-path0.0.1A · lightpulse.mangorbit[.]com2026-07-3198bcafa86bc15ecb93ab47a4e5b7914932699d0ae38ff5f50020cb3d54f3b603
52zoxon.monokai-deep0.0.1A · lightpulse.mangorbit[.]com2026-07-31f6ae05bdf6ed9fd092d0c8976850980b7a03703f9412a8369a5d7ccd8412dba8
53bdaeumer.vscode-eslint0.0.1A · lightpulse.mangorbit[.]com2026-08-0118e952ea9848a7bff90d1fdde4b3d6b6648df30959ce6771840ca8cc67fbdfe6
54chavyleung.vscode-pnpm-verlens0.0.1A · lightpulse2.mangorbit[.]com2026-08-016682a6142cf38e877b2683269c413eab1580e36f8e40876febaf1bf72e12c0f8
55chris-hock.pioasm0.0.1A · lightpulse.mangorbit[.]com2026-08-01a4f0dd467b3abbdacd5ebb2929de3899c6b3d7b0b90da9ea4ae58e30d101db8f
56davidpallinder.rails-test-runner0.0.1A · lightpulse.mangorbit[.]com2026-08-010039e6d3a653d255841320db1dcd9664e1c9ae8b0a0d8a2e9f7e0ecb5fd2c0f6
57Flutterando.flutter-mobx0.0.1A · lightpulse.mangorbit[.]com2026-08-01280ef259644dfbd888ad3eb630aaf6f895449b5e97e075e459fc96c266fb8682
58Glavin001.unibeautify-vscode0.0.1A · lightpulse.mangorbit[.]com2026-08-015c41fa68ce0f879199d9b5646cf9b82ebde426e1ee0fa2ae10dd6447b0223c54
59GoBystrokReactJS.gobystrok0.0.1A · lightpulse2.mangorbit[.]com2026-08-0115cd8ee6f874539d915a9284adf17a3c2eb0b970c331725a8388f58efba48719
60helixquar.asciidecorator0.0.1A · lightpulse.mangorbit[.]com2026-08-01375216cb58f3700c181da52c62eccd4534b0d2aaa0d84df6a9f476f35a87217d
61HyperledgerComposer.composer-support-client0.0.1A · lightpulse.mangorbit[.]com2026-08-0158b7c5b92ed0756034af0de02c7addcab215ab612bd37c86bad11aafdfb25d62
62Insigne.powershell0.0.1A · lightpulse2.mangorbit[.]com2026-08-01041019527f4c13b1007b71d35d4383b13ee8c7bf3eaaff366abe90c6c9a804c3
63Jeremy38100.init-node-script0.0.1A · lightpulse.mangorbit[.]com2026-08-01b461c72229cefd5f2078db74565451dc656e71af3a1e95f13db70bf7dec94c70
64jt.jakt0.0.1A · lightpulse2.mangorbit[.]com2026-08-018af30a4e2c57034a64e924a893de50450dd668528f2723a94f907a8a758965ea
65kaellarkin.hugo-shortcode-syntax0.0.1A · lightpulse.mangorbit[.]com2026-08-01e7ba9bff28fa17d507753e0ca157d7add64cbfed593530504b153e24b6c982c9
66Levertion.mcjson0.0.1A · lightpulse.mangorbit[.]com2026-08-01f04b8c9af22fd8e20fd070806b1aa21424a0f6d5e2c770fa9fb1d131b08393fd
67magne-sjaastad.opm-flow-editor-support0.0.1A · lightpulse.mangorbit[.]com2026-08-01b490d56da1c5a4e26de8c07f907157b0e328c84464df38d2bd1cb92858906f83
68MarinhoBrandao.Angular2Tests0.0.1A · lightpulse2.mangorbit[.]com2026-08-016bbaee0ccfbbd45c7683efbf4688ad4532050f8a0ad6b39ea1b435ce2b6cf546
69microsoft-dciborow.align-bicep0.0.1A · lightpulse.mangorbit[.]com2026-08-01c5fe26dbc56a84ab672a28d3d3d767dd1496a56b027ecec7e3f8f25f83cd6a7d
70mkdirdocs.mkd-docs0.0.1A · lightpulse.mangorbit[.]com2026-08-010937bfa48a6b2ee53c978c1edea77a04db6ef407adef108b12fb6612e308b275
71nihilus118.perl-debugger0.0.1A · lightpulse2.mangorbit[.]com2026-08-01c578b03b8bd150ec9bf71326293f9f1abc2f1d451b3ca2ee05a516ec53866b1b
72pwrs.cem-language-server-vscode0.0.1A · lightpulse2.mangorbit[.]com2026-08-012364b4cf089da6d4d6c1fe854daca03047eea0849b325a3e9f28fac4584418d2
73wghats.vscode-nxunit-test-adapter0.0.1A · lightapi.mangorbit[.]com2026-08-01b301202210164188696b6a6fced62cf0c1641dca650ea4981f81b210ced9116f
74wordpresstools.wordpress0.0.1A · lightpulse.mangorbit[.]com2026-08-019ec8c53e41766b7b1dbc7fd9acf759b63f65353434231de57d22a6002fef7998
75xnerd.ampscript-language0.0.1A · lightpulse.mangorbit[.]com2026-08-0181402a5dc2901bc68ae7e866d78ad7c0ab1c77868d1cb026f95a68a0de851f5d
76ydaveluy.xsmp-modeler0.0.1A · lightpulse2.mangorbit[.]com2026-08-014b3dfffd11366d4171ba57efe53fc4fe4d4c39e9b8590c81365453005aed9b80
77ydaveluy.xsmp-tas-mdk0.0.1A · lightpulse2.mangorbit[.]com2026-08-01b9f36f1e416a3b6c650cebcad3dc046c0a0164442d0a2c3ed35279179f457c24

The publisher namespaces above (AMD, Artsy, LEGO Education, Hyperledger, Azure, IOTA, Salesforce OSS, the ssagov .gov namespace, and marketplace.visualstudio which impersonates the marketplace itself) were squatted. Those organisations were not involved and the genuine extensions are unaffected.

05

Affected Surface & Telemetry Matrix

SurfaceWhy it is in scopeRequired telemetryPriorityGap risk
Developer workstations — WindowsVS Code, Insiders, VSCodium, Cursor, Windsurf all read Open VSX or a mirror of it. Extensions live under %USERPROFILE%\.vscode\extensions and %USERPROFILE%\.cursor\extensions.DnsRequest, NetworkConnectIP4, ProcessRollup2, ZipFileWrittenCRITICALLow — standard Falcon sensor coverage
Developer workstations — macOS / LinuxSame editors, extensions under ~/.vscode/extensions, ~/.vscode-oss/extensions, ~/.cursor/extensions.DnsRequest, NetworkConnectIP4, ProcessRollup2CRITICALMedium — file-write event coverage on non-PE files is thinner than on Windows
Remote / server-side editor sessions~/.vscode-server/extensions on SSH and WSL targets; the extension host runs on the server, so the beacon originates there, not from the laptop.DnsRequest, NetworkConnectIP4 on the server aidHIGHHigh — remote dev hosts are frequently outside standard endpoint coverage
CI/CD build runnersThis is where the damage is: GITHUB_REPOSITORY, CI_PROJECT_PATH, Azure DevOps collection URI, Buildkite slug, CircleCI username all resolve to the private repository path.Endpoint sensor on the runner, or runner egress logs; container image inventoryCRITICALHigh — ephemeral runners often carry no sensor at all
Devcontainers / Codespaces / Gitpoddevcontainer.json installs extensions by name on every container build, with no publisher verification. Codespace name and Gitpod workspace context URL are both collected fields.Image build logs, container runtime telemetry, egress logsHIGHHigh — container-internal process telemetry is commonly absent
Golden images / VDI templatesAn extension baked into a template silently re-provisions on every new machine, long after the registry takedown.Image inventory; code --list-extensions at build timeHIGHMedium — needs a build-time check, not a runtime one
Internal Open VSX mirrorA mirror that syncs by name will have cached the counterfeits and can keep serving them after the upstream takedown.Mirror download / sync logsHIGHMedium — mirror logging varies by product
Web proxy / secure web gatewayOnly place the User-Agent and URI path are visible when TLS is inspected.Proxy or SWG logs forwarded to NG-SIEMMEDIUMHigh — parser field names are tenant-specific and TLS inspection may not cover dev subnets
Recursive DNS resolversCatches endpoints with no sensor, including runners and containers, and is the only reliable place to see the _beacon TXT lookups from unmanaged hosts.Resolver query logs (with record type)HIGHMedium — many estates log resolver queries without the record type
06

ATT&CK Mapping

TacticTechniqueObserved behaviourQuery / control
Initial AccessT1195.002 — Supply Chain Compromise: Compromise Software Supply Chain77 counterfeit packages published to Open VSX reusing real extension names and namespaces from accounts that do not own them.Q5, Q6, Q11 · H-2, H-4
PersistenceT1176.002 — Software Extensions: IDE ExtensionsThe extension is the persistence. It loads and beacons on every editor start, and a repo-config pin re-installs it on every container rebuild.Q5, Q6, Q8 · H-2, H-7
Defense EvasionT1036.005 — Masquerading: Match Legitimate Name or LocationName, namespace and description copied verbatim from a real VS Code Marketplace extension; version pinned low (0.0.1) to look like an early release.Q5, Q8 · H-4, H-7
Defense EvasionT1554 — Compromise Host Software BinaryThe legitimate bundled extension.js is swapped for the beacon while the rest of the package looks normal.Q6, Q11
ExecutionT1204 — User ExecutionInstall by name — a developer choosing from a listing, or an agent / devcontainer / provisioning script resolving the name unattended.Q5, Q8 · H-4, H-7
DiscoveryT1082 — System Information DiscoveryHostname, machine ID, editor name/version/host kind, platform, architecture, locale, timezone.Q3, Q4, Q7
DiscoveryT1033 — System Owner/User DiscoveryOS username; domain portion of the configured Git commit email.Q7
DiscoveryT1083 — File and Directory DiscoveryWorkspace folder name and full filesystem path; presence of the extension's own ID in devcontainer.json / .vscode/extensions.json.Q7, Q8
DiscoveryT1518 — Software DiscoveryEnumerates up to 60 installed extension IDs.Q7 (behavioural) · Partial
DiscoveryT1016 — System Network Configuration DiscoveryReads the proxy hostname from the environment.GAP — no distinct endpoint signal
CollectionT1005 — Data from Local SystemReads .git/config (origin/upstream remote host + org, commit email domain) and .git/HEAD (branch, HEAD commit SHA); reads CI identity variables.Q7 · H-9
CollectionT1119 — Automated CollectionWhole collection routine fires automatically 4–5 s after editor activation, with no user interaction.Q3, Q4
Command & ControlT1071.001 — Application Layer Protocol: Web ProtocolsHTTPS POST/GET to /api/v1/metrics, /api/v1/events, /t/<24-hex> with UA vscode-ext-metrics/1.0.Q1, Q4, Q9, Q10
Command & ControlT1071.004 — Application Layer Protocol: DNSTXT lookup at _beacon.<domain> returning a base=https:// replacement collector URL.Q2 · H-1
Command & ControlT1568 — Dynamic ResolutionRandomised subdomain labels under cb.mangorbit[.]com; endpoint array with failover across multiple hosts and a second registered domain.Q1, Q3
Command & ControlT1008 — Fallback ChannelsMultiple hardcoded endpoints, any HTTP status treated as success, 7-day retry ladder resuming on each editor restart.Q4 (cadence) · Partial
ExfiltrationT1041 — Exfiltration Over C2 ChannelThe single assembled request carrying host, user, workspace, Git and CI identity.Q1, Q4, Q9, Q10
07

Native Audit-Log Hunts (non-CQL)

These run outside Falcon and answer the questions endpoint telemetry cannot. Run N1 and N2 first — they are the fastest route to "were we hit", and they work on hosts with no sensor.

#SourceWhat to run / look forWhy it matters
N1Recursive DNS resolver logsAny query for a name ending mangorbit.com, over the full window 2026-07-26 to now. Then any TXT query for a name beginning _beacon., with no domain restriction.Covers unmanaged hosts, ephemeral runners and containers that carry no sensor. The _beacon query is the only signal that survives the operator relocating collectors.
N2Endpoint extension inventorycode --list-extensions --show-versions (and the codium, cursor, windsurf, code-insiders equivalents) across the fleet, diffed against the 77 IDs in §4. Playbook P1 wraps this."What is installed" is a different question from "what the config requests" — Manifold's core recommendation. Removal from the registry did not remove anything from a machine.
N3Source-control search across all repositoriesSearch every repo for .vscode/extensions.json, .devcontainer/devcontainer.json and .devcontainer.json, then grep those files for the 77 counterfeit IDs.A pinned ID re-installs the extension on every container build and every new joiner's clone. This is the difference between a one-time cleanup and a recurring one.
N4Internal Open VSX mirror / artifact proxy logsDownload and sync records for any of the 77 extension IDs, and for any package pulled at version 0.0.1 under a namespace you consider well-known.A name-syncing mirror can keep serving a counterfeit after the upstream takedown. Also gives you the exact list of machines that pulled it.
N5Web proxy / SWG logsUser-Agent containing vscode-ext-metrics; any request to a host under mangorbit.com; requests to /api/v1/metrics or /api/v1/events paired with either of the first two.The only place the UA and URI path are visible. Q9 and Q10 are the NG-SIEM versions of the same hunt.
N6Newly-registered-domain / passive DNS enrichmentFor every external destination first contacted by an editor process in the last 30 days, check domain age. Flag anything under 30 days old.Catches the undisclosed second failover domain, and the next campaign. mangorbit.com was eleven days old when the first package shipped.
N7CI/CD platform audit logsOn any project whose runner is confirmed or suspected exposed, review subsequent access patterns and treat the private repository path as public knowledge from the exposure date forward.No credentials were taken, so there is nothing to rotate — the loss is knowledge of your private repo paths, which fuels targeted phishing and follow-on reconnaissance.
N8Git config on affected hostsRecord which remotes, branches and commit-email domains were readable at the time of exposure, so the scope note in the incident record is factual rather than assumed.Payload B only reads .git/config and .git/HEAD, strips credentials from remote URLs, and keeps only the email domain — scope the notification accordingly instead of over-reporting.
08

CrowdStrike LogScale CQL Hunt Queries

Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.

None of these queries carry an in-query time filter. Set the window with the console time picker; each card records the intended lookback in a // LOOKBACK: comment. A bare @timestamp comparison against now() is rejected by Falcon on line 1 and would silently kill the whole query.

Q1 · Collector domain resolved on an endpoint
CONF HIGHFP LOWCOST LOW

Looks for: any DNS resolution of mangorbit.com or any subdomain of it, including the randomised cb labels. This is the "were we hit" query — run it first, over the widest window your retention allows. FP: essentially none outside a malware-analysis host.

// HUNT: Open VSX "Evil Twin" collector domain resolved on an endpoint
// MITRE: T1071.004, T1041 | CONF: high  FP: low  COST: low
// REQUIRES: DnsRequest
// LOOKBACK: 30d or max retention -- set the window in the console time picker
// FALSE POSITIVES: sandbox / malware-analysis workstations detonating the VSIX samples
// TUNING: if a research host is a known hit, exclude by aid rather than by ComputerName
#event_simpleName=/^DnsRequest$/
| DomainName=/(^|\.)mangorbit\.com$/i
| table([@timestamp, aid, ComputerName, UserName, DomainName, ContextBaseFileName, ContextProcessId])
| sort(@timestamp, order=desc, limit=2000)
Q2 · DNS TXT takedown-contingency lookup (_beacon.*)
CONF MEDFP LOWCOST LOW

Looks for: lookups of any _beacon.-prefixed label. This is the channel the operator uses to publish a replacement collector base URL after a takedown, so it is the one query here that keeps working when mangorbit.com stops resolving. FP: low — no mainstream tooling uses this label (ACME uses _acme-challenge, DKIM uses _domainkey), so treat any hit as high priority.

// HUNT: DNS TXT takedown-contingency lookup (_beacon.<domain>) for a replacement collector
// MITRE: T1071.004, T1568 | CONF: medium  FP: low  COST: low
// REQUIRES: DnsRequest -- record type is optional but sharpens the result
// LOOKBACK: 30d or max retention
// FALSE POSITIVES: underscore-prefixed service labels are common, but the _beacon label itself is not
// TUNING: keep the ^_beacon\. anchor. If your tenant populates RequestType, add | RequestType = 16
//   to keep TXT only. Any surviving hit is worth a same-day pivot to Q4 on that aid.
#event_simpleName=/^DnsRequest$/
| DomainName=/^_beacon\./i
| table([@timestamp, aid, ComputerName, UserName, DomainName, RequestType, ContextBaseFileName, ContextProcessId])
| sort(@timestamp, order=desc, limit=1000)
Q3 · Rare external domain resolved by a code editor
CONF MEDFP MEDCOST MED

Looks for: domains resolved by an editor or extension-host process that almost nothing else in the fleet resolves. This is the query that catches the undisclosed second failover domain and the next campaign, because it never names an indicator. FP: medium — language servers, package registries, AI assistants and vendor telemetry all resolve long-tail names from the editor process; the exclusion regex and the host-count ceiling are what make it readable.

// HUNT: Rare external domain resolved by a code editor or its extension host
// MITRE: T1176.002, T1071.001 | CONF: medium  FP: medium  COST: medium
// REQUIRES: DnsRequest with ContextBaseFileName populated
// LOOKBACK: 7d (widen to 30d once the exclusion list is tuned)
// FALSE POSITIVES: language servers, registries, AI coding assistants, update and telemetry endpoints
// TUNING: extend the exclusion regex with your own approved dev estate -- internal registries, your
//   artifact proxy, your SSO domain -- then raise DistinctHosts until the result set is reviewable.
//   Enrich survivors with domain age; anything under 30 days old goes straight to Q4 on that aid.
#event_simpleName=/^DnsRequest$/
| ContextBaseFileName=/^(code|code-insiders|codium|vscodium|cursor|windsurf|code-oss|node|electron)(\.exe)?$/i
| !DomainName=/\.(microsoft|windows|visualstudio|github|githubusercontent|githubassets|npmjs|pypi|crates|nuget|maven|jetbrains|google|gstatic|apple|cloudflare|akamai|azureedge|openai|anthropic)\.(com|net|org|io|dev|ai)$/i
| groupBy([DomainName], function=[count(as=Lookups), count(aid, distinct=true, as=DistinctHosts), collect(ComputerName, limit=10), collect(ContextBaseFileName, limit=5), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)], limit=20000)
| DistinctHosts <= 3
| Lookups > 1
| sort(DistinctHosts, order=asc, limit=500)
Q4 · Editor / extension-host external egress to a rare destination
CONF MEDFP MEDCOST MED

Looks for: the IP-layer half of Q3 — editor and extension-host processes opening web-port connections to external addresses that few hosts in the fleet touch. Use it when DNS context is missing (DoH, a resolver you do not log, or a hardcoded IP). FP: medium — CDN address churn makes a single IP look rare even for a common service, so always resolve survivors back to a name before judging.

// HUNT: Editor / extension-host process egressing to a rare external destination
// MITRE: T1041, T1071.001 | CONF: medium  FP: medium  COST: medium
// REQUIRES: NetworkConnectIP4 with ContextBaseFileName populated
// LOOKBACK: 7d
// FALSE POSITIVES: marketplace and update traffic, language servers, AI assistants; CDN churn makes
//   individual edge IPs look rare even when the service behind them is ubiquitous
// TUNING: add !cidr() lines for your egress proxy, your VPN concentrators and your CDN provider ranges.
//   Resolve every surviving RemoteAddressIP4 to a name before triage, then feed it back into Q1/Q3.
#event_simpleName=/^NetworkConnectIP4$/
| ContextBaseFileName=/^(code|code-insiders|codium|vscodium|cursor|windsurf|code-oss|node|electron)(\.exe)?$/i
| !cidr(RemoteAddressIP4, subnet="10.0.0.0/8")
| !cidr(RemoteAddressIP4, subnet="172.16.0.0/12")
| !cidr(RemoteAddressIP4, subnet="192.168.0.0/16")
| !cidr(RemoteAddressIP4, subnet="127.0.0.0/8")
| !cidr(RemoteAddressIP4, subnet="169.254.0.0/16")
| !cidr(RemoteAddressIP4, subnet="100.64.0.0/10")
| in(RemotePort_decimal, values=[80, 443, 8443])
| groupBy([RemoteAddressIP4, ContextBaseFileName], function=[count(as=Conns), count(aid, distinct=true, as=DistinctHosts), collect(ComputerName, limit=10), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)], limit=20000)
| DistinctHosts <= 5
| sort(DistinctHosts, order=asc, limit=500)
Q5 · Command-line install of a counterfeit extension ID
CONF HIGHFP LOWCOST LOW

Looks for: --install-extension invocations naming one of the 19 reconnaissance-payload IDs — the ones that leak Git and CI identity. This is how provisioning scripts, Dockerfiles and devcontainer post-create hooks install extensions, so it is the highest-fidelity way to find automated re-provisioning. FP: low — a match is either a real install or a deliberate research install.

// HUNT: Command-line install of a counterfeit Open VSX extension ID (payload B / recon set)
// MITRE: T1176.002, T1195.002, T1204 | CONF: high  FP: low  COST: low
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// LOOKBACK: 30d or max retention
// NOTE: this regex carries the 19 reconnaissance IDs. The full 77 are in the IOC appendix; use the
//   lookup-file form in Q11 rather than pasting 77 alternatives into a regex.
// FALSE POSITIVES: an analyst deliberately reproducing the sample
// TUNING: drop the --install-extension line to also catch marketplace-UI installs recorded elsewhere
//   in the command line, at the cost of some noise from log-shipping agents echoing the ID
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| CommandLine=/--install-extension/i
| CommandLine=/(amd\.gaia-vscode|artsy\.artsy-studio-extension-pack|configcat\.configcat-feature-flags|iotaledger\.iota-move|marketplace\.visualstudio|obyte\.oscript-vscode-plugin|openeuphoria\.vscode-euphoria|oss\.sfmc-devtools-vscode|rumbledb\.jsoniq-vscode|ssagov\.uef-snippets|taskfile\.vscode-task|doi\.fileheadercomment|mengsicode\.vscode-django-boilerplate|move\.move-analyzer|uavcan\.dsdl|vs-publisher-988541\.apexsql-power-tools|casualjim\.gotemplate|jcamp\.dotnet-test-provider-view|superposition\.supertoml-analyzer)/i
| table([@timestamp, aid, ComputerName, UserName, FileName, ParentBaseFileName, CommandLine])
| sort(@timestamp, order=desc, limit=1000)
Q6 · VSIX package written to disk
CONF MEDFP MEDCOST MED

Looks for: VSIX archives landing on disk, with the hash projected so you can diff against the 77 published SHA-256 values. A VSIX is a ZIP, so ZipFileWritten is the event that sees it. FP: medium — every legitimate extension install and update writes here too, which is why this is an inventory query first and a detection only once joined to the hash list or to Q1.

// HUNT: VSIX extension package written to disk (inventory + hash surface)
// MITRE: T1176.002, T1554 | CONF: medium  FP: medium  COST: medium
// REQUIRES: ZipFileWritten (a VSIX is a ZIP archive)
// LOOKBACK: 30d
// FALSE POSITIVES: every legitimate extension install and auto-update writes a VSIX
// TUNING: this is an inventory sweep, not an alert. Scope it to CI/build images and golden images
//   first, then diff SHA256HashData against the 77 published hashes -- or just run Q11, which does
//   the diff server-side via a lookup file.
#event_simpleName=/^ZipFileWritten$/
| TargetFileName=/\.vsix$/i
| table([@timestamp, aid, ComputerName, UserName, TargetFileName, SHA256HashData, ContextBaseFileName])
| sort(@timestamp, order=desc, limit=2000)
Q7 · Editor / extension host reading .git config and HEAD
CONF MEDFP HIGHCOST HIGH

Looks for: the recon variant's collection step — the editor or extension host opening .git\config and .git\HEAD. On its own this is ordinary editor behaviour; its value is as a confirmation query on a host already surfaced by Q1, Q3, Q4 or Q11. FP: high — the built-in Git extension, GitLens and most source-control providers read these files continuously. Never schedule this as an alert.

// HUNT: Code editor or extension host reading .git\config / .git\HEAD (recon-variant collection)
// MITRE: T1005, T1083, T1033 | CONF: medium  FP: high  COST: high
// REQUIRES: FileOpenInfo -- ⚠ validate in tenant: file-read telemetry is off by default in many
//   sensor policies, and enabling it fleet-wide is expensive. Confirm before relying on this card.
// LOOKBACK: 24h, scoped to specific hosts
// FALSE POSITIVES: the built-in Git extension, GitLens, source-control providers and language
//   servers all read .git\config constantly -- this is normal, expected editor behaviour
// TUNING: run this ONLY scoped to an aid already surfaced by Q1/Q3/Q4/Q11 -- add a top-level
//   aid filter before the event filter. Then correlate on ContextProcessId with an external
//   connection from the same process id inside a 10-second window; that pairing is the signal,
//   the file read alone is not.
#event_simpleName=/^FileOpenInfo$/
| ContextBaseFileName=/^(code|code-insiders|codium|vscodium|cursor|windsurf|code-oss|node|electron)(\.exe)?$/i
| TargetFileName=/[\\\/]\.git[\\\/](config|HEAD)$/i
| table([@timestamp, aid, ComputerName, UserName, TargetFileName, ContextBaseFileName, ContextProcessId])
| sort(@timestamp, order=desc, limit=2000)
Q8 · Repository extension manifests written or modified
CONF LOWFP HIGHCOST MED

Looks for: .vscode\extensions.json and devcontainer.json being written on endpoints and build hosts — the files that cause an extension to be re-installed by name on every clone and every container rebuild. FP: high — normal repository work touches these constantly. Its job is to give you a file list to grep against the 77 IDs, and native hunt N3 (a source-control-wide search) is the better primary. Use this to catch the copies that only exist inside images.

// HUNT: Repository extension manifests written (.vscode\extensions.json, devcontainer.json)
// MITRE: T1176.002, T1195.002, T1036.005 | CONF: low  FP: high  COST: medium
// REQUIRES: CriticalFileModified / FileOpenInfo -- ⚠ validate in tenant: non-PE file event
//   coverage is policy-dependent and varies between Windows, macOS and Linux sensors
// LOOKBACK: 30d
// FALSE POSITIVES: ordinary repository work, container rebuilds, IaC templating and repo scaffolding
//   all write these files -- expect a large, benign result set
// TUNING: treat this as an inventory sweep, not an alert. Scope to CI/build hosts and golden-image
//   builders first, then grep the resulting paths for the 77 counterfeit IDs in the IOC appendix.
//   For repositories, use native hunt N3 instead -- a source-control search sees every branch.
#event_simpleName=/^(CriticalFileModified|FileOpenInfo)$/
| TargetFileName=/(extensions\.json|devcontainer\.json)$/i
| table([@timestamp, aid, ComputerName, UserName, TargetFileName, ContextBaseFileName])
| sort(@timestamp, order=desc, limit=2000)
Q9 · Beacon User-Agent in proxy / SWG telemetry
CONF HIGHFP LOWCOST LOW

Looks for: the User-Agent vscode-ext-metrics/1.0. No legitimate editor component uses it, so a single hit is actionable — and it identifies the beacon even after the operator moves to a new domain. FP: low. The pattern matches only the product token, not the version, so a bumped version still hits.

// HUNT: Beacon User-Agent vscode-ext-metrics in web-proxy / secure-web-gateway telemetry
// MITRE: T1071.001, T1041 | CONF: high  FP: low  COST: low
// REQUIRES: third-party proxy / SWG logs forwarded to NG-SIEM
// ⚠ validate in tenant: parser field names differ by vendor -- http_user_agent, user_agent,
//   cs_user_agent, useragent. Confirm against one sample row and adjust before scheduling.
// LOOKBACK: 30d or max retention
// FALSE POSITIVES: none known -- the token is not used by any shipping editor component
// TUNING: match the product token only, not the /1.0 version, so a bumped version still fires
http_user_agent=/vscode-ext-metrics/i
| table([@timestamp, src_ip, user, http_user_agent, url])
| sort(@timestamp, order=desc, limit=1000)
Q10 · Collector URI paths in proxy / SWG telemetry
CONF MEDFP MEDCOST MED

Looks for: the three collector paths — /api/v1/metrics, /api/v1/events, and the per-package tracking path /t/ followed by a 24-character hex identifier. The tracking path is the distinctive one; it also tells you which counterfeit extension is installed. FP: medium — the two /api/v1/ paths are generic REST endpoints used by plenty of legitimate SaaS.

// HUNT: Collector URI paths -- /api/v1/metrics, /api/v1/events, /t/<24-hex tracking id>
// MITRE: T1041, T1071.001 | CONF: medium  FP: medium  COST: medium
// REQUIRES: proxy / SWG URL logging forwarded to NG-SIEM
// ⚠ validate in tenant: the URL field may be url, uri, uri_path, cs_uri_stem or request
// LOOKBACK: 30d
// FALSE POSITIVES: /api/v1/metrics and /api/v1/events are generic REST paths shipped by many
//   legitimate products -- on their own they are not evidence of anything
// TUNING: alert only on the /t/ 24-hex branch, or pair the /api/v1 branch with the mangorbit host
//   filter or the Q9 User-Agent. Add | url=/mangorbit/i as a second stage to make it high-fidelity.
url=/\/api\/v1\/(metrics|events)$|\/t\/[0-9a-f]{24}$/i
| table([@timestamp, src_ip, user, http_user_agent, url])
| sort(@timestamp, order=desc, limit=1000)
Q11 · Known counterfeit VSIX hash sweep (all 77)
CONF HIGHFP LOWCOST MED

Looks for: any of the 77 published VSIX SHA-256 values on an endpoint. Uses a lookup file so all 77 hashes stay out of the query text and the enrichment columns come back with the hit — you learn the extension ID and the payload class in the same row. Upload the "VSIX SHA-256 lookup" block from §10 as openvsx_eviltwin_vsix.csv before running. FP: low.

// HUNT: Known counterfeit VSIX SHA-256 present on an endpoint (all 77 packages)
// MITRE: T1195.002, T1554 | CONF: high  FP: low  COST: medium
// REQUIRES: a lookup file uploaded at Falcon -> NG-SIEM -> Lookup files. Copy the "VSIX SHA-256
//   lookup" block from the IOC appendix and save it as openvsx_eviltwin_vsix.csv. Lookup files are
//   per search head -- upload separately in each region if you run multi-region tenants.
// LOOKBACK: 30d or max retention
// FALSE POSITIVES: a malware-analysis host holding the samples deliberately
// TUNING: if a research host is a known holder, exclude it by aid rather than removing hashes
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2|ZipFileWritten|PeFileWritten)$/
| match(file="openvsx_eviltwin_vsix.csv", field=SHA256HashData, column=sha256, include=[extension_id, payload], strict=true)
| table([@timestamp, aid, ComputerName, UserName, SHA256HashData, extension_id, payload, TargetFileName, FileName])
| sort(@timestamp, order=desc, limit=1000)
09

Custom IOA & Scheduled-Search Alert Package

What belongs where

SignalRight mechanismWhy
Collector domain and hostsFalcon IOC Management — domain IOCs, action preventDomain blocking belongs in IOC Management, not in a Custom IOA. Blocking the zone covers all 77 samples in one entry.
77 VSIX SHA-256 valuesFalcon IOC Management — hash IOCs, action preventAtomic, published, and stable. The CSV in §10 imports directly.
Counterfeit extension installed via CLICustom IOA (process-creation rule, below)Command-line pattern is deterministic and cheap to evaluate on the sensor.
Q1, Q2, Q9, Q11Scheduled Search with email notificationLow FP, high confidence — safe to alert on.
Q3, Q4, Q6, Q7, Q8, Q10Investigate-only hunt queriesFP medium or high. Run on a cadence and review; do not page on them.

IOA-1 — Counterfeit Open VSX extension install

FieldValue
Rule groupDeveloper Supply Chain
Rule typeProcess Creation
PlatformWindows, macOS, Linux (create one per platform)
ActionDetect — severity High. Do not set Block: killing an editor mid-install is disruptive and the install has already happened by the time the process exits.
Image filename pattern.*(code|code-insiders|codium|cursor|windsurf)(\.exe)?
Command line pattern.*--install-extension\s+(amd\.gaia-vscode|iotaledger\.iota-move|move\.move-analyzer|ssagov\.uef-snippets|oss\.sfmc-devtools-vscode|vs-publisher-988541\.apexsql-power-tools|marketplace\.visualstudio|configcat\.configcat-feature-flags|taskfile\.vscode-task|doi\.fileheadercomment|uavcan\.dsdl|casualjim\.gotemplate|jcamp\.dotnet-test-provider-view|superposition\.supertoml-analyzer|obyte\.oscript-vscode-plugin|openeuphoria\.vscode-euphoria|rumbledb\.jsoniq-vscode|artsy\.artsy-studio-extension-pack|mengsicode\.vscode-django-boilerplate).*
ExclusionsExclude the aid of any sanctioned malware-analysis workstation. Do not exclude build agents — those are the highest-value hits.
Deployment pathEndpoint security → Custom IOA rule groups → new group → Process Creation rule → assign the group to your developer and build-agent prevention policies.
ValidationOn a test host, run the editor CLI with --install-extension and a harmless ID that you have temporarily added to the pattern. Confirm the IOA fires, then revert the pattern.

Scheduled Search — SS-1

FieldValue
NameDETECT — Open VSX Evil Twin collector contact (mangorbit)
QueryQ1
ScheduleEvery 1 hour, 1-hour window (plus one historical run over max retention when you first deploy)
DescriptionFires when any managed endpoint resolves the Open VSX "Evil Twin" campaign collector zone. All 77 counterfeit extensions reference this zone, so a hit means a counterfeit extension is installed and active on that host.
NotificationEmail to the SOC queue; severity High

Alert email — SS-1

Subject: [HIGH] Detection: Open VSX "Evil Twin" extension beacon — developer host contacted mangorbit

WHAT FIRED
  Host {{ComputerName}} (aid {{aid}}), user {{UserName}}, resolved {{DomainName}} at {{@timestamp}}.
  Resolving process: {{ContextBaseFileName}} (pid {{ContextProcessId}}).

WHAT IT MEANS
  mangorbit[.]com is the collector zone for a campaign of 77 counterfeit VS Code-compatible
  extensions published to the Open VSX registry between 2026-07-26 and 2026-08-01. A DNS
  resolution from a developer endpoint means one of those extensions is installed and running
  on that machine. The packages were pulled from Open VSX on 2026-08-03; removal from the
  registry does not remove the code from a machine or an image.

  58 of the 77 send only host-level data. The other 19 send Git remote host and organisation,
  the commit-email domain, the current branch, the HEAD commit SHA, and CI identity values --
  on a build runner or cloud dev environment that is the full private repository path.
  No source code, credentials, tokens, SSH material or browser data are taken.

IMMEDIATE ACTIONS
  1. Confirm the zone is blocked at DNS and egress, and that the Falcon domain IOC is set to prevent.
  2. On the host, list installed editor extensions and compare against the 77 IDs (playbook P1).
  3. Determine payload class: if the installed ID is one of the 19 recon IDs, treat repository
     and CI identity for that host's workspaces as disclosed.
  4. Check whether the install came from a repo config (devcontainer.json / .vscode/extensions.json)
     -- if so, the same extension is on every machine and every container built from that repo.
  5. Remove the extension, then verify the beacon stops (re-run Q1 scoped to the aid after a restart).

ESCALATION
  Escalate to the incident channel if the host is a CI/build runner, a shared golden image, or a
  cloud development environment -- those expose private repository paths rather than just a hostname.

Triage checklist — L1

  1. Record the aid, ComputerName, UserName and the exact collector host from the alert. Note whether it is pulse, pulse2, api or a randomised cb label.
  2. Classify the host: developer workstation, remote dev server, CI/build runner, container, or golden-image builder. This single fact sets the severity.
  3. Run playbook P1 on the host to list installed extensions, and match against the 77 IDs in §4.
  4. Look the matched ID up in the payload column. B means Git and CI identity left the machine; A means hostname-level data only.
  5. Run Q1 scoped to that aid over full retention to establish the first-contact date — that is the start of the exposure window.
  6. Search the host's repositories and any devcontainer/provisioning config for the matched ID (native hunt N3). A pin means this recurs on rebuild.
  7. Run Q3 and Q4 on the same aid to check for contact with the undisclosed second failover domain.
  8. Remove the extension, restart the editor, and confirm no further Q1 hits for that aid over the next 24 hours.

Pivot queries

  • Host-scoped: add a top-level aid filter to Q3, Q4 and Q7 to reconstruct everything the extension host did on that machine.
  • Environment-wide: run Q11 across max retention to find every other host holding one of the 77 VSIX files, including ones that never got to beacon.
  • Infrastructure-wide: run Q2 with no host filter — a _beacon TXT hit anywhere means the operator has already moved, and Q1 is now blind.
10

Machine-Readable IOC Appendix

Falcon IOC Management CSVbulk import
type,value,action,severity,expiration,description,tags
domain,mangorbit.com,prevent,high,2027-02-08,Open VSX Evil Twin collector zone,campaign:OpenVSX-EvilTwin
domain,pulse.mangorbit.com,prevent,high,2027-02-08,Open VSX Evil Twin primary collector,campaign:OpenVSX-EvilTwin
domain,pulse2.mangorbit.com,prevent,high,2027-02-08,Open VSX Evil Twin secondary collector,campaign:OpenVSX-EvilTwin
domain,api.mangorbit.com,prevent,high,2027-02-08,Open VSX Evil Twin collector (single sample),campaign:OpenVSX-EvilTwin
domain,cb.mangorbit.com,prevent,high,2027-02-08,Open VSX Evil Twin randomised-label collector parent,campaign:OpenVSX-EvilTwin
domain,d9j3drkeib4sa01kk82014tt97wxnndam.cb.mangorbit.com,prevent,high,2027-02-08,Observed randomised collector label,campaign:OpenVSX-EvilTwin
sha256,b8cd87d5e36f167f51ed6a6d2e824f76dc1b3792de9c10091e46f87ad07f5d5a,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX lego-education.ev3-micropython lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,8763bda130fe4305c4d13c25584c981424dc47d3de3df46ae0ef05466b05526a,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX better-ts-errors.better-ts-errors lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,415cef17b6560643c0e4dddccd956ae6887c369d84c7f45be638a76dc4930dc1,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX groksrc.ruby lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,7342f26ff97c0f8dc29edb1c5a5460bafcaacbc84356020a785628b36156ab6e,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX maptz.regionfolder lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,5d62cfb190145bd21f930d4c21da01e8dc951e4112992790361e2fd4b09e66f6,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX mitsuhiko.insta lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,d51e331ae110eb37d453e78ac4cf3063d4606ebd2357d8ee4342fd8f06933e20,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX SBSnippets.pytorch-snippets lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,0ccd62eb6c1155c252248a98c8ff8e1a45a5cc721d6725511effb770037690d5,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX slb235.vscode-coffeelint lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,1b775ad3ca61addf5bf3e326951314d80ab526cbab0d745edb3e2bd85de2fcd9,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX amd.gaia-vscode recon payload B,campaign:OpenVSX-EvilTwin
sha256,a3dbc0a35a781c61c34bf41e9d21eadbf2e77d18531236d1d9c1e622aaf418ef,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX artsy.artsy-studio-extension-pack recon payload B,campaign:OpenVSX-EvilTwin
sha256,941e9cf4fb7a1cdadb1a0e64fce3262426847f167ad255a3f724d77f2d0ee103,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX configcat.configcat-feature-flags recon payload B,campaign:OpenVSX-EvilTwin
sha256,fde634f337ab81b3d6bfa320047ff4fe450556fd90f44a30612d549b996edc86,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX iotaledger.iota-move recon payload B,campaign:OpenVSX-EvilTwin
sha256,9f8a69c193c3c4b83db18149408a9687d1cb6ca149ffa3a1ca6d72e95e3b6954,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX marketplace.visualstudio recon payload B,campaign:OpenVSX-EvilTwin
sha256,7179f11710f242a578bd50187dadd7fa2ba0062e11e1a631edcd75417bda3f7e,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX obyte.oscript-vscode-plugin recon payload B,campaign:OpenVSX-EvilTwin
sha256,0e99e23329f3ca908d7a88ccf102e4b10bd9b67f24fc79408aae15d5d10dfd0a,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX openeuphoria.vscode-euphoria recon payload B,campaign:OpenVSX-EvilTwin
sha256,fc3a32f59300c7e8b3e486f23146224beb658d10ae04098a9e27faf341356fbe,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX oss.sfmc-devtools-vscode recon payload B,campaign:OpenVSX-EvilTwin
sha256,498b0c9632959eb37cc278f35561f0736c91884b370251dfebae07ff9a942bc3,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX rumbledb.jsoniq-vscode recon payload B,campaign:OpenVSX-EvilTwin
sha256,95fbdc4c81e14a46dd8f3eb5c4207567baae99712e3ccf8644af09680f3caf5e,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX ssagov.uef-snippets recon payload B,campaign:OpenVSX-EvilTwin
sha256,2526c160b6a08377d02e035fc166e91e8029b997fa6c91169640c2ad2c2ec60b,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX taskfile.vscode-task recon payload B,campaign:OpenVSX-EvilTwin
sha256,15cd6a4909161c406b8428100d688999377e399ae13aa4da49bb73744933807d,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX doi.fileheadercomment recon payload B,campaign:OpenVSX-EvilTwin
sha256,d35b690502fd3b73312aacf62dc700ac1e43bbc01b53d129096c2c0cdf92a1c4,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX MengsiCode.vscode-django-boilerplate recon payload B,campaign:OpenVSX-EvilTwin
sha256,3b407a548040927802f2cfa05b81eabf48f8b03785ba9fd69a3caa3981a7bc22,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX move.move-analyzer recon payload B,campaign:OpenVSX-EvilTwin
sha256,d19c076fc8213617f889a313a3da1565d8364e35c53290fa96400183127b1187,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX uavcan.dsdl recon payload B,campaign:OpenVSX-EvilTwin
sha256,9e148da1d338c32286b54a102ba3a42c7c2627855c3cc820122cff2ee4fc0d90,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX vs-publisher-988541.apexsql-power-tools recon payload B,campaign:OpenVSX-EvilTwin
sha256,03d81fce32d898e4791672e8e324ca8854d4667f56131f9c2b9d13d66faade4f,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX 365businessdevelopment.bdev-al-xml-doc lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,5a1fb5758a6f4b1635d1e1d3fd83c2c13376f466508a949cdad23114af82db45,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX AcademiaDosDevs.javafx lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,508d3fb15d317846239e8d1f89ab5a5f31242d159f102602cfbf671103658bd3,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX AlDuncanson.react-hooks-snippets lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,7dc683fc3e0acf757cb48091c259bd71a7e3fe985f83d5b40468fa5b58e70804,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX Alex-Chen.gitee-code-settings-sync lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,3f97f02073caab6208d103693b13105f9dd42daca379e07953e433bd50ec5d3a,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX AndenetAlexander.vim-cheatsheet lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,3e9c96b913b54af9d50966b27b878750c3ae9d8e7944c4437a40d5a5cbc5ef21,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX AndreyVolosovich.monokai-st3 lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,aac1efa84293929006436160f77e58e70d9025fc0b320ecc64953e066785a0fa,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX angelo-breuer.license-header-manager lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,8e90e637bd7f3a2eaee077f216610248bf4d41c8d1f7880a2d2623be13be339f,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX AshhadDevLab.customtkinter-snippets lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,410e6b62b5e62cd8603b374f34e5ec6dad62bb95435fa320e08a850fa359f8c5,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX AzureADB2CTools.aadb2c lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,805c0c50271afbe94301d376cc5eb191feb7a409ea05f7771dcb4a07dcb0442a,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX AzurePolicy.azurepolicyextension lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,9afe7dd8f3eece28719edac1572ca7adbf307fdb29580535cbc109dec2e44da0,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX BartmanAbyss.amiga-debug lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,1a1211418b048678ff5a219d29587cb4beea251615bd8319b2a39caca0ac9cf9,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX BDZNH.c-cpp-compile-run-windows lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,67d23c83dfe5199815aeb471bf468bb99faa6103e7238484e26e106194548642,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX bradymholt.pgformatter lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,cf3593f41cb52c4b52386b539bc1b5095e6123de3d6bfda6f85f98b4570af6a4,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX BretDoyle.javascript-extensions-pack---js-essentials lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,abcdee583b02c827fdd746acad814545f9e823d5c7859743d090e176e0f344f4,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX casualjim.gotemplate recon payload B,campaign:OpenVSX-EvilTwin
sha256,48976edf057d4064db52c8ccd5d2b70769008556072cc411617dc6eeb28cbd53,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX drewbourne.vscode-remark-lint lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,573072c5107fb2fc749225caeee8ddabe7b1b6c9b45463a9e3247d4658870d33,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX garaemon.vscode-emacs-tab lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,796d0536a0de16cd09f768b24d56763bdf8b36535c4ff946a754ff1f16857a65,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX jakeboone02.cypher-query-language lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,3d734eb54a45f18ffa885f21240c95fccbd4a222368044ae01920176a3666d0e,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX jcamp.dotnet-test-provider-view recon payload B,campaign:OpenVSX-EvilTwin
sha256,db253057b1d54c0d4bf2ea5d358e33043292c0e1780989a030f489a194d83cb5,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX madhavd1.javadoc-tools lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,e25513c12e71784067a36db09670c244eb2f22272a5cf4589cb46bc6a27b6fb7,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX miclo.sort-typescript-imports lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,1a5b4ea5cb768aec7bbba28d881774a124aeb84ee8827d56aa0213ecf59a77a1,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX Mukundan.python-docs lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,52a54dd4b72d7c113b22c54a3fe48131d064215f1987589b4ce5aac13573c96d,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX npxms.hide-gitignored lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,b0e0615140a5aebf3ef3e99693e11f3fcc2c2c0c8a60e92fd4152a8e29c418a5,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX qiu.llvm-ir-language-support lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,b7655dd07d18bfb6f9b724647fd393ce2f312b2a874a046e2f568aa9c1eec32f,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX superposition.supertoml-analyzer recon payload B,campaign:OpenVSX-EvilTwin
sha256,a27ef5dc929616d46ca1bb3fc12e25195e42726db9135b5946e53036992aa3a5,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX tamuratak.vscode-lezer lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,fd8d100ee32bf3f23f9cf82a16c43a61c701380d443d246c34da5aa1d4352e84,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX tgreen7.vs-code-node-require lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,98bcafa86bc15ecb93ab47a4e5b7914932699d0ae38ff5f50020cb3d54f3b603,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX yardensachs.copy-python-path lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,f6ae05bdf6ed9fd092d0c8976850980b7a03703f9412a8369a5d7ccd8412dba8,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX zoxon.monokai-deep lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,18e952ea9848a7bff90d1fdde4b3d6b6648df30959ce6771840ca8cc67fbdfe6,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX bdaeumer.vscode-eslint lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,6682a6142cf38e877b2683269c413eab1580e36f8e40876febaf1bf72e12c0f8,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX chavyleung.vscode-pnpm-verlens lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,a4f0dd467b3abbdacd5ebb2929de3899c6b3d7b0b90da9ea4ae58e30d101db8f,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX chris-hock.pioasm lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,0039e6d3a653d255841320db1dcd9664e1c9ae8b0a0d8a2e9f7e0ecb5fd2c0f6,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX davidpallinder.rails-test-runner lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,280ef259644dfbd888ad3eb630aaf6f895449b5e97e075e459fc96c266fb8682,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX Flutterando.flutter-mobx lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,5c41fa68ce0f879199d9b5646cf9b82ebde426e1ee0fa2ae10dd6447b0223c54,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX Glavin001.unibeautify-vscode lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,15cd8ee6f874539d915a9284adf17a3c2eb0b970c331725a8388f58efba48719,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX GoBystrokReactJS.gobystrok lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,375216cb58f3700c181da52c62eccd4534b0d2aaa0d84df6a9f476f35a87217d,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX helixquar.asciidecorator lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,58b7c5b92ed0756034af0de02c7addcab215ab612bd37c86bad11aafdfb25d62,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX HyperledgerComposer.composer-support-client lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,041019527f4c13b1007b71d35d4383b13ee8c7bf3eaaff366abe90c6c9a804c3,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX Insigne.powershell lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,b461c72229cefd5f2078db74565451dc656e71af3a1e95f13db70bf7dec94c70,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX Jeremy38100.init-node-script lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,8af30a4e2c57034a64e924a893de50450dd668528f2723a94f907a8a758965ea,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX jt.jakt lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,e7ba9bff28fa17d507753e0ca157d7add64cbfed593530504b153e24b6c982c9,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX kaellarkin.hugo-shortcode-syntax lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,f04b8c9af22fd8e20fd070806b1aa21424a0f6d5e2c770fa9fb1d131b08393fd,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX Levertion.mcjson lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,b490d56da1c5a4e26de8c07f907157b0e328c84464df38d2bd1cb92858906f83,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX magne-sjaastad.opm-flow-editor-support lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,6bbaee0ccfbbd45c7683efbf4688ad4532050f8a0ad6b39ea1b435ce2b6cf546,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX MarinhoBrandao.Angular2Tests lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,c5fe26dbc56a84ab672a28d3d3d767dd1496a56b027ecec7e3f8f25f83cd6a7d,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX microsoft-dciborow.align-bicep lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,0937bfa48a6b2ee53c978c1edea77a04db6ef407adef108b12fb6612e308b275,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX mkdirdocs.mkd-docs lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,c578b03b8bd150ec9bf71326293f9f1abc2f1d451b3ca2ee05a516ec53866b1b,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX nihilus118.perl-debugger lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,2364b4cf089da6d4d6c1fe854daca03047eea0849b325a3e9f28fac4584418d2,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX pwrs.cem-language-server-vscode lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,b301202210164188696b6a6fced62cf0c1641dca650ea4981f81b210ced9116f,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX wghats.vscode-nxunit-test-adapter lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,9ec8c53e41766b7b1dbc7fd9acf759b63f65353434231de57d22a6002fef7998,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX wordpresstools.wordpress lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,81402a5dc2901bc68ae7e866d78ad7c0ab1c77868d1cb026f95a68a0de851f5d,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX xnerd.ampscript-language lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,4b3dfffd11366d4171ba57efe53fc4fe4d4c39e9b8590c81365453005aed9b80,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX ydaveluy.xsmp-modeler lightweight payload A,campaign:OpenVSX-EvilTwin
sha256,b9f36f1e416a3b6c650cebcad3dc046c0a0164442d0a2c3ed35279179f457c24,prevent,high,2027-02-08,OpenVSX EvilTwin counterfeit VSIX ydaveluy.xsmp-tas-mdk lightweight payload A,campaign:OpenVSX-EvilTwin
VSIX SHA-256 lookupupload for Q11
sha256,extension_id,payload
b8cd87d5e36f167f51ed6a6d2e824f76dc1b3792de9c10091e46f87ad07f5d5a,lego-education.ev3-micropython,A
8763bda130fe4305c4d13c25584c981424dc47d3de3df46ae0ef05466b05526a,better-ts-errors.better-ts-errors,A
415cef17b6560643c0e4dddccd956ae6887c369d84c7f45be638a76dc4930dc1,groksrc.ruby,A
7342f26ff97c0f8dc29edb1c5a5460bafcaacbc84356020a785628b36156ab6e,maptz.regionfolder,A
5d62cfb190145bd21f930d4c21da01e8dc951e4112992790361e2fd4b09e66f6,mitsuhiko.insta,A
d51e331ae110eb37d453e78ac4cf3063d4606ebd2357d8ee4342fd8f06933e20,SBSnippets.pytorch-snippets,A
0ccd62eb6c1155c252248a98c8ff8e1a45a5cc721d6725511effb770037690d5,slb235.vscode-coffeelint,A
1b775ad3ca61addf5bf3e326951314d80ab526cbab0d745edb3e2bd85de2fcd9,amd.gaia-vscode,B
a3dbc0a35a781c61c34bf41e9d21eadbf2e77d18531236d1d9c1e622aaf418ef,artsy.artsy-studio-extension-pack,B
941e9cf4fb7a1cdadb1a0e64fce3262426847f167ad255a3f724d77f2d0ee103,configcat.configcat-feature-flags,B
fde634f337ab81b3d6bfa320047ff4fe450556fd90f44a30612d549b996edc86,iotaledger.iota-move,B
9f8a69c193c3c4b83db18149408a9687d1cb6ca149ffa3a1ca6d72e95e3b6954,marketplace.visualstudio,B
7179f11710f242a578bd50187dadd7fa2ba0062e11e1a631edcd75417bda3f7e,obyte.oscript-vscode-plugin,B
0e99e23329f3ca908d7a88ccf102e4b10bd9b67f24fc79408aae15d5d10dfd0a,openeuphoria.vscode-euphoria,B
fc3a32f59300c7e8b3e486f23146224beb658d10ae04098a9e27faf341356fbe,oss.sfmc-devtools-vscode,B
498b0c9632959eb37cc278f35561f0736c91884b370251dfebae07ff9a942bc3,rumbledb.jsoniq-vscode,B
95fbdc4c81e14a46dd8f3eb5c4207567baae99712e3ccf8644af09680f3caf5e,ssagov.uef-snippets,B
2526c160b6a08377d02e035fc166e91e8029b997fa6c91169640c2ad2c2ec60b,taskfile.vscode-task,B
15cd6a4909161c406b8428100d688999377e399ae13aa4da49bb73744933807d,doi.fileheadercomment,B
d35b690502fd3b73312aacf62dc700ac1e43bbc01b53d129096c2c0cdf92a1c4,MengsiCode.vscode-django-boilerplate,B
3b407a548040927802f2cfa05b81eabf48f8b03785ba9fd69a3caa3981a7bc22,move.move-analyzer,B
d19c076fc8213617f889a313a3da1565d8364e35c53290fa96400183127b1187,uavcan.dsdl,B
9e148da1d338c32286b54a102ba3a42c7c2627855c3cc820122cff2ee4fc0d90,vs-publisher-988541.apexsql-power-tools,B
03d81fce32d898e4791672e8e324ca8854d4667f56131f9c2b9d13d66faade4f,365businessdevelopment.bdev-al-xml-doc,A
5a1fb5758a6f4b1635d1e1d3fd83c2c13376f466508a949cdad23114af82db45,AcademiaDosDevs.javafx,A
508d3fb15d317846239e8d1f89ab5a5f31242d159f102602cfbf671103658bd3,AlDuncanson.react-hooks-snippets,A
7dc683fc3e0acf757cb48091c259bd71a7e3fe985f83d5b40468fa5b58e70804,Alex-Chen.gitee-code-settings-sync,A
3f97f02073caab6208d103693b13105f9dd42daca379e07953e433bd50ec5d3a,AndenetAlexander.vim-cheatsheet,A
3e9c96b913b54af9d50966b27b878750c3ae9d8e7944c4437a40d5a5cbc5ef21,AndreyVolosovich.monokai-st3,A
aac1efa84293929006436160f77e58e70d9025fc0b320ecc64953e066785a0fa,angelo-breuer.license-header-manager,A
8e90e637bd7f3a2eaee077f216610248bf4d41c8d1f7880a2d2623be13be339f,AshhadDevLab.customtkinter-snippets,A
410e6b62b5e62cd8603b374f34e5ec6dad62bb95435fa320e08a850fa359f8c5,AzureADB2CTools.aadb2c,A
805c0c50271afbe94301d376cc5eb191feb7a409ea05f7771dcb4a07dcb0442a,AzurePolicy.azurepolicyextension,A
9afe7dd8f3eece28719edac1572ca7adbf307fdb29580535cbc109dec2e44da0,BartmanAbyss.amiga-debug,A
1a1211418b048678ff5a219d29587cb4beea251615bd8319b2a39caca0ac9cf9,BDZNH.c-cpp-compile-run-windows,A
67d23c83dfe5199815aeb471bf468bb99faa6103e7238484e26e106194548642,bradymholt.pgformatter,A
cf3593f41cb52c4b52386b539bc1b5095e6123de3d6bfda6f85f98b4570af6a4,BretDoyle.javascript-extensions-pack---js-essentials,A
abcdee583b02c827fdd746acad814545f9e823d5c7859743d090e176e0f344f4,casualjim.gotemplate,B
48976edf057d4064db52c8ccd5d2b70769008556072cc411617dc6eeb28cbd53,drewbourne.vscode-remark-lint,A
573072c5107fb2fc749225caeee8ddabe7b1b6c9b45463a9e3247d4658870d33,garaemon.vscode-emacs-tab,A
796d0536a0de16cd09f768b24d56763bdf8b36535c4ff946a754ff1f16857a65,jakeboone02.cypher-query-language,A
3d734eb54a45f18ffa885f21240c95fccbd4a222368044ae01920176a3666d0e,jcamp.dotnet-test-provider-view,B
db253057b1d54c0d4bf2ea5d358e33043292c0e1780989a030f489a194d83cb5,madhavd1.javadoc-tools,A
e25513c12e71784067a36db09670c244eb2f22272a5cf4589cb46bc6a27b6fb7,miclo.sort-typescript-imports,A
1a5b4ea5cb768aec7bbba28d881774a124aeb84ee8827d56aa0213ecf59a77a1,Mukundan.python-docs,A
52a54dd4b72d7c113b22c54a3fe48131d064215f1987589b4ce5aac13573c96d,npxms.hide-gitignored,A
b0e0615140a5aebf3ef3e99693e11f3fcc2c2c0c8a60e92fd4152a8e29c418a5,qiu.llvm-ir-language-support,A
b7655dd07d18bfb6f9b724647fd393ce2f312b2a874a046e2f568aa9c1eec32f,superposition.supertoml-analyzer,B
a27ef5dc929616d46ca1bb3fc12e25195e42726db9135b5946e53036992aa3a5,tamuratak.vscode-lezer,A
fd8d100ee32bf3f23f9cf82a16c43a61c701380d443d246c34da5aa1d4352e84,tgreen7.vs-code-node-require,A
98bcafa86bc15ecb93ab47a4e5b7914932699d0ae38ff5f50020cb3d54f3b603,yardensachs.copy-python-path,A
f6ae05bdf6ed9fd092d0c8976850980b7a03703f9412a8369a5d7ccd8412dba8,zoxon.monokai-deep,A
18e952ea9848a7bff90d1fdde4b3d6b6648df30959ce6771840ca8cc67fbdfe6,bdaeumer.vscode-eslint,A
6682a6142cf38e877b2683269c413eab1580e36f8e40876febaf1bf72e12c0f8,chavyleung.vscode-pnpm-verlens,A
a4f0dd467b3abbdacd5ebb2929de3899c6b3d7b0b90da9ea4ae58e30d101db8f,chris-hock.pioasm,A
0039e6d3a653d255841320db1dcd9664e1c9ae8b0a0d8a2e9f7e0ecb5fd2c0f6,davidpallinder.rails-test-runner,A
280ef259644dfbd888ad3eb630aaf6f895449b5e97e075e459fc96c266fb8682,Flutterando.flutter-mobx,A
5c41fa68ce0f879199d9b5646cf9b82ebde426e1ee0fa2ae10dd6447b0223c54,Glavin001.unibeautify-vscode,A
15cd8ee6f874539d915a9284adf17a3c2eb0b970c331725a8388f58efba48719,GoBystrokReactJS.gobystrok,A
375216cb58f3700c181da52c62eccd4534b0d2aaa0d84df6a9f476f35a87217d,helixquar.asciidecorator,A
58b7c5b92ed0756034af0de02c7addcab215ab612bd37c86bad11aafdfb25d62,HyperledgerComposer.composer-support-client,A
041019527f4c13b1007b71d35d4383b13ee8c7bf3eaaff366abe90c6c9a804c3,Insigne.powershell,A
b461c72229cefd5f2078db74565451dc656e71af3a1e95f13db70bf7dec94c70,Jeremy38100.init-node-script,A
8af30a4e2c57034a64e924a893de50450dd668528f2723a94f907a8a758965ea,jt.jakt,A
e7ba9bff28fa17d507753e0ca157d7add64cbfed593530504b153e24b6c982c9,kaellarkin.hugo-shortcode-syntax,A
f04b8c9af22fd8e20fd070806b1aa21424a0f6d5e2c770fa9fb1d131b08393fd,Levertion.mcjson,A
b490d56da1c5a4e26de8c07f907157b0e328c84464df38d2bd1cb92858906f83,magne-sjaastad.opm-flow-editor-support,A
6bbaee0ccfbbd45c7683efbf4688ad4532050f8a0ad6b39ea1b435ce2b6cf546,MarinhoBrandao.Angular2Tests,A
c5fe26dbc56a84ab672a28d3d3d767dd1496a56b027ecec7e3f8f25f83cd6a7d,microsoft-dciborow.align-bicep,A
0937bfa48a6b2ee53c978c1edea77a04db6ef407adef108b12fb6612e308b275,mkdirdocs.mkd-docs,A
c578b03b8bd150ec9bf71326293f9f1abc2f1d451b3ca2ee05a516ec53866b1b,nihilus118.perl-debugger,A
2364b4cf089da6d4d6c1fe854daca03047eea0849b325a3e9f28fac4584418d2,pwrs.cem-language-server-vscode,A
b301202210164188696b6a6fced62cf0c1641dca650ea4981f81b210ced9116f,wghats.vscode-nxunit-test-adapter,A
9ec8c53e41766b7b1dbc7fd9acf759b63f65353434231de57d22a6002fef7998,wordpresstools.wordpress,A
81402a5dc2901bc68ae7e866d78ad7c0ab1c77868d1cb026f95a68a0de851f5d,xnerd.ampscript-language,A
4b3dfffd11366d4171ba57efe53fc4fe4d4c39e9b8590c81365453005aed9b80,ydaveluy.xsmp-modeler,A
b9f36f1e416a3b6c650cebcad3dc046c0a0164442d0a2c3ed35279179f457c24,ydaveluy.xsmp-tas-mdk,A
Behavioural signaturessurvives infra rotation
# Behavioural signals -- no atomic indicator required.
# These keep working after the operator moves collectors over the _beacon TXT channel.

1. Editor or extension-host process resolves a domain registered within the last 30 days,
   within seconds of editor activation.                          -> Q3 + domain-age enrichment
2. DNS TXT lookup for any label beginning "_beacon."             -> Q2
3. Extension host reads .git/config and .git/HEAD, then opens an
   external connection from the same process id inside 10 s.     -> Q7 correlated with Q4
4. Outbound HTTP User-Agent "vscode-ext-metrics" (any version).  -> Q9
5. Request path /t/ followed by exactly 24 hex characters.       -> Q10
6. Beacon cadence: retries at ~15 min, ~50 min, ~3.5 h, then
   every 7-8 h, resuming on editor restart, ceasing after 7 days.
7. Extension provides no advertised feature: status-bar checkmark
   plus a single "extension is active" command, and nothing else.
8. Editor extension installed at version 0.0.1 under a namespace
   belonging to a well-known vendor.                             -> Q5, Q8, N3, N4
Counterfeit extension IDs — recon setthe 19 that leak Git + CI
amd.gaia-vscode
artsy.artsy-studio-extension-pack
casualjim.gotemplate
configcat.configcat-feature-flags
doi.fileheadercomment
iotaledger.iota-move
jcamp.dotnet-test-provider-view
marketplace.visualstudio
MengsiCode.vscode-django-boilerplate
move.move-analyzer
obyte.oscript-vscode-plugin
openeuphoria.vscode-euphoria
oss.sfmc-devtools-vscode
rumbledb.jsoniq-vscode
ssagov.uef-snippets
superposition.supertoml-analyzer
taskfile.vscode-task
uavcan.dsdl
vs-publisher-988541.apexsql-power-tools
Fleet extension-inventory auditrun on every dev host + image
# POSIX -- list what is ACTUALLY installed, for every editor variant present.
for cli in code code-insiders codium code-oss cursor windsurf; do
  command -v "$cli" >/dev/null 2>&1 && \
    "$cli" --list-extensions --show-versions 2>/dev/null | sed "s|^|$cli |"
done | sort -u

# Also enumerate the extension directories directly -- catches editors whose CLI
# is not on PATH, and copies baked into images by a Dockerfile COPY.
ls -1 ~/.vscode/extensions ~/.vscode-server/extensions ~/.vscode-oss/extensions \
      ~/.cursor/extensions ~/.windsurf/extensions 2>/dev/null | sort -u

# Repository / image config pins -- these re-install on every rebuild.
grep -rIl --include=extensions.json --include=devcontainer.json \
  -e "recommendations" -e "customizations" . 2>/dev/null
Egress / DNS block listfirewall, SWG, resolver
# Block the whole zone. Manifold confirms all 77 samples reference it, and the
# randomised cb.* labels make host-by-host blocking pointless.
mangorbit.com
*.mangorbit.com

# Also alert (do not silently drop) on outbound DNS TXT queries for any _beacon.* label.
# That is the operator's post-takedown relocation channel; a silent drop hides the pivot.
_beacon.*

# Sinkhole verification after deployment:
#   Windows : Resolve-DnsName pulse.mangorbit.com
#   POSIX   : dig +short pulse.mangorbit.com
# Expect NXDOMAIN or your sinkhole address. A real answer means the block is not applied.

Every atomic indicator above is traced to a saved source snapshot in OpenVSX-EvilTwin-Extensions-Hunt-sources/. The second failover domain referenced by a subset of samples is deliberately absent — Manifold declined to publish it, so this pack cannot cite it. Q3 and Q4 cover it behaviourally.

11

Detection Validation Gates

GateCheckPass criteria
G1 — Telemetry readyConfirm DnsRequest and NetworkConnectIP4 return rows with ContextBaseFileName populated for at least one known developer host in the last 24 h.Both events present with a non-empty process context. If ContextBaseFileName is blank, Q3 and Q4 silently return nothing.
G2 — Editor coverageRun Q3 with the domain-exclusion line removed and confirm each editor variant your estate actually uses appears in the results.Every editor in use appears at least once. If cursor or windsurf never appears, its process name differs in your build — fix the regex before trusting the query.
G3 — File telemetry reality checkConfirm FileOpenInfo and CriticalFileModified actually return rows in your tenant.Rows returned. If not, mark Q7 and Q8 as unavailable in your coverage record rather than leaving them looking deployed. This is the most common silent failure in this pack.
G4 — Proxy parser field namesPull one sample proxy row and confirm the User-Agent and URL field names match Q9 and Q10.Field names confirmed and the queries edited to match. Both cards carry a ⚠ validate in tenant note precisely because this varies by vendor.
G5 — Benign baselineRun Q3 and Q4 over 7 days on a normal population and record the volume before tuning.A reviewable result set after the exclusion list is extended. If either returns more than roughly 100 rows a day, keep tuning; do not schedule it.
G6 — Positive test, networkFrom an isolated test host, issue a DNS lookup for a sinkholed name in the zone and a TXT lookup for a _beacon.-prefixed label under a domain you control.Q1 and Q2 both fire within the expected ingest delay. Never execute a real sample to test — a DNS lookup is sufficient and safe.
G7 — Positive test, installOn a test host, temporarily add a harmless extension ID to the Q5 regex and install it via the editor CLI.Q5 returns the row; the IOA-1 rule fires. Revert the pattern afterwards.
G8 — Lookup file loadedRun Q11 with strict=false against any event stream and confirm the enrichment columns appear.extension_id and payload are populated on matched rows. If the columns are empty, the lookup file did not load or the column headers differ.
G9 — PromotionOnly Q1, Q2, Q9 and Q11 are eligible for a Scheduled Search with email notification.Q3, Q4, Q6, Q7, Q8 and Q10 remain investigate-only until their FP rating is measured at low in your own environment.
G10 — Block verificationAfter deploying the DNS/egress block and the Falcon domain IOC, resolve a collector host from a normal developer subnet.NXDOMAIN or sinkhole. A real answer means the block did not reach that subnet — dev VLANs are a frequent gap.
12

Hardening — Tiered

Immediate · deploy this week · no compatibility risk

H-1 · Block the collector zone at DNS, egress and in Falcon

MITRE M1037 (Filter Network Traffic) · M1031 (Network Intrusion Prevention). Block mangorbit.com and *.mangorbit.com at the recursive resolver, the egress firewall and the SWG, and import the domain rows from §10 into Falcon IOC Management with action prevent. Alert rather than silently drop on _beacon.* TXT queries — a silent drop hides the operator's relocation attempt.

Verify: Resolve-DnsName pulse.mangorbit.com (Windows) or dig +short pulse.mangorbit.com (POSIX) from a developer subnet returns NXDOMAIN or your sinkhole. CQL spot-check: Q1 returns no new rows after the block date.

H-2 · Inventory what is installed, not what is requested

MITRE M1033 (Limit Software Installation) · CIS Controls v8 §2.1 (software inventory). Run playbook P1 across every developer workstation, remote dev host, CI runner image and golden image; diff against the 77 IDs in §4. This is Manifold's headline recommendation and it is the only step that finds copies the registry takedown left behind.

Verify: an inventory record exists for every host in the developer and build estate, with a date after 2026-08-08 and a recorded diff result.

H-3 · Purge repository and container config pins

MITRE M1051 (Update Software) · M1045 (Code Signing) as the long-term control. Search every repository for .vscode/extensions.json, .devcontainer/devcontainer.json, .devcontainer.json, Dockerfiles and provisioning scripts referencing any of the 77 IDs, and remove them. A surviving pin re-installs the extension on every clone and every container build, indefinitely.

Verify: a source-control-wide search for the 19 recon IDs returns zero results on the default branch of every repository, and zero in image build contexts.

Near term · 1–4 weeks · pilot on a ring first

H-4 · Enforce an extension allowlist in the editor itself

MITRE M1033 (Limit Software Installation) · vendor guidance: VS Code's extensions.allowed policy setting. Publish an allowlist keyed by publisher.extension — optionally pinned to specific versions — and deploy it via Group Policy, Intune, Jamf or a managed settings.json. This turns "install by name" from an unchecked operation into an approved one, which is the actual root cause here. Playbook P2 has the configuration.

Verify: on a piloted host, attempt to install an extension outside the allowlist and confirm the editor refuses. ⚠ best practice / vendor setting — no CIS benchmark covers editor extension policy yet.

H-5 · Pin the internal mirror by publisher and version

MITRE M1051 · M1016 (Vulnerability Scanning). If you mirror Open VSX internally, pin every entry by publisher and version rather than syncing by name, and treat the Open VSX unverified-publisher banner as a blocking condition in automated installs rather than a cosmetic notice. A name-syncing mirror will have cached these counterfeits and can keep serving them after the upstream takedown.

Verify: mirror configuration shows explicit publisher and version pins; a test sync of a namespace with an unverified publisher is rejected.

H-6 · Deny-by-default egress from CI runners and devcontainers

MITRE M1037 (Filter Network Traffic) · M1030 (Network Segmentation) · CIS Controls v8 §13.4, §4.4. Build runners and devcontainers should reach your registries, your artifact proxy and your VCS — and nothing else. This is the control that would have contained the actual damage here, because the runner is where GITHUB_REPOSITORY and CI_PROJECT_PATH resolve to a private repository path.

Verify: from inside a build container, an outbound HTTPS request to an arbitrary external host fails. Re-run after every runner image change.

H-7 · Stop exporting repository-identifying CI variables to untrusted steps

MITRE M1018 (User Account Management) · M1022 (Restrict File and Directory Permissions). Scope the environment visible to third-party tooling — including editor extensions running in a devcontainer — so that repository-path variables and cloud workspace identifiers are only present in the steps that genuinely need them. Prefer short-lived OIDC tokens over long-lived variables generally, and treat the repository path itself as sensitive metadata on private projects.

Verify: print the environment in a representative untrusted step and confirm no repository-path variable is present. ⚠ best practice, no formal benchmark for this specific control.

Strategic · 1–3 months · architectural

H-8 · Runtime behavioural monitoring of extensions and agents

MITRE M1040 (Behavior Prevention on Endpoint). Static scanning cannot see this campaign: the code is clean, legible, commented and honest about most of what it does. The only reliable signal is runtime behaviour — what an extension reads and where it connects, in the environment where it runs. Establish per-extension behavioural baselines for the editor estate and alert on deviation. Q3, Q4 and Q7 are the interim version of this control.

Verify: a documented behavioural baseline exists for the top 20 extensions in use, and a deviation alert has been tested end to end.

H-9 · Treat the developer toolchain as a first-class supply chain

MITRE M1051 · M1016 · NIST SP 800-161r1 (C-SCRM) · SLSA provenance. Bring editor extensions into the same intake, SBOM and provenance process as application dependencies: an owner, a review record, a pinned version, and a periodic re-review. The gap this campaign exploited is that two registries hold the same names under different ownership rules, and a name resolved by an agent or a container build cannot tell which one it got.

Verify: the extension allowlist has a named owner and a review cadence; new entries require a provenance check against the upstream project.

H-10 · Separate developer identity from CI identity

MITRE M1026 (Privileged Account Management) · M1030 (Network Segmentation). Assume the workstation is an untrusted reader of repository metadata. Anything a workstation-resident process can read about your repositories and CI should be information you are content to have leave the building — and everything above that bar should live only in the build plane, behind H-6 and H-7.

Verify: a documented data-classification decision covering repository paths and CI identifiers, with the controls that enforce it named.

13

Deployable Playbooks

P1 · Fleet extension inventory and removal

Prerequisites: RMM or configuration-management agent able to run a script as the logged-in user (extensions are per-user, so a SYSTEM-context run finds nothing); PowerShell 5.1+ on Windows or bash on POSIX; the 77-ID list from §4 staged as ids.txt, one ID per line.
Reboot required: No. The editor must be restarted for a removal to take effect.
Rollback: Reinstall the extension from your approved source with code --install-extension <publisher.name>@<version>. Step 3 writes each removed ID and version to the report file before uninstalling, so the pre-change state is recoverable. Do not use this rollback for any of the 77 IDs — those are counterfeits and the genuine project is a different package.

# --- Step 1. Enumerate what is installed, per editor variant, as the logged-in user ---
# Windows (PowerShell, user context)
$clis = 'code','code-insiders','codium','code-oss','cursor','windsurf'
$found = foreach ($c in $clis) {
  $exe = Get-Command $c -ErrorAction SilentlyContinue
  if ($exe) { & $exe.Source --list-extensions --show-versions 2>$null |
              ForEach-Object { [pscustomobject]@{ Cli=$c; Id=$_ } } }
}
$found | Export-Csv "$env:TEMP\ext-inventory.csv" -NoTypeInformation

# POSIX (bash)
for c in code code-insiders codium code-oss cursor windsurf; do
  command -v "$c" >/dev/null 2>&1 && \
    "$c" --list-extensions --show-versions 2>/dev/null | sed "s|^|$c,|"
done | sort -u > /tmp/ext-inventory.csv

# --- Step 2. Diff against the counterfeit list (ids.txt, one publisher.name per line) ---
# Windows
$bad = Get-Content .\ids.txt
$hits = $found | Where-Object { $bad -contains ($_.Id -split '@')[0] }
$hits | Format-Table -AutoSize

# POSIX
cut -d, -f2 /tmp/ext-inventory.csv | cut -d@ -f1 | sort -u > /tmp/installed.txt
comm -12 /tmp/installed.txt <(sort -u ids.txt)

# --- Step 3. Record, then remove ---
# Record FIRST so the pre-change state is recoverable, then uninstall.
# Windows
$hits | Export-Csv "$env:TEMP\ext-removed.csv" -NoTypeInformation
foreach ($h in $hits) { & (Get-Command $h.Cli).Source --uninstall-extension ($h.Id -split '@')[0] }

# POSIX
comm -12 /tmp/installed.txt <(sort -u ids.txt) | tee /tmp/ext-removed.txt | \
  while read -r id; do code --uninstall-extension "$id"; done

# --- Step 4. Sweep the extension directories for leftovers ---
# An uninstall can leave the directory behind; the beacon lives in extension.js inside it.
# Windows
Get-ChildItem "$env:USERPROFILE\.vscode\extensions","$env:USERPROFILE\.cursor\extensions" `
  -Directory -ErrorAction SilentlyContinue | Where-Object { $bad -contains ($_.Name -replace '-\d+\.\d+\.\d+$','') }
# POSIX
ls -1d ~/.vscode/extensions/* ~/.vscode-server/extensions/* ~/.cursor/extensions/* 2>/dev/null

# --- Step 5. Verify ---
# Restart the editor, then re-run Step 1 and confirm no ID from ids.txt appears.
# Then re-run CQL Q1 scoped to this aid over the next 24 h and confirm no new collector contact.

P2 · Editor extension allowlist policy

Prerequisites: VS Code 1.96 or later (the extensions.allowed policy setting); a management channel that can write a machine-scope settings file or a policy value — Group Policy / Intune on Windows, a configuration profile or Jamf on macOS, configuration management on Linux; an agreed allowlist of publisher.extension entries.
Reboot required: No. Editor restart required.
Rollback: Delete the extensions.allowed key from the machine settings file (or set the policy to Not Configured) and restart the editor; extension installation returns to unrestricted immediately. Back up the existing machine settings.json before writing, as shown in step 1.

# --- Step 1. Back up the existing machine-scope settings ---
# Windows : %ProgramData%\Microsoft\VS Code\settings.json  (or the Intune/GPO policy path)
# macOS   : /Library/Application Support/Code/User/settings.json
# Linux   : /etc/vscode/settings.json  (path varies by packaging -- confirm on a pilot host)
Copy-Item "$env:ProgramData\Microsoft\VS Code\settings.json" `
          "$env:ProgramData\Microsoft\VS Code\settings.json.bak" -ErrorAction SilentlyContinue

# --- Step 2. Write the allowlist ---
# "true" allows any version of that extension; a version array pins it.
# An entry of "*": false denies everything not explicitly listed -- that is the whole point.
{
  "extensions.allowed": {
    "*": false,
    "ms-python.python": true,
    "ms-vscode.cpptools": true,
    "esbenp.prettier-vscode": ["11.0.0"],
    "dbaeumer.vscode-eslint": true
  },
  "extensions.autoUpdate": false,
  "extensions.autoCheckUpdates": false
}

# --- Step 3. Deploy to a pilot ring ---
# Start with one team. An over-tight allowlist breaks people's editors, which is how
# an allowlist gets rolled back permanently. Collect the misses, then widen once.

# --- Step 4. Verify ---
# On a pilot host: code --install-extension some.unlisted-extension
# Expect the install to be refused. Then confirm the approved set still installs.

# --- Step 5. Wire the allowlist to the intake process (H-9) ---
# Every addition needs an owner and a provenance check against the upstream project,
# or the allowlist becomes a list of everything anyone ever asked for.

P3 · Block deployment and verification

Prerequisites: administrative access to the recursive resolver / DNS firewall, the egress firewall or SWG, and Falcon IOC Management (write scope on the IOC Management API or console role); the CSV from §10.
Reboot required: No.
Rollback: Remove the zone entries from the resolver and firewall policy, and set the imported Falcon IOCs to no action or delete them by tag campaign:OpenVSX-EvilTwin — the tag exists specifically so the whole import can be reversed in one operation. Rollback takes effect at the next policy push; no restart needed.

# --- Step 1. Resolver / DNS firewall ---
# Add a blocking or sinkhole entry for the zone and everything under it.
mangorbit.com
*.mangorbit.com
# Configure the response as a sinkhole address you monitor, not NXDOMAIN, if you want
# to keep seeing which hosts are still trying. NXDOMAIN is fine if you rely on Q1 instead.

# --- Step 2. Egress firewall / SWG ---
# Same zone. Also create an ALERT (not a drop) rule for outbound DNS TXT queries whose
# QNAME begins "_beacon." -- a silent drop hides the operator relocating collectors.

# --- Step 3. Falcon IOC Management ---
# Console: Endpoint security -> IOC management -> Import. Use the CSV from section 10.
# Set the domain rows to action=prevent and confirm they apply to your developer host groups.
# Tag every row campaign:OpenVSX-EvilTwin so the whole import can be reversed at once.

# --- Step 4. Verify from a developer subnet, not from the SOC subnet ---
Resolve-DnsName pulse.mangorbit.com            # Windows -- expect NXDOMAIN or sinkhole
dig +short pulse.mangorbit.com                 # POSIX  -- expect empty or sinkhole
# A real answer means the policy did not reach that VLAN. Developer and lab VLANs are
# the usual gap, and they are exactly the population that matters here.

# --- Step 5. Confirm in telemetry ---
# Re-run CQL Q1 the day after deployment. New rows after the block date mean either the
# block is incomplete, or a host is using a resolver you do not control (check for DoH).

P4 · CI image and repository sweep

Prerequisites: read access to every repository and to the image build definitions; the 77-ID list as ids.txt; a source-control search API token or a local clone of each repository.
Reboot required: No.
Rollback: None required — this playbook only reads. The follow-up removals it feeds are covered by P1 (endpoints) and by ordinary version control (repository configs revert with git revert of the cleanup commit).

# --- Step 1. Find every extension-pinning config in the estate ---
find . -type f \( -name extensions.json -o -name devcontainer.json \) \
       -not -path "*/node_modules/*" -print > /tmp/ext-configs.txt

# --- Step 2. Grep those configs for the counterfeit IDs ---
grep -Fnif ids.txt $(cat /tmp/ext-configs.txt) 2>/dev/null

# --- Step 3. Check image build definitions too ---
# A Dockerfile RUN line or a post-create hook installs by name just as a devcontainer does.
grep -rIn --include=Dockerfile --include="*.dockerfile" --include="*.sh" --include="*.yml" \
     -e "--install-extension" . 2>/dev/null | grep -Ff ids.txt

# --- Step 4. Check what the built images actually contain ---
# The config is the request; the image is the fact. Inspect a built image, not just its recipe.
docker run --rm --entrypoint sh <image> -c \
  'ls -1 /home/*/.vscode-server/extensions /root/.vscode-server/extensions 2>/dev/null'

# --- Step 5. Record and remediate ---
# For each hit: remove the pin, rebuild the image, and run P1 on any host built from it.
# Record the repository, the file, the ID and the payload class (A or B) -- payload B means
# that project's private repository path was disclosed from every machine that built it.
14

Containment Runbook

PhaseActionsOwnerEvidence to capture
0 · Scope
hour 0–2
Run Q1 and Q11 over max retention with no host filter. Run native hunt N1 against resolver logs to catch sensorless hosts. Classify every hit: workstation, remote dev host, CI runner, container, golden image.Threat huntingHost list with aid, first-contact timestamp, and the matched extension ID and payload class for each.
1 · Isolate
hour 0–4
Deploy playbook P3 — zone block at resolver, egress and Falcon IOC. Do not network-contain workstations for this threat: no code execution, no lateral movement and no credential theft is in evidence, so containment cost outweighs benefit. Do quarantine affected CI runner images from further builds until swept.Network ops + SOCBlock policy diff with timestamp; verification output from P3 step 4 taken from a developer subnet.
2 · Eradicate — endpoints
day 0–2
Run playbook P1 across the affected population. Remove the extension, sweep the extension directory for leftovers, restart the editor.Endpoint engineeringext-removed.csv per host; post-removal inventory showing a clean diff.
3 · Eradicate — configs and images
day 0–5
Run playbook P4. Remove every repository and Dockerfile pin, rebuild affected images, re-run P1 on hosts built from them. Purge the counterfeits from any internal Open VSX mirror cache.Platform / DevExCleanup commit SHAs; rebuilt image digests; mirror cache purge record.
4 · Assess disclosure
day 1–5
For every payload B hit, record what was readable at the time: Git origin and upstream host plus organisation, commit-email domain, branch, HEAD commit SHA, workspace path, and any CI identity value the host exported. On a runner, that includes the private repository path. Payload A hits are hostname-level only — do not over-report them.Incident responsePer-host disclosure record, scoped to what the code actually reads. No credentials, tokens, SSH material, source code or browser data are in scope.
5 · Downstream risk
day 2–10
Nothing needs rotating — no secrets were taken. What changed is that private repository paths, organisation names and developer email domains may now be known to a third party. Brief the affected teams to expect targeted phishing referencing real internal project names, and add those project names to your phishing-simulation and detection content.Incident response + security awarenessNotification record; phishing-content update ticket.
6 · Recover
day 3–14
Deploy the H-4 allowlist to a pilot ring, then broadly. Pin the internal mirror (H-5). Apply deny-by-default egress to runners and devcontainers (H-6). Keep the zone block permanent — the registration runs to 2029.Platform / DevEx + network opsAllowlist policy deployed and verified per P2 step 4; runner egress policy verified from inside a build container.
7 · Watch
ongoing
Keep Q2 scheduled indefinitely — a _beacon TXT hit means the operator has relocated and Q1 is blind. Keep Q3 and Q4 on a weekly cadence with domain-age enrichment. Re-run Q11 after any change to the runner or golden-image build.Threat huntingScheduled-search configuration; weekly hunt notes.
15

Detection Coverage Map

TechniqueBehaviourCQLIOA / controlCoverage
T1195.002Counterfeit package published and pulled from Open VSXQ5, Q6, Q11H-2, H-5, N4GOOD
T1176.002Malicious IDE extension installed and loading on every editor startQ5, Q6, Q8, Q11IOA-1, H-4GOOD
T1036.005Name, namespace and description copied from a real extension; version pinned to 0.0.1Q5, Q8H-4, H-5, N3, N4PARTIAL — no endpoint telemetry distinguishes a counterfeit name from a real one; caught at the registry and config layer, not on the host
T1554extension.js swapped for a beacon inside an otherwise normal packageQ6, Q11H-8GOOD — hash-level, for the 77 known samples
T1204Install by name, by a human or by an agent / devcontainer / provisioning scriptQ5, Q8H-3, H-4, N3PARTIAL — GUI marketplace installs leave no command line
T1082 / T1033Host, user, editor, platform, locale and timezone collectedQ3, Q4, Q7H-6, H-8PARTIAL — inferred from the egress, not observed directly
T1005 / T1083.git/config and .git/HEAD read; workspace path collectedQ7H-8PARTIAL — depends on FileOpenInfo being enabled, which it often is not (gate G3)
T1518Up to 60 installed extension IDs enumeratedH-8GAP — reading the extensions directory is indistinguishable from the editor's own startup behaviour
T1016Proxy hostname read from the environmentH-7GAP — an in-process environment read produces no endpoint event
CI identity values read (GITHUB_REPOSITORY, CI_PROJECT_PATH, Azure DevOps URI, Buildkite slug, CircleCI username, Codespace name, Gitpod URL)H-6, H-7GAP — same reason. This is the highest-impact behaviour in the campaign and it is invisible to endpoint telemetry. Prevention (H-6, H-7) is the only real answer.
Extension reports whether a repo config pulled it in; reads the telemetry opt-out and ignores itH-3GAP — in-process logic, no observable artefact
T1071.001 / T1041HTTPS beacon to the collector with a distinctive User-Agent and URI pathsQ1, Q4, Q9, Q10H-1, H-6GOOD where proxy logs reach NG-SIEM; PARTIAL without them
T1071.004 / T1568DNS TXT lookup at _beacon.<domain> for a replacement collector; randomised cb labelsQ1, Q2H-1GOOD
T1008Multi-endpoint failover, any HTTP status counted as success, 7-day retry ladderQ4 (cadence)H-1, H-6PARTIAL — the undisclosed second failover domain is only reachable behaviourally, via Q3 and Q4

Validation gates: §11 carries ten gates. The three that decide whether this pack works in your tenant are G1 (is ContextBaseFileName populated — without it Q3 and Q4 return nothing and look clean), G3 (does FileOpenInfo return rows at all — if not, Q7 and Q8 are unavailable and the T1005 row above drops to GAP), and G4 (do your proxy parser field names match Q9 and Q10). Only Q1, Q2, Q9 and Q11 are eligible for promotion to a Scheduled Search per G9; everything else stays investigate-only until its FP rate is measured locally.

Standing gap: steps 4, and the environment-variable and extension-enumeration reads in step 6 of the attack chain, produce no endpoint artefact at all — they happen inside the Node process. No query in this pack can close them, and none pretends to. They are covered by prevention (H-4, H-6, H-7) and by runtime behavioural monitoring (H-8), not by detection.

16

Hunt Summary Ticket

Copy into your case management system
TITLE:      Hunt - Open VSX "Evil Twin" counterfeit extension campaign (mangorbit collector)
SEVERITY:   High
SCOPE:      Developer workstations (Windows / macOS / Linux), remote dev hosts, CI/CD build
            runners, devcontainers and Codespaces, golden images and VDI templates, internal
            Open VSX mirror, web proxy / SWG, recursive DNS resolvers.

HYPOTHESIS: One or more endpoints or build images in the estate carry one of 77 counterfeit
            Open VSX extensions published 2026-07-26 to 2026-08-01, each beaconing to
            mangorbit[.]com. 19 of the 77 exfiltrate Git remote host and organisation, commit
            email domain, branch, HEAD commit SHA and CI identity values -- on a build runner
            that is the full private repository path. Packages were removed from Open VSX on
            2026-08-03; installed copies and config pins are unaffected by that removal and
            the beacon still fires on every editor start.

QUERIES:    Q1  collector domain resolved                       CONF high / FP low   [ALERT]
            Q2  DNS TXT _beacon.* takedown-contingency lookup   CONF med  / FP low   [ALERT]
            Q3  rare external domain from an editor process     CONF med  / FP med   [HUNT]
            Q4  editor / extension-host rare external egress    CONF med  / FP med   [HUNT]
            Q5  CLI install of a counterfeit extension ID       CONF high / FP low   [ALERT]
            Q6  VSIX written to disk                            CONF med  / FP med   [HUNT]
            Q7  editor reads .git config / HEAD                 CONF med  / FP high  [HUNT]
            Q8  repo extension manifests written                CONF low  / FP high  [HUNT]
            Q9  User-Agent vscode-ext-metrics in proxy logs     CONF high / FP low   [ALERT]
            Q10 collector URI paths in proxy logs               CONF med  / FP med   [HUNT]
            Q11 known counterfeit VSIX SHA-256 sweep (all 77)   CONF high / FP low   [ALERT]

DO FIRST:   1. Block mangorbit.com and *.mangorbit.com at resolver, egress and Falcon IOC
               management (playbook P3). Verify from a DEVELOPER subnet, not the SOC subnet.
            2. Run Q1 and Q11 over maximum retention with no host filter.
            3. Run native hunt N1 against resolver logs -- catches sensorless runners and
               containers that Q1 cannot see.
            4. Run playbook P1 across developer hosts, runner images and golden images.
            5. Run playbook P4 across repositories and image build definitions.

FINDINGS:   [ ] hosts with collector contact (Q1 / N1): ____
            [ ] hosts with a matching VSIX hash (Q11):  ____
            [ ] repository or image configs pinning a counterfeit ID (P4): ____
            [ ] of the above, how many are payload B (Git + CI disclosure): ____
            [ ] CI runners or cloud dev environments affected: ____

GAPS:       - Extension enumeration, CI environment-variable reads and the proxy-hostname read
              happen in-process and produce NO endpoint artefact. Prevention only (H-6, H-7).
            - Q7 and Q8 depend on FileOpenInfo / CriticalFileModified, which are off by default
              in many sensor policies. Confirm via validation gate G3 before relying on them.
            - The second failover domain used by a subset of samples was not published by the
              researchers, so it is not in this pack. Q3 and Q4 cover it behaviourally.
            - GUI marketplace installs leave no command line, so Q5 misses them.

ACTIONS:    [ ] P3 block deployed and verified from a developer subnet
            [ ] P1 inventory + removal complete across the developer and build estate
            [ ] P4 repository and image sweep complete; pins removed; images rebuilt
            [ ] Q1 / Q2 / Q9 / Q11 promoted to Scheduled Searches (gate G9)
            [ ] Payload-B disclosure record written per affected host
            [ ] Affected teams briefed on targeted phishing using real internal project names
            [ ] H-4 extension allowlist piloted (playbook P2)
            [ ] H-6 deny-by-default egress applied to CI runners and devcontainers

OWNER:      SOC / Threat Hunting  (co-owners: Platform-DevEx for P2/P4, Network Ops for P3)
VERSION:    v0.1 (2026-08-08)
17

Changelog

v0.12026-08-08Initial build. Five sources fetched and snapshotted; SC World dropped (HTTP 403, no capturable text). 11 CQL queries, 1 Custom IOA, 1 Scheduled Search with alert package, 10 tiered hardening controls, 4 deployable playbooks, 7-phase containment runbook. IOC set: 6 network indicators plus all 77 VSIX SHA-256 values, every one traced to a saved source snapshot. Second failover domain deliberately excluded — not published by the researchers.
18

References

TierSourceUsed forAccessed
1 · PrimaryManifold Security — 77 "evil twin" Open VSX extensions: 19 copy private repo and CI data to a new domain (Ax Sharma, Cody Nash, 2026-08-04)Every atomic indicator; the 77-package table with versions, payload class, beacon hosts, first-seen dates and VSIX SHA-256; retry schedule; TXT failover; payload field lists2026-08-08
2The Hacker News — Open VSX Removes 77 Malicious Evil Twin Extensions Exfiltrating Developer Data (2026-08-05)Independent confirmation of counts, dates, collected field set; the 19 recon extension IDs2026-08-08
2BleepingComputer — 77 Open VSX extensions found harvesting developer info (Lawrence Abrams, 2026-08-04)Collector subdomains; per-package tracking ID; 7-day retry; TXT fallback; explicit negative scope (no source code, credentials, tokens, SSH material or browser data)2026-08-08
2Cyber Security News — 77 Evil Twin Open VSX Extensions Exfiltrate Private Git Repository and CI Data (2026-08-05)Second independent IOC table: domain, hosts, URI paths, _beacon TXT with base=https:// response prefix, User-Agent2026-08-08
2GBHackers — Fake Open VSX Extensions Hijack AMD, Azure, Salesforce and Government NamespacesCorroborating IOC table; collectors answer "ok"; any HTTP status treated as delivery success2026-08-08
FrameworkMITRE ATT&CK T1176.002 — Software Extensions: IDE ExtensionsPrimary technique mapping and mitigation routing2026-08-08
FrameworkMITRE ATT&CK T1195.002 — Compromise Software Supply ChainInitial-access mapping; M1051 / M1016 routing for H-5 and H-92026-08-08
FrameworkNIST SP 800-161r1 — Cybersecurity Supply Chain Risk ManagementAuthority behind H-9 (treat the developer toolchain as a first-class supply chain)2026-08-08
VendorVisual Studio Code — enterprise deployment and the extensions.allowed policyConfiguration behind H-4 and playbook P22026-08-08

Snapshot provenance. The verbatim fetched text of every cited news and research source is saved alongside this pack in OpenVSX-EvilTwin-Extensions-Hunt-sources/ (01-manifold-security.txt, 02-thehackernews.txt, 03-bleepingcomputer.txt, 04-cybersecuritynews.txt, 05-gbhackers.txt). Every atomic indicator shipped in §10 was mechanically traced back to that text. No source contained agent-directed text or instructions.