Back to Intelligence

NullReceiver EtherHiding Attack: Trojanized npm Packages bianira-ui and fluid-type-ui Hide C2 IPs in Empty Ethereum Transfers — Detection and Removal Guide

SA
Security Arsenal Team
August 5, 2026
9 min read

Security researchers have identified an evolution of the EtherHiding command-and-control technique — now codenamed NullReceiver — delivered through two trojanized npm packages: bianira-ui and fluid-type-ui. Rather than hardcoding a C2 address or even resolving one from DNS, the malware decodes its C2 server IP from a fabricated destination address embedded in a completely empty Ethereum transfer. The blockchain itself becomes the dead drop resolver.

This matters for every organization with JavaScript developers, Node.js build servers, or CI/CD pipelines. npm remains the most abused package ecosystem in software supply-chain attacks, and EtherHiding variants are increasingly attractive to threat actors because blockchain infrastructure cannot be seized, sinkholed, or taken down by registrars and hosting providers. If either of these packages entered a developer workstation, a build agent, or a production container, you must assume credential theft and persistent access are in play — and respond accordingly.

There is no CVE associated with this campaign; it is malware distribution through social engineering and typosquat-style package naming, not a vulnerability in npm itself. The defensive burden falls squarely on dependency hygiene, behavioral detection, and egress monitoring.

Technical Analysis

The NullReceiver Dead Drop Resolver

Classic EtherHiding stores C2 configuration in a smart contract's storage or in transaction input data, which defenders have learned to hunt (large eth_call payloads from browser or Node contexts). NullReceiver evolves the tradecraft:

  1. A threat actor broadcasts an Ethereum transfer carrying zero value — an "empty" transaction that moves no Ether and looks innocuous on-chain.
  2. The destination ("receiver") address is fabricated. It is not a wallet anyone controls. Instead, the 20-byte address field is used as a covert data channel: the C2 IP address (and potentially port) is encoded into the address bytes.
  3. The malicious package queries a public Ethereum JSON-RPC endpoint (e.g., via eth_getTransactionByHash against a hardcoded transaction hash, or by reading recent transactions to a known sender address) and decodes the receiver field into an IP.
  4. The decoded IP becomes the live C2 endpoint for the second stage — typically an infostealer or remote access implant targeting .npmrc tokens, cloud credentials (~/.aws, ~/.azure, kubeconfigs), SSH keys, and browser credential stores on developer machines.

Why this defeats naive defenses: the Ethereum transaction has no payload data to inspect, no smart contract to flag, and no value transfer to heuristically score. The "maliciousness" only exists in the decoding logic inside the package. Blocking by contract address or input-data heuristics — techniques published against earlier EtherHiding campaigns — will not fire here.

Delivery Mechanism

  • Packages: bianira-ui, fluid-type-ui (npm registry; names mimic legitimate UI component libraries to lure installs or to be planted as transitive dependencies)
  • Execution trigger: Almost certainly npm lifecycle hooks — preinstall/install/postinstall scripts in package.json, which execute arbitrary code at npm install time with the installing user's privileges. This is the standard execution vector for npm malware and requires no victim interaction beyond installation.
  • Affected platforms: Any system running npm install on a project that directly or transitively depends on these packages — developer workstations (Windows, macOS, Linux), Docker-based builds, and CI/CD runners are all in scope.
  • Exploitation status: Confirmed in-the-wild distribution of live malicious packages. These are poisoned packages, not a theoretical technique.

Detection & Response

The strongest signals are behavioral: Node/npm processes spawning shells or unexpected children, and Node processes making outbound connections to public Ethereum JSON-RPC endpoints — something almost no legitimate build step needs to do.

Sigma Rules

YAML
---
title: NPM Lifecycle Script Spawning Shell or Script Interpreter
id: 3f8a2c41-9b6d-4e27-a1c5-7d3e9f02b8a1
status: experimental
description: Detects node/npm processes spawning shells or script interpreters, consistent with malicious npm install-time lifecycle scripts such as those used by the trojanized bianira-ui and fluid-type-ui packages.
references:
  - https://thehackernews.com/2026/08/trojanized-npm-packages-decode-c2-ip.html
  - https://attack.mitre.org/techniques/T1195/002/
author: Security Arsenal
date: 2026/08/14
tags:
  - attack.execution
  - attack.t1059
  - attack.supply_chain_compromise
  - attack.t1195.002
logsource:
  category: process_creation
  product: windows
detection:
  selection_parent:
    ParentImage|endswith:
      - '\node.exe'
      - '\npm.cmd'
      - '\npm.exe'
  selection_child:
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\pwsh.exe'
      - '\wscript.exe'
      - '\cscript.exe'
      - '\curl.exe'
      - '\certutil.exe'
  condition: selection_parent and selection_child
