| title | NullReceiver: A Blank Crypto Transfer Is the Next Step Past EtherHiding | ||||||
|---|---|---|---|---|---|---|---|
| description | The OSM team identified a new DPRK npm technique that hides a C2 IP address inside the recipient of a completely empty Ethereum transfer — no calldata, no smart contract, and a stronger design than the EtherHiding technique Google exposed in 2025. | ||||||
| date | 2026-08-01 | ||||||
| author | 6mile | ||||||
| tags |
|
||||||
| readTime | 5 min read |
The OSM team has identified a new blockchain-based command-and-control technique hiding inside two trojanized npm packages, bianira-ui and fluid-type-ui. Both are DPRK-linked clones of legitimate Tailwind CSS plugins, and both use the same trick to find their command server: they read it out of the destination address of a completely blank cryptocurrency transfer. We're calling it NullReceiver, and we think it's a deliberate improvement on EtherHiding, the blockchain C2 technique Google's Threat Intelligence Group tied to a North Korea-linked actor in October 2025.
- Threat Type: Blockchain-based C2 resolution technique in a Node.js RAT
- Affected Ecosystem: npm (
bianira-ui@1.27.0,fluid-type-ui@2.0.8) - Attribution: DPRK "Contagious Interview" campaign
- Key Finding: The malware decodes its C2 IP address directly from the bytes of a made-up recipient address on a zero-value, zero-data Ethereum transfer — no smart contract, no transaction data, nothing EtherHiding-style detections are built to catch
- Why it matters: It fixes EtherHiding's biggest weakness — a fixed, publicly known destination address — and we think it's a strong candidate to become the DPRK ecosystem's default blockchain dead-drop going forward
Malware needs a way to phone home without getting its command-and-control (C2) server blocked. Hardcode a domain or IP, and defenders can flag and kill it in minutes. So DPRK-linked operators have increasingly turned to public blockchains as dead drops — ledgers that are effectively impossible to take down, since they run on thousands of machines at once. Instead of hardcoding a C2 address, the malware looks one up on-chain at runtime.
The best-known version of this is EtherHiding. It works by sending a transaction to Ethereum's "burn address" — 0x000...dEaD, the well-known address people send tokens to when they want to destroy them — and stuffing the actual secret (a URL, sometimes a whole malicious script) into the transaction's data field, the part of a transaction that carries extra bytes alongside a payment. Google's own writeup on the technique states plainly that "the final address of these transactions is inconsequential" — only the data matters.
We've now identified a variant that inverts that design entirely.
bianira-ui and fluid-type-ui don't call a smart contract, and they don't put anything in the transaction's data field. They hide the C2 address in a place no prior public reporting describes: the recipient of an ordinary, empty transfer.
Every crypto transaction has a "from" and a "to" address — think of it like writing a check. Normally the "to" address is just wherever the money is going. Here, the "to" address isn't a real destination at all. It's a made-up address that was never meant to receive anything. Its only job is to encode data in its own bytes — read the right chunk as numbers, and it spells out the current C2 IP.
The malware's logic, once running on a victim machine:
- Look up a hardcoded attacker wallet.
- Find its most recent outbound transaction.
- Read that transaction's "to" address.
- Decode a C2 IP address directly out of the address bytes.
- Connect to that IP.
The transaction we recovered carries zero value and zero data ("input": "0x") — a completely mundane wallet-to-wallet transfer. There's no contract to flag and no payload field to fingerprint. The recipient bytes decode to 166.88.134.62, and — as an added attacker fingerprint — the trailing bytes of the same address spell out the ASCII string "helloipbot!!".
| EtherHiding | NullReceiver | |
|---|---|---|
| Where the secret lives | Transaction data field (calldata) | Transaction recipient address |
| Destination used | Fixed, public burn address (0x000...dEaD), every time |
New, disposable, made-up address every lookup |
| Calldata | Non-empty — carries the encoded payload | Empty ("input": "0x") |
| Capacity | Large — a full URL or script | Small — just enough for an IP |
| Cost | Pays gas for every payload byte | Cheapest possible transaction shape |
| Pre-existing detection hook | Yes — the burn address is a known landmark; that's how GTIG caught it | None — nothing fixed to watch before the wallet itself is already known |
Three things make NullReceiver the harder of the two to catch:
- No fixed target. EtherHiding always goes to the same burn address, for every campaign that uses it — which is exactly how GTIG caught it: watch one address, forever, and flag anything unusual sent there. NullReceiver never reuses a destination. Every lookup is a brand-new, throwaway address that's never been seen before.
- Nothing to fingerprint. A burn-address transaction carrying calldata already looks wrong on its face, before anyone decodes it — normal burn-address transfers don't carry payloads. A NullReceiver transaction carries nothing extra at all. There's no field to fingerprint, because there's no field.
- It's the cheapest transaction there is. Calldata costs gas per byte. EtherHiding pays for that. NullReceiver's transfer is completely blank, making it the cheapest, least conspicuous transaction shape on the network.
To be fair, EtherHiding still wins on raw capacity — it can smuggle a full URL or script, where NullReceiver can only encode a few bytes. They're not quite doing the same job: EtherHiding is closer to a hidden delivery channel, NullReceiver is closer to a hidden beacon. And both share one real weakness — the sending wallet gets reused across a campaign (the same attacker wallet sits behind both bianira-ui and fluid-type-ui), so once a defender identifies it, they can watch it going forward, same as they'd watch the burn address. The difference is what it takes to get to that point: the burn address is interesting to any blockchain-monitoring tool on day one, before anyone has seen the malware — it's a permanent, famous landmark. An attacker's ordinary wallet isn't interesting to anyone until an analyst has already found the malware and gone looking for it. NullReceiver doesn't hand defenders that day-one hook.
DPRK's npm tooling has a consistent pattern: every time a technique gets a name and a public writeup with IOCs, its usable lifespan drops fast, and the next sample quietly fixes exactly what got it caught. Obfuscator.io strings gave way to shuffled-array encoding, then to base91 with rotating alphabets. Hardcoded C2 domains gave way to dead-drop resolvers, then to EtherHiding. NullReceiver reads like the next rung on that same ladder — it directly patches the one property of EtherHiding that made attribution possible in the first place: a fixed, watchable destination. Given this actor's track record of incrementally hardening each piece of tradecraft once it's burned, we'd bet on NullReceiver — or something close to it — showing up as the default blockchain dead-drop in future DPRK npm campaigns, with EtherHiding phased out for anything the operators care about protecting.
npm/bianira-ui@1.27.0
npm/fluid-type-ui@2.0.8
0xa322e5f3d311d3080e6f0121063e9adc2490ef1a
0xa658863ea658863e68656c6c6f6970626f742121
166.88.134.62:443
166.88.134.62:80
I have included all malware artefacts from the bianira-ui and fluid-type-ui NPM packages in the NPM directory. The second, third and fourth stages are in the root directory:
- bianira-ui_stage1_encrypted.bin
- bianira-ui_stage2_encrypted.bin
- bianira-ui_stage3_callback.json
- fluid_stage2_encrypted.bin
- fluid_stage4_encrypted.bin
- fluid_stage4_ELF.payload
- fluid-type-ui.payload
As of this writing the fluid-type-ui package is still on NPM at https://www.npmjs.com/package/fluid-type-ui. It is a copy of a legitimate NPM package named tailwindcss-fluid-type.
Blockchain dead drops for malware keep getting harder to spot, and NullReceiver is the leanest version we've seen yet — no contract call, no payload field, just an ordinary-looking transfer with a meaningful destination address. If your detections are built around "malware talking to a smart contract" or "malware reading data out of a transaction," this is a reminder to also watch what address the money is (supposedly) going to.
We did not execute any part of these packages. All findings came from static analysis of the published npm tarballs and read-only lookups against the attacker's own public blockchain activity.
If you encounter similar packages or suspicious activity, please report them to OpenSourceMalware.com.
Stay safe out there.
Tags: #npm #dprk #lazarus #supply-chain #c2 #analysis

