- Classification: TLP:WHITE
- Threat type: Command and Control / Dead Drop Resolver
- MITRE ATT&CK: T1102.001 (Web Service: Dead Drop Resolver)
Threat actors are storing C2 domains inside Ethereum smart contracts. The contract address is stable, the retrieval is anonymous, and there is no registrar or hosting provider to send a takedown request to. We observed this technique live in the Shai-Hulud supply chain campaign, queried the contract on mainnet, and decoded the C2 domain directly from the chain.
What is EtherHiding?
Traditional malware C2 infrastructure has a shelf life. Defenders register sinkhole domains, hosting providers pull servers, and law enforcement seizes infrastructure. The attacker loses the channel and has to rebuild.
EtherHiding sidesteps all of that.
The concept is simple. Instead of hardcoding a C2 domain in the payload, or relying on a DGA that defenders can predict, the malware calls an Ethereum smart contract to retrieve its current C2 address. The smart contract acts as a dead drop resolver, a lookup service that only the attacker can update, hosted on infrastructure that nobody can take down.
From the EVM's perspective this is a read-only call. No gas is spent, no transaction is recorded for the read, and the request blends in with millions of other eth_call RPCs hitting public endpoints every day. The malware needs no wallet and no ETH. The deployment and any domain rotation calls do leave on-chain traces, but the read path the malware uses does not.
How it works
The attacker deploys a minimal Solidity contract with a getter function that returns a string: the C2 domain, a payload URL, or any other configuration data.
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12 <0.9.0;
contract EtherHiding {
function getPayload() public pure returns (string memory) {
return "awqhnjewqjkl.icu";
}
}

That is the whole thing. A single function, a single string. The real Shai-Hulud variant stores the domain in a state variable behind an onlyOwner setter, so the attacker can update the C2 domain whenever they want by sending a transaction. The contract address stays the same, the malware never needs updating, and the domain it resolves can change at any time.
The contract's ABI is equally minimal, a single pure function returning a string.
[
{
"inputs": [],
"name": "getPayload",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "pure",
"type": "function"
}
]
We deployed a proof of concept on the Sepolia testnet so you do not have to take our word for any of this. Open the EtherHiding C2 Demo, pick the Crimson7 Demo (testnet) preset, and hit Query Contract. The tool sends the same eth_call a piece of malware would send and shows you both the raw hex and the decoded string.
- Contract:
0xb69e699e4dc0e7663bf10c519d0b4273e36c2b3f(Sepolia) - Selector:
0x2cae8ae4(getPayload()) - Verified source: Blockscout
- Query it live: crimson7research.github.io/etherhiding

The malware payload makes a JSON-RPC eth_call to any public Ethereum RPC endpoint. No authentication is required.
{
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{
"to": "0xE1f2395ee43e45A1556EC6438a88c31B83493103",
"data": "0x53ed5143"
}, "latest"],
"id": 1
}
The data field is just the 4-byte keccak256 function selector for getAll(). The response comes back ABI encoded, the malware decodes it, and it has its current C2 domain.
![Figure 3: Live query of the Shai-Hulud C2 resolver on Ethereum mainnet. The decoded string is awqhnjewqjkl[.]icu.](/images/blog/etherhiding-blockchain-c2-dead-drop-resolver/mainnet-c2-query.png)
Why defenders cannot kill it
- No takedown path. There is no registrar to contact, no hosting provider to abuse report, no DNS to sinkhole. The contract is replicated across the Ethereum network, no single party can remove or freeze it, and the attacker can update the stored domain at will.
- Domain rotation without payload updates. The attacker calls a setter function on the contract to change the C2 domain. Every infected host picks up the new address on its next check-in, with no payload update needed.
- Traffic blends in. RPC calls to endpoints like
publicnode.com,getblock.io, orinfura.ioare legitimate traffic. At the network layer the malware's requests are indistinguishable from ordinary DeFi activity. - No on-chain footprint for readers. The
eth_callmethod is a local simulation, it does not create a transaction. There is nothing to find in block explorers unless you already know the contract address.
What we observed
During analysis of the Shai-Hulud malware, the weaponized keyv@6.0.0 npm package, we identified an EtherHiding contract deployed at:
0xE1f2395ee43e45A1556EC6438a88c31B83493103
The malware rotates through a list of public Ethereum RPC endpoints so it never depends on a single gateway:
go.getblock.iosecurerpc.comrunonflux.iochain49.comtenderly.codiamondswap.orglokibuilder.xyz
At time of query (2026-08-06) the contract returned a single domain: awqhnjewqjkl[.]icu
The contract bytecode is 2,597 bytes and remained deployed as of 2026-08-06. The attacker retains the ability to rotate C2 at any time by calling the setter function, and that setter transaction would be visible on-chain, which gives defenders a monitoring opportunity.
For the full analysis of the campaign this contract belongs to, see our companion report, Shai-Hulud's Ethereum C2: Inside a Blockchain-Backed npm Supply-Chain Attack.
The selector problem
One thing worth noting for defenders doing static analysis: the malware does not contain the function name getAll() anywhere. It only contains the 4-byte selector 0x53ed5143.
If you are reverse engineering a sample and you see a hex constant like this in an eth_call, you can reverse it using public signature databases.
0x53ed5143 → getAll()
0x2cae8ae4 → getPayload()
0xa9059cbb → transfer(address,uint256)
The selector is keccak256("functionName(argTypes)") truncated to 4 bytes. Public databases that let you reverse the lookup:
- OpenChain Signature Database, openchain.xyz/signatures
- 4byte.directory, 4byte.directory
- sig.eth, sig.eth.samczsun.com
Paste a selector like 0x53ed5143 and you get back getAll(). The Selector Toolkit in our EtherHiding demo does both directions in one place: type a function name to get its selector, or paste a selector to look up the name. These databases have indexed millions of function signatures from verified contracts across every EVM chain. If the attacker used a common function name you will find it. If they used something obfuscated such as function x7f2a() it will not be in any database, but that absence is itself a red flag during analysis.