falsepositives:
  - Build tooling that legitimately shells out during postinstall (e.g., node-gyp, esbuild) - baseline by parent command line and package name
level: high
---
title: Node Process Outbound Connection to Public Ethereum JSON-RPC Endpoint
id: 8c1d5e72-4a3f-4b98-c2d6-5e7a9b01f3d4
status: experimental
description: Detects Node.js processes establishing connections to known public Ethereum RPC endpoints, consistent with EtherHiding/NullReceiver C2 resolution via blockchain dead drop.
references:
  - https://thehackernews.com/2026/08/trojanized-npm-packages-decode-c2-ip.html
  - https://attack.mitre.org/techniques/T1102/
author: Security Arsenal
date: 2026/08/14
tags:
  - attack.command_and_control
  - attack.t1102
  - attack.t1071.001
logsource:
  category: network_connection
  product: windows
detection:
  selection_image:
    Image|endswith:
      - '\node.exe'
      - '\npm.exe'
  selection_domain:
    DestinationHostname|contains:
      - 'mainnet.infura.io'
      - 'eth-mainnet.g.alchemy.com'
      - 'cloudflare-eth.com'
      - 'rpc.ankr.com'
      - 'eth.llamarpc.com'
      - 'ethereum.publicnode.com'
      - 'eth-mainnet.public.blastapi.io'
  condition: selection_image and selection_domain
falsepositives:
  - Legitimate Web3/blockchain development on the endpoint - restrict tuning to known Web3 dev machines
level: high

The second rule is your highest-fidelity signal. Standard application builds, test suites, and developer workflows have essentially no reason for a Node process to query Ethereum mainnet RPC endpoints. Where it fires outside of known Web3 engineering teams, investigate immediately.

KQL — Microsoft Sentinel / Defender

KQL — Microsoft Sentinel / Defender
// Hunt: Node/npm processes querying Ethereum RPC endpoints or spawning shells
// Covers both the NullReceiver blockchain resolution and install-time execution
let EthRpcEndpoints = dynamic([
    "mainnet.infura.io", "eth-mainnet.g.alchemy.com", "cloudflare-eth.com",
    "rpc.ankr.com", "eth.llamarpc.com", "ethereum.publicnode.com",
    "eth-mainnet.public.blastapi.io", "eth.drpc.org", "1rpc.io"
]);
let NodeNetEvents = DeviceNetworkEvents
| where TimeGenerated > ago(14d)
| where InitiatingProcessFileName in~ ("node.exe", "node", "npm.exe", "npm.cmd")
| where RemoteUrl has_any (EthRpcEndpoints)
| project TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine,
          RemoteUrl, RemoteIP, InitiatingProcessParentFileName;
let NodeShellEvents = DeviceProcessEvents
| where TimeGenerated > ago(14d)
| where InitiatingProcessFileName in~ ("node.exe", "node", "npm.exe", "npm.cmd")
| where FileName in~ ("cmd.exe", "powershell.exe", "pwsh.exe", "sh", "bash", "curl.exe", "wget", "certutil.exe")
| project TimeGenerated, DeviceName, InitiatingProcessCommandLine, ProcessCommandLine,
          FileName, InitiatingProcessParentFileName;
union NodeNetEvents, NodeShellEvents
| order by TimeGenerated desc

For Linux build agents ingested via Syslog, additionally hunt Syslog and CommonSecurityLog for process-command strings containing postinstall, preinstall, or the package names bianira-ui / fluid-type-ui.

Velociraptor VQL

VQL — Velociraptor
-- Hunt: Identify malicious npm packages and suspicious Node child processes
-- Artifact idea: enumerate installed packages and flag known-bad names plus install scripts

-- 1) Check for presence of the trojanized packages in common node_modules locations
SELECT FullPath, Mtime, Size
FROM glob(globs=[
  'C:/Users/*/**/node_modules/bianira-ui/package.json',
  'C:/Users/*/**/node_modules/fluid-type-ui/package.json',
  'C:/Users/*/**/node_modules/**/node_modules/bianira-ui/package.json',
  'C:/Users/*/**/node_modules/**/node_modules/fluid-type-ui/package.json'
])

-- 2) Enumerate live Node processes with shell/network-relevant children or command lines
SELECT Pid, Ppid, Name, CommandLine, Exe, Username, CreateTime
FROM pslist()
WHERE Name =~ '(?i)node|npm'
  AND (CommandLine =~ '(?i)bianira|fluid-type'
       OR CommandLine =~ '(?i)infura|alchemy|ankr\.com|llamarpc|publicnode')

-- 3) Active network connections from node processes
SELECT Pid, Name, RemoteAddr, RemotePort, Status
FROM netstat()
WHERE Name =~ '(?i)node'
  AND Status = 'ESTABLISHED'
  AND RemotePort in (443, 8545)

