- Malware Analysis
- Threat Intelligence
- Reverse Engineering
- Crypto Security
Needle: Inside a Modular Crypto-Stealing C2 That Left Its Keys in the Malware
A single MalwareBazaar sample, fed to our Caronte CTI platform, unfolded into 1,932 victims, six withdrawal wallets, and ~$148 ETH already moved to cold storage. All from intelligence Caronte extracted autonomously in minutes.
Beelzebub Labs
From researchers at beelzebub labs
Research note: The API key used to enumerate this infrastructure was extracted from malware distributed publicly on MalwareBazaar. The same credential the threat actor embedded in their own agent to phone home from infected machines. No victim data was retained beyond the statistics reported here. No settings were modified.
TL;DR
- Analysis performed autonomously by Caronte, our agentic CTI platform: from a single MalwareBazaar sample to full C2 attribution, victim enumeration, and on-chain fund tracking, in minutes, with no manual reverse engineering
- Needle is an active MaaS crypto-stealing platform with two live modules: a browser extension spoofer targeting MetaMask, Phantom, and Trust Wallet; and a Rust desktop agent impersonating Exodus, Trezor, and Ledger
- The Rust agent embeds its C2 API key without protection. Using it, we enumerated 1,932 victims and the attacker’s full withdrawal configuration across six blockchains
- The panel’s React SPA performed authentication entirely client-side: writing a structurally valid token to
localStoragewas enough to render the full admin dashboard, with no server validation of the UI routing layer - The frontend bundle defines a write endpoint for the withdrawal configuration using the same agent key authentication scheme, meaning the same credential that phones home from infected machines could potentially redirect every future auto-withdrawal
Key Findings at a Glance
| Category | Details |
|---|---|
| Sample | Rust PE (Windows), v1.3, SHA256: d6ca3760...a490 |
| C2 Stack | React 19 SPA · Express.js API · nginx 1.29.8 |
| Active Modules | Desktop Wallet Spoofer · Browser Wallet Spoofer |
| Victims | 1,932 total (111 browser extension, 1,821 desktop sessions) |
| Targeted Wallets | MetaMask · Trust · Phantom · OKX · Exodus · Trezor · Ledger · Atomic · Guarda · Zelcore |
| Campaign Window | Active from April 18, 2026; ongoing at time of writing |
| Auth Failures | Unprotected agent key · client-side-only auth · unauthenticated builder |
| C2 Infrastructure | 130.12.180.135 (ports 3000, 8080, 8181) |
| Confirmed Fund Movement | EVM hot wallet: ~$148 ETH forwarded to cold storage |
Discovery: From MalwareBazaar to Full C2 Visibility
A sample flagged as a Windows crypto-stealer surfaced on MalwareBazaar. We fed it to Caronte, our agentic CTI platform. From that single upload, Caronte ran the full analysis pipeline autonomously, with no manual reverse engineering required.
Caronte started with binary classification: a stripped Rust PE, 8.9MB, unsigned, entropy 6.71, tagged on MalwareBazaar as RustyStealer, distributed via the Phorpiex spam botnet. It identified the egui desktop GUI framework from characteristic string patterns, traced the Rust panic handler paths to recover wallet impersonation targets (zelcore, trezor, ledger), and reconstructed the embedded configuration schema from the binary’s data segments.
Static reverse-engineering also surfaced capabilities beyond the wallet spoofer: keylogging via GetAsyncKeyState (consistent with capturing seed phrases as typed, not only on submission), persistence via the Windows Run registry key, and anti-debugging techniques including SetUnhandledExceptionFilter and software breakpoint traps. The C2 IP resolved to ASN 202412 (Omegatech LTD, Amsterdam), a known bulletproof hosting provider with 27 OTX malicious pulses on record at time of analysis.
The full analysis produced a threat graph of 94 nodes and 135 edges, matching 40 YARA rules across local and MalwareBazaar rulesets, including high-severity hits for keylogging, anti-VM evasion, and Rust-based stealer patterns. Within minutes of submission, Caronte had produced a complete operator profile: platform identity, C2 address, API key, and a live panel managing nearly two thousand victims.
What Caronte produced autonomously
| Deliverable | Output |
|---|---|
| Binary classification | Stripped Rust PE, 8.9MB, RustyStealer family |
| Framework identification | egui GUI, Phorpiex distribution |
| Capability extraction | Keylogging, persistence, anti-debug |
| Embedded config recovery | C2 URL and API key from binary data segments |
| Infrastructure attribution | ASN 202412 Omegatech, 27 OTX pulses |
| Threat graph | 94 nodes, 135 edges |
| Detection coverage | 40 YARA rule matches |
| Time to intelligence | Minutes vs. days of manual RE |
Everything below this section is verification and exploration built on top of Caronte’s autonomous output: the human-in-the-loop step.
What is Needle?
Needle is a modular MaaS (Malware-as-a-Service) platform. The panel is built around purchasable modules: operators activate what they need, the infrastructure stays shared. During our analysis two modules were active:

| Module | Status | Description |
|---|---|---|
| Needle Core | Inactive | Form grabber, Clipper, system core |
| Extension Base | Inactive | Site substitution, backup domain management |
| Desktop Wallet Spoofer | Active | Rust agent, fake wallet UI, seed phrase capture |
| Browser Wallet Spoofer | Active | Extension replacement, password and seed interception |
| Farm | Inactive | Deferred withdrawal triggered by balance threshold |
| Launch Panel | Inactive | Traffic management and launcher creation |
| Captcha (Win+R) | Inactive | Social engineering delivery mechanism |
The module structure is consistent with a platform sold or rented to multiple operators, inactive modules represent capabilities the current operator hasn’t subscribed to. This is corroborated by independent research: in the same month, Malwarebytes documented a separate Needle campaign using entirely different infrastructure (different hashes, different C2 IPs), focused on delivery via a fake trading website and DLL hijacking. Their samples share no overlap with this C2. Where that analysis documented how victims get infected, this report goes inside the infrastructure that processes them.
The Rust Agent: A Key Hidden in Plain Sight
The binary is a stripped Rust PE with no debug symbols. Rust’s panic handler mechanism retains source file paths even in release builds, these strings, present in the binary’s data section, identify the wallet impersonation targets:
src/views/zelcore/app.rs
src/views/trezor/seed_panel.rs
src/views/ledger/screens/seed_recovery.rs
The binary also carries a full egui desktop GUI framework, identifiable from characteristic string patterns in the binary, used to render the fake wallet dialogs victims interact with.
Through analysis of the binary’s data segments and configuration structures, Caronte reconstructed the agent’s embedded configuration block:
{
"version": "1.3",
"api_url": "http://130.12.180.135:3000/api/v2",
"api_key": "alk_776...fc1",
"open_original_wallet_on_valid_seed": true
}
The open_original_wallet_on_valid_seed field is worth noting: when set to true, the agent opens the legitimate wallet application after successfully capturing the seed phrase, so the victim sees their real wallet load normally and has no immediate reason to suspect anything went wrong. It is a deliberate anti-detection measure baked into the configuration.
The configuration was stored without any obfuscation or encryption. More critically, the api_key field is the same credential infected machines use to authenticate back to the C2. There is no separation between agent authentication and API read access: the key that registers a new victim also reads the entire victim list.
Mapping the API Surface: Frontend Bundle Analysis
Before sending a request to the server, Caronte analyzed the React SPA’s webpack bundle served at port 3000. All API route strings were present in the bundled JavaScript:
/api/v2/wallets
/api/v2/antiledger/settings
/api/v2/antiledger-v2/seed-phrases
/api/v2/panel-access/evaluate
/api/v2/backup-domains/active
/api/v2/browser-spoofer/build/{buildId}/status
/api/v2/builds/{buildId}/{filename}
This gave us a complete endpoint map derived from a single bundle fetch, before any active enumeration of the server.
Getting Inside the Panel: Two Independent Access Layers
Analysis of the C2 required two separate techniques that provided different types of visibility. They are worth distinguishing clearly.
Frontend UI: Client-Side-Only Authentication
The React SPA’s authentication logic reads from localStorage to decide whether to render the dashboard or redirect to login. The server plays no role in this routing decision.
Writing two values directly to localStorage in the browser was sufficient:
localStorage.setItem("auth_token", "<structurally valid JWT, exp:9999999999>");
localStorage.setItem("auth_user", JSON.stringify({ username: "admin", role: "admin" }));
With requests to /login intercepted to prevent the app from refreshing auth state, the full admin dashboard rendered. This is not a JWT library bypass; it is the absence of any server-side authentication gate on the UI routing layer, the SPA trusts localStorage unconditionally for rendering decisions.
What this gave us: The panel’s structure, module layout, configuration pages, the overall UI. The C2 stack was confirmed from HTTP response headers (Server: nginx/1.29.8) and bundle metadata (React 19 version strings in the webpack output). The screenshots in this report show the rendered frontend.
What it did not give us: Actual data. Backend API endpoints require a valid signed JWT (HS256) for privileged operations. With only a fake token, API calls from the SPA returned 401s.