Detection opportunities
EtherHiding is hard to block, but it is not invisible.
- Monitor for unusual Ethereum RPC traffic. If
node.exeorbun.exeis making POST requests to Ethereum RPC endpoints, that is anomalous in most enterprise environments. Nobody is running DeFi from a CI server. - Watch the contract. The address is public. You can set up monitoring to alert when the stored domain changes, giving you early warning of C2 rotation.
- Detect the RPC endpoints. The malware needs some gateway to reach Ethereum. If you block or alert on the known RPC domains it cycles through, you force the attacker to find new endpoints.
- Hunt for the pattern, not the IOC. Any process making
eth_callrequests with a small, fixeddatapayload and then immediately connecting to the decoded domain is the behavioral pattern, regardless of which contract or RPC is involved.
Key takeaways
- EtherHiding gives attackers censorship-resistant, dynamically updatable C2 infrastructure at near-zero ongoing cost. This is not a theoretical technique, it is in active use in the Shai-Hulud campaign.
- You cannot neutralize this infrastructure. There is no takedown, no sinkhole, no seizure. The attacker can change the stored domain at any time, or deploy a new contract entirely. Accept that and focus on detection.
- Detect the behavior, not the IOC. The durable signal is non-DeFi processes such as
nodeandbunmakingeth_callrequests to Ethereum RPC endpoints. That pattern survives domain rotation, contract changes, and RPC endpoint swaps. - The barrier to entry is low. One Solidity contract and a few dollars in deployment gas. Other actors are likely to adopt this pattern.
IOC summary
| Type | Indicator | Context |
|---|---|---|
| Contract | 0xE1f2395ee43e45A1556EC6438a88c31B83493103 | EtherHiding C2 resolver (mainnet), stored domain may change |
| Selector | 0x53ed5143 | Function selector for getAll() |
| Domain | awqhnjewqjkl[.]icu | Active C2 domain from the contract, as of 2026-08-06 |
| RPC | go.getblock.io | Ethereum RPC endpoint used by malware |
| RPC | securerpc.com | Ethereum RPC endpoint used by malware |
| RPC | runonflux.io | Ethereum RPC endpoint used by malware |
| RPC | chain49.com | Ethereum RPC endpoint used by malware |
| RPC | tenderly.co | Ethereum RPC endpoint used by malware |
| RPC | diamondswap.org | Ethereum RPC endpoint used by malware |
| RPC | lokibuilder.xyz | Ethereum RPC endpoint used by malware |
Research conducted through passive, read-only interaction with public blockchain data. No attacker infrastructure was modified, disrupted, or otherwise interfered with. Domains are defanged per standard practice.