Verification and Removal Script

Run this on developer workstations, build agents, and CI runner images. It audits for the malicious packages, flags suspicious lifecycle scripts across all dependencies, and checks shell history for installs.

Bash / Shell
#!/usr/bin/env bash
# NullReceiver / trojanized npm package detection & removal helper
# Usage: sudo ./nullreceiver_check.sh /path/to/scan/root

ROOT="${1:-$HOME}"
BAD_PKGS=("bianira-ui" "fluid-type-ui")

echo "[*] Scanning $ROOT for trojanized npm packages..."
for pkg in "${BAD_PKGS[@]}"; do
  # Check lockfiles (package-lock.json / yarn.lock / pnpm-lock.yaml) - catches transitive deps
  grep -rl --include='package-lock.json' --include='yarn.lock' --include='pnpm-lock.yaml' \
      "\"${pkg}\"" "$ROOT" 2>/dev/null | while read -r f; do
    echo "[ALERT] $pkg referenced in lockfile: $f"
  done
  # Check installed node_modules
  find "$ROOT" -type d -name "$pkg" -path '*/node_modules/*' 2>/dev/null | while read -r d; do
    echo "[ALERT] Installed copy found: $d"
  done
done

echo "[*] Auditing all package.json files for install lifecycle scripts..."
find "$ROOT" -name 'package.json' -path '*/node_modules/*' 2>/dev/null | while read -r pj; do
  if grep -qE '"(preinstall|install|postinstall)"\s*:' "$pj"; then
    script_line=$(grep -E '"(preinstall|install|postinstall)"\s*:' "$pj")
    # Flag scripts that shell out, download, or execute remote content
    if echo "$script_line" | grep -qE 'curl|wget|bash|sh -c|node -e|powershell|eval|base64'; then
      echo "[WARN] Suspicious lifecycle script in $pj : $script_line"
    fi
  fi
done

echo "[*] If alerts were found:"
echo "    1. Remove the dependency: npm rm <pkg> && rm -rf node_modules package-lock.json"
echo "    2. Pin replacements and reinstall from a clean cache: npm cache clean --force && npm ci"
echo "    3. ROTATE all credentials on the affected host: npm tokens (~/.npmrc),"
echo "       cloud creds (~/.aws, ~/.azure, ~/.config/gcloud), SSH keys, kubeconfigs."
echo "    4. Treat the host as compromised; image or rebuild CI runners."

Remediation

Immediate (today):

  1. Search every lockfile in your estate — repos, artifact stores, container images, and build caches — for bianira-ui and fluid-type-ui. Lockfiles reveal transitive inclusion even when no developer intentionally installed the package. Remove the dependency, purge node_modules, clear the npm cache (npm cache clean --force), and reinstall from a clean, lockfile-pinned state.
  2. Rotate credentials on any affected host. Assume install-time malware harvested .npmrc auth tokens, AWS/Azure/GCP credentials, SSH private keys, kubeconfigs, and environment variables visible to the build. Rotate npm registry tokens organization-wide if any developer machine was hit — a stolen publish token converts one infection into a pipeline for attacking your own downstream consumers.
  3. Rebuild compromised CI runners from clean images. Do not attempt to "clean" a build agent that executed malicious install scripts; persistence may survive uninstallation.

Structural (this quarter):

  1. Disable or gate lifecycle scripts. Enforce --ignore-scripts in CI (npm ci --ignore-scripts), and globally via .npmrc (ignore-scripts=true), explicitly allowlisting the small set of packages that legitimately need build steps (node-gyp-based native modules). This single control neutralizes the dominant execution vector for npm malware.
  2. Deploy a private registry proxy with malware screening (e.g., an artifact repository that scans packages before caching them) and block direct public-registry access from build infrastructure.
  3. Egress filtering on build agents and developer VLANs: deny outbound access from Node/npm processes to public blockchain RPC endpoints and restrict egress to required registries and CDNs. The NullReceiver technique is dead on arrival if the implant cannot reach an Ethereum RPC node.
  4. Adopt lockfile integrity enforcement (npm ci only in CI, never npm install), enable Sigstore/npm audit signatures verification, and require two-party review for dependency additions.

Executive Takeaways

  • EtherHiding has evolved: NullReceiver proves that even empty blockchain transactions can serve as C2 dead drops, and blockchain infrastructure cannot be taken down — prevention and egress control matter more than takedown-based response.
  • The npm install-time lifecycle hook remains the single highest-value control point: --ignore-scripts in CI breaks this entire class of attack.
  • Developer workstations and CI runners are now primary breach entry points. They hold the credentials to everything else; monitor them with the same rigor as production servers.
  • Any exposure to these packages is an incident-response event, not a cleanup task — credential rotation and host rebuild are mandatory, not optional.

Related Resources

Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.