Data Access: The Embedded Agent Key
All victim records, withdrawal configuration, and session statistics came from direct API calls using the agent key extracted by Caronte from the binary. This key authenticates the agent-facing endpoints independently of the admin JWT scheme, the same credential infected machines use to register themselves was sufficient to read the entire victim list and operator configuration.
The two techniques are complementary but entirely independent. The frontend bypass revealed the panel’s structure and confirmed it was a live operator platform. The agent key provided all the data.
Two-Pronged Attack Strategy
Browser Wallet Spoofer
The browser module replaces or intercepts legitimate crypto wallet extensions. Victims interact with what appears to be their normal MetaMask or Phantom extension; the fake version intercepts the password or seed phrase on entry and exfiltrates it to the C2.
The builder, running on port 8080 with no meaningful authentication, lets operators configure which wallets to target, installer self-destruction behavior, wait mode, and smart-pin options.

Targets include MetaMask, Trust Wallet, Phantom, OKX, TonKeeper, Coinbase, Atomic, Bybit, and Binance Wallet. 111 browser victims were recorded from the API:
| Wallet | Victims |
|---|---|
| MetaMask | 48 |
| Trust Wallet | 30 |
| Phantom | 20 |
| OKX | 6 |
| TON | 3 |
| Binance | 2 |
| Bybit | 2 |
| Total | 111 |
Polling the /api/v2/wallets endpoint repeatedly during our monitoring window, we observed the count growing by 3 to 5 new entries every few hours. The campaign was actively acquiring victims throughout our observation period.
Desktop Wallet Spoofer
The Rust agent impersonates desktop wallet applications. When a victim opens what they believe is their Exodus, Trezor, or Ledger application, a fake “Restore Wallet” dialog appears and requests their seed phrase. Entered seeds are posted to the C2; if valid and the auto-transfer module is enabled, funds move to the operator’s withdrawal addresses.

1,821 desktop sessions were on record at time of data collection, all with awaitingSeed: true, indicating the C2 had registered the infected machines but no seed phrases had been submitted yet. The server-reported total reached 1,825 during continued monitoring, confirming new sessions were accumulating. Whether the absence of submitted seeds reflects early-stage distribution or victims abandoning the fake dialog cannot be determined from the data alone.

The withdrawal module supports three automation modes: auto balance check on connect, automatic withdrawal after password entry, and withdrawal triggered by seed phrase submission, all independently toggleable per operator.
Inside the C2: What the Agent Key Unlocked
Attacker Configuration and Fund Movement
GET /api/v2/antiledger/settings returned the full operational configuration: withdrawal addresses for BTC (1PVq...qfZ), LTC (LKmc...DBL), DOGE (D5xE...FFq), SOL (BpxG...YU), TRON (TDij...UNp), and EVM (0xD5...F1b), alongside a TronGrid API key.
TronGrid is the TRON equivalent of Infura, a professional-grade blockchain RPC service. Its presence here indicates the operator has set up automated TRON/USDT balance checking before triggering withdrawal, rather than relying on manual monitoring.
On-chain analysis confirms the operation is actively moving funds. The EVM withdrawal address had approximately $148 in ETH emptied and forwarded to three separate cold wallets at time of analysis. The TRON address held approximately $60 in USDT and TRX. The BTC, LTC, DOGE, and SOL addresses showed no recorded transaction history, consistent with a campaign still in early collection phase for those chains.
Write Access to Withdrawal Configuration
The frontend bundle defines PUT /api/v2/antiledger/settings using the same agent key authentication scheme as the GET endpoint. If the key is accepted on the write path, which the identical auth implementation suggests, the six withdrawal addresses could be replaced with arbitrary addresses, redirecting every future auto-transfer away from the operator.
We did not send any write requests. The implication is reported here because it represents the full consequence of distributing an agent key with no scope restriction: a researcher, a competitor, or a hostile party with access to the sample binary can enumerate this C2’s victims and potentially redirect its earnings.

Two Instances, One Database
During enumeration we identified a second panel instance at port 8181 on the same host, accepting the same agent key. Cross-referencing seed phrase session IDs confirmed 1,814 records in common out of 1,817 queried from port 3000, near-perfect overlap confirming a shared database backend. The delta reflects the time gap between queries, not a separate deployment.
The shared-database setup is consistent with a MaaS platform providing independent panel access to multiple operators or affiliates without separate infrastructure. Port 8181 returned an empty result set for the browser wallet endpoint; whether this reflects a different module subscription or simply a different API surface for that instance is not possible to determine definitively from the available data.
Indicators of Compromise
Sample:
SHA256: d6ca3760...a490
MD5: 8b3433...36ca534b
Name: needle_agent_v1.3 (internal)
Network:
C2 Panel: 130.12.180.135:3000
Builder: 130.12.180.135:8080
Secondary: 130.12.180.135:8181
Stack: nginx/1.29.8 (Server header), React 19, Express.js
Hosting: ASN 202412 Omegatech LTD, Amsterdam (bulletproof hoster)
Persistence:
Registry: HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
rule Needle_Crypto_Stealer_v1 {
meta:
description = "Needle MaaS crypto-stealer, Rust desktop agent v1.x, distributed as agent.exe"
author = "Beelzebub Research"
date = "2026-04-28"
reference = "https://beelzebub.ai/blog/needle-c2-crypto-stealer-analysis"
strings:
// Needle-specific C2 API path and config field
$api1 = "antiledger-v2/seed-phrases" ascii
$api2 = "open_original_wallet_on_valid_seed" ascii
// Rust panic handler source paths, present in release builds
$src1 = "src/views/zelcore/app.rs" ascii
$src2 = "src/views/trezor/seed_panel.rs" ascii
$src3 = "src/views/ledger/screens/seed_recovery.rs" ascii
// UI string keys from Needle's fake-wallet dialog logic
// needle_desktop_wallet_debug.txt is a debug log path embedded in the agent
// needle.wallet. is a namespace prefix for wallet-specific UI keys
$ui1 = "checkseed.invalid_message" ascii
$ui2 = "needle_desktop_wallet_debug.txt" ascii
$ui3 = "needle.wallet." ascii
condition:
// Valid Windows PE (MZ header + PE signature)
uint16(0) == 0x5A4D and
uint32(uint32(0x3c)) == 0x4550 and
// Rust + egui binary size range
filesize > 5MB and filesize < 50MB and
// Both Needle-specific API strings must be present
all of ($api*) and
// At least one compiled-in Rust source path
1 of ($src*) and
// At least two Needle UI strings
2 of ($ui*)
}
Conclusion
In under two weeks, this Needle deployment registered 1,932 targets across two attack surfaces. The EVM hot wallet had already moved funds to cold storage before we completed our analysis, the operation was not hypothetical.
The complete picture only became available because of how the operator built the agent. Using credentials they distributed inside their own malware, we read their victim list, extracted their withdrawal configuration, and traced their on-chain activity. The Malwarebytes report on a separate Needle campaign confirms this is not an isolated deployment: multiple operators are running simultaneous campaigns on the same platform.
How Caronte Made This Possible
This entire investigation started from a single sample upload. Caronte autonomously recovered the embedded configuration, attributed the infrastructure, generated the YARA rule, and built the threat graph. Work that would have taken a senior reverse engineer days. The human analyst’s role was verification and on-chain correlation, not extraction.
If your SOC is still reverse-engineering samples by hand, you are operating at the speed of the attacker’s first wave, not their last.