Executive Summary
Between April 27 and April 28, 2026, Yeeth Security’s scanning pipeline confirmed sixteen distinct malicious extensions on the Open VSX marketplace. The samples were submitted under sixteen different publisher accounts, with sixteen different extension names, and sixteen different displayed purposes like Android tooling, Tailwind CSS support, and C++ helpers. They are the same campaign, written by the same threat actor, using the same obfuscation, and pointing at the same payload host.
This report walks through how Bane — Yeeth Security’s proprietary threat-intelligence knowledge graph — converts per-artifact verdicts produced by Argus into threat actor-level attribution by matching on behavior preserved across surface variation. The investigation centers on the karnenko / drovenko wave, links it to an earlier wave under a different publisher cluster (dopbop-studio), and demonstrates that detection alone is no longer the right unit of analysis for an Open VSX threat surface where the threat actor is shipping faster than the marketplace can review.
The Technical Challenge: Obfuscation as Evasion of Similarity Tools
The functional logic — fetch a payload from an attacker-controlled GitHub release, decode it at runtime, write it to disk, execute it — is identical across samples in the cluster. The expression of that logic is obfuscated per build. The threat actor’s primary evasion mechanism is string-fragment reconstruction with index-based decoders. A representative excerpt from one of this week’s samples shows how the payload host URL is assembled at runtime:
S =
"https" +
"://gi" +
"thub." +
ad(0x59d, 0x523, 0x53e, 0x598) +
"rance" +
ac(0x6bb, 0x5fb, 0x63f, 0x639) +
"8/dqw" +
ad(0x4eb, 0x517, 0x505, 0x54d) +
"relea" +
ac(0x5aa, 0x57c, 0x575, 0x59a) +
ad(0x4b4, 0x475, 0x4bb, 0x4e8) +
ad(0x499, 0x4fd, 0x50a, 0x4dd) +
ad(0x490, 0x49a, 0x491, 0x4b8) +
ac(0x682, 0x5b2, 0x6b2, 0x62e) +
ad(0x4c8, 0x44f, 0x468, 0x3f6) +
ad(0x470, 0x446, 0x3c8, 0x3e3) +
ad(0x459, 0x44a, 0x453, 0x414) +
".5.8." +
ac(0x5da, 0x551, 0x542, 0x55e);
The literal substrings ("https", "://gi", "thub.", "rance", "8/dqw", "relea") are the threat actor’s constant fragments; the ad() and ac() decoder calls return the rest of the URL letter-by-letter from a per-build offset table. A textual search for github.com/francesca898/dqwffqw/releases/download/vsx/autoimport-smart-tool-2.5.8.vsix will not match this code. The bytes that produce that URL at runtime are spread across the bundle as four-argument hex tuples that index into a generated lookup. Across the sixteen samples in this wave the lookup tables, offsets, variable names, and the order of fragment concatenation differ. The resulting URL is identical.
This is the central problem for any detection stack that relies on byte-similarity:
- Cryptographic hashing (SHA-256, MD5) misses by design — even a single offset change inside the lookup table changes the hash entirely.
- Locality-sensitive hashing (TLSH, ssdeep) misses because the threat actor regenerates the bundle from scratch per sample with different variable names, different module-stub layouts, and different bluebird-bundled boilerplate. A sidebar diff between any two samples in this wave is solid red on the left and solid green on the right with no streaks of preserved code.
- YARA rules anchored on string literals miss because no full-form malicious string survives in the artifact at rest. The
francesca898username never appears as a contiguous token; neither doesreleases/downloadorautoimport-smart-tool.
What survives across the variants is the logic: the control-flow shape of the URL-assembly function, the structural relationship between the decoder calls and the constant fragments, the order in which the resulting URL is passed to https.get and the response is written to disk. Those features are not invariant to a textual diff but they are invariant to the threat actor’s transformation pipeline. Detection at this level requires a different primitive.
Methodology: Bane on Top of Argus
Argus produces per-artifact verdicts. For every sample submitted to Open VSX (and for every sample submitted directly through the Argus API), the pipeline returns a binary judgment — malicious or clean — along with a structured set of findings. Bane is a layer above that provides lineage and attribution. The workflow initially extracts features from each sample and indexes them into Bane. These features are used to form an enriched picture that clusters samples together based on various metrics and weights. When samples share a meaningful overlap, the relationship is reported as a link and the attribution work begins.
The design is consciously dual to Argus. Argus answers should this artifact be installed. Bane answers who built this artifact, and what else have they built. The two products are pipelined: every Argus verdict lands in Bane’s corpus, and every Bane attribution result feeds back into Argus as a static signal that elevates priors for the next analyst pass.
Investigation Case Study: The Karnenko / Drovenko Cluster
When the threat actor shipped the wave under analysis, our pipeline confirmed sixteen samples within forty-eight hours:
keltarin.android-deep-hub-0.4.1
trenarin.autodocstring-auto-studio-0.6.3
gronarin.auto-super-kit-0.1.12
goltikov.auto-rich-forge-0.5.17
sremuven.beautify-super-lens-1.4.12
meltovik.bookmark-rich-tool-0.0.24
kwinsolin.better-cpp-tool-1.27.5
karnenko.cursorless-pro-pilot-1.1.1767
krosaven.dot-live-forge-0.6.1
sremovik.dendron-deep-hub-0.1.7
brixovik.es7-quick-hub-4.4.4
dranaven.flask-live-craft-0.1.4
CubeDiverVolt.html-code-validate-2.12.18
tralarin.firefox-rich-lens-2.15.1
drovenko.data-live-suite-1.0.32
Anchoring on karnenko.cursorless-pro-pilot-1.1.1767 and querying Bane for distinctive genes shared across the corpus surfaces a tight pairwise relationship with drovenko.data-live-suite-1.0.32. The two samples were submitted through different namespaces, advertised entirely different functionality, and produced bundle contents with effectively no overlapping text — yet they share a meaningful population of distinctive, recurring genes.
The same query, expanded across the historical corpus, surfaces another match: dopbop-studio.vscode-tailwindcss-extension-toolkit-0.14.29. That sample belongs to an earlier wave, submitted by a different publisher cluster, advertised as a Tailwind CSS toolkit. The genes that link dopbop-studio to the current wave are the same genes that link karnenko to drovenko. The conclusion is direct: a single threat actor is responsible for both waves, and the toolkit between them has been refined but not replaced.
The campaign’s payload host is the same across both waves: github[.]com/francesca898/dqwffqw. The full URL — github[.]com/francesca898/dqwffqw/releases/download/vsx/autoimport-smart-tool-2.5.8.vsix — is the runtime output of the string-fragment reconstruction shown earlier. The francesca898 GitHub account, previously documented as the dropper host in an earlier campaign post, continues to serve the same payload to this generation of samples. Infrastructure persistence on the threat actor side; surface variance on the artifact side. The combination is the signature.
What this means in practice: the threat actor treats publishing names, namespaces, and bundle contents as disposable, and treats payload-host infrastructure and the underlying dropper toolkit as persistent. The disposable layer is what marketplace policy can react to. The persistent layer is what attribution analysis must hold onto. Bane is built on the assumption that the persistent layer is where the actor identity lives.
Conclusion
Detection without attribution leaves the threat actor playing the same game indefinitely. A malicious extension is removed; the next one is uploaded minutes later under a different publisher. A naming pattern is added to a blocklist; the generator’s regex is altered to evade it. A YARA rule is published; the next bundle reorders the offending function. None of these reactions touch the threat actor’s actual cost structure, which is dominated by toolkit development, not by individual sample publication.
Attribution flips that asymmetry. When every sample shipped under a new identity is automatically linked to a known threat actor’s prior corpus, the threat actor’s costs scale with their toolkit rather than with their publishing volume. Replacing the dropper meaningfully — changing the gene population that survives obfuscation — is expensive; iterating publisher names is free. The Bane methodology is designed to make the cheap part useless and the expensive part the threat actor’s only remaining option.
The sixteen samples in this wave are removed. The next wave is being assembled. The infrastructure at github[.]com/francesca898/dqwffqw was still serving the payload at the time of writing. If you operate an extension marketplace, an IDE fork that resolves missing extensions through Open VSX, or an internal extension catalogue, the Argus + Bane combination is available through the Yeeth Security platform. The pipeline integration is straightforward; the attribution layer pays back the integration cost the first time a sample variant arrives that no per-artifact tool would have linked to anything else.
Indicators of Compromise
Confirmed Malicious Extensions (April 27–28, 2026 wave)
keltarin.android-deep-hub-0.4.1trenarin.autodocstring-auto-studio-0.6.3gronarin.auto-super-kit-0.1.12goltikov.auto-rich-forge-0.5.17sremuven.beautify-super-lens-1.4.12meltovik.bookmark-rich-tool-0.0.24kwinsolin.better-cpp-tool-1.27.5karnenko.cursorless-pro-pilot-1.1.1767krosaven.dot-live-forge-0.6.1sremovik.dendron-deep-hub-0.1.7brixovik.es7-quick-hub-4.4.4dranaven.flask-live-craft-0.1.4CubeDiverVolt.html-code-validate-2.12.18tralarin.firefox-rich-lens-2.15.1drovenko.data-live-suite-1.0.32
Earlier-Wave Linked Sample (Threat Actor Lineage)
dopbop-studio.vscode-tailwindcss-extension-toolkit-0.14.29
Network Indicators (Defanged)
- Payload host repository:
github[.]com/francesca898/dqwffqw - Payload artifact URL:
hxxps://github[.]com/francesca898/dqwffqw/releases/download/vsx/autoimport-smart-tool-2.5.8[.]vsix
Read More
All Posts →
Threat Intel
Weaponizing Empty Github Repositories with Releases
A campaign of VS Code extensions using GitHub releases as a payload delivery mechanism — and blockchain RPCs as comma...
Threat Intel
Threat Intel
Weaponizing Extension Packs with PackRAT
Dissecting a Downloader Abusing Extension Packs for Stealthy Distribution