The npm package @guangnao/claude-cli, published under the @guangnao scope (six packages total), ships a single 120 KB esbuild-bundled binary that presents as a multi-identity Claude API proxy. On first run it reads OAuth credentials from ~/.claude/.credentials.json and the macOS Keychain, opens a WebSocket to an XOR-obfuscated hub at hxxps://hub[.]client-llm[.]com, uploads the stolen tokens as a “backup,” and begins accepting work dispatched by the hub operator. The connection is enabled by default, and logging for it is deliberately suppressed. Eighteen versions were published in six days (Aug 1-6, 2026), with credential exfiltration and the C2 hub present from v1.0.0.
The artifact
@guangnao/claude-cli v1.0.17 was published on Aug 6, 2026 under the npm account guangnao ([email protected]). The scope contains five other packages: @guangnao/agent-cli, @guangnao/claude-hub, @guangnao/webapi, @guangnao/agent-os, and @guangnao/agent-ui, all published between Jul 18 and Aug 6, 2026. No repository URL is listed in the package metadata.
The package ships four files: bin/claude-cli.js (119,587 bytes), package.json, README.md (Chinese-language), and an MIT LICENSE crediting “claude-clv contributors.” The binary declares "type": "module" and bundles the ws WebSocket library inline. It requires Node 20.12+ and registers a claude-cli bin entry. The README describes the hub as an opt-in compute pool. It does not mention the hardcoded default hub, the credential backup upload, the suppressed logging, the auto-update mechanism, or the macOS Keychain access.
What it does
claude-cli start opens an HTTP server on 127[.]0[.]0[.]1:8787 exposing a /v1/messages endpoint compatible with the Anthropic API. Behind the proxy, the binary reads credentials from three sources. First, it reads ~/.claude/.credentials.json and extracts the claudeAiOauth object containing accessToken, refreshToken, and expiresAt. On macOS, if the file read fails, it falls back to security find-generic-password -s "Claude Code-credentials" -w to pull the same credentials from the system Keychain. Per-identity long-lived tokens are read from ~/.claude-cli/<id>/token files. The environment variable CLAUDE_CODE_OAUTH_TOKEN is also consumed if set.
The binary refreshes expired OAuth tokens by posting grant_type: "refresh_token" to hxxps://console[.]anthropic[.]com/v1/oauth/token with client ID 9d1c250a-e61b-44d9-88ed-5944d1962f5e. Refreshed tokens are cached with a 60-second expiry buffer.
In “proxy” mode, the binary forges Claude Code session headers directly against hxxps://api[.]anthropic[.]com/v1/messages. It populates x-stainless-* tracing headers, a x-claude-code-session-id, hardcoded version strings (2.1.220 for Claude CLI, 0.94.0 for SDK, v26.3.0 for runtime), and the beta header oauth-2025-04-20,claude-code-20250219. A parity command compares forged headers against a real claude -p subprocess to verify the impersonation is accurate.
The single obfuscated value in the entire binary is the C2 hub URL. A function named _deHub XOR-decodes a base64 string using key gnP2p!7xQ:
DxokQgMbGFc5Egx+URxIUhYlSgI8X15CWBU=
The result is hxxps://hub[.]client-llm[.]com. The function defaultHubEnabled() returns true unless the user has explicitly set defaultOff: true in ~/.claude-cli/hub.json. On startup, activeHubs() silently adds the decoded hub to the connection list with builtin: true. Connections to the builtin hub suppress all console logging (...s.builtin?{}:{log:...}).
The WebSocket opens to wss://hub[.]client-llm[.]com/node. The registration message sends the node ID (a SHA-256 of the machine’s hardware UUID, salted with claude-cli/node-id/v1), the hostname via os.hostname(), the local model catalog, the concurrency capacity, and the package version. Immediately after registration, the binary uploads a credential “backup” via HTTP PUT to /node/backup/{nodeId} with header x-node-token. The backup payload is a JSON object containing the full contents of ~/.claude/.credentials.json (or the Keychain equivalent) and a map of all per-identity tokens.
The hub dispatches work as {"t": "job", "id": "<jobId>", "body": "<request>"} messages. The node forwards each job to its local /v1/messages endpoint using the victim’s credentials, then streams the response back as head, chunk, and end frames. The hub can also push credentials down to the node via the restore command, which writes received tokens to ~/.claude/.credentials.json and per-identity token files.
Device fingerprinting uses OS-specific methods: ioreg -rd1 -c IOPlatformExpertDevice for the macOS IOPlatformUUID, reg query HKLM\SOFTWARE\Microsoft\Cryptography /v MachineGuid on Windows, and /etc/machine-id on Linux.
Auto-update runs every five minutes by default. The binary fetches hxxps://registry[.]npmjs[.]org/@guangnao%2fclaude-cli/latest and, if a newer version exists, runs npm i -g @guangnao/claude-cli@latest silently. The daemon then waits for in-flight requests to drain, restarts itself, and continues. The CLAUDE_CLI_AUTO_UPDATE environment variable must be explicitly set to "off" to disable this.
The campaign
The @guangnao scope operates six packages. @guangnao/claude-hub is referenced in the README as the server-side component that receives connections from claude-cli nodes. The hub protocol supports multi-node orchestration: registration, job dispatch, response streaming, credential backup, credential restore, and keepalive. Each infected host contributes its Claude subscription capacity to the pool. The operator harvests OAuth tokens (including refresh tokens that grant persistent access) and can redistribute them across nodes via the restore command.
Version 1.0.0, published Aug 1, 2026, already contained the XOR-obfuscated C2 hub, credential harvesting, backup exfiltration, and auto-update. No version was clean. The hub was always-on from v1.0.0, with no documented way to disable it. Version 1.0.8 (Aug 6) added direct API impersonation via “proxy” mode, the parity command for verifying header forgery accuracy, multi-hub support, and the deliberate logging suppression for the builtin hub connection. The five-minute auto-update interval means the operator can deploy new capabilities to every infected host within minutes of publishing a new version.
Why the operation matters here
Removing @guangnao/claude-cli from npm disables one installation path. The hub at hub[.]client-llm[.]com retains every credential backup uploaded to it. Refresh tokens harvested through the OAuth endpoint grant persistent access that survives package removal. The five other packages in the @guangnao scope and the companion @guangnao/claude-hub server remain operational infrastructure for the same credential pool.
| Indicator | Value |
|---|---|
| Package | @guangnao/claude-cli v1.0.17 |
| npm scope | @guangnao (6 packages) |
| npm account | guangnao ([email protected]) |
| npm shasum (v1.0.17) | 8e1d4b55844157ec15ced3c3bb35db1f4cdfecd0 |
| SHA-256 (claude-cli.js v1.0.17) | bccaaac1bb4c81ad73a90b1a9096176517fd75fb5daf6551f553c8151a990735 |
| SHA-256 (claude-cli.js v1.0.0) | 6574a18474c3a4436a1982085daa8f8813f9c8567d79ffbbd8e4586af6e0d24f |
| SHA-256 (tarball v1.0.17) | 5dbf28d2691dff998a016d76dcb74dade8397c93659ff5f92cd9a88e79e636dd |
| C2 hub | hxxps://hub[.]client-llm[.]com |
| C2 WebSocket path | /node |
| C2 backup path | PUT /node/backup/{nodeId} |
| XOR key | gnP2p!7xQ |
| XOR payload | DxokQgMbGFc5Egx+URxIUhYlSgI8X15CWBU= |
| OAuth client ID | 9d1c250a-e61b-44d9-88ed-5944d1962f5e |
| OAuth endpoint | hxxps://console[.]anthropic[.]com/v1/oauth/token |
| Node ID salt | claude-cli/node-id/v1 |
| Credential file | ~/.claude/.credentials.json |
| Keychain service | Claude Code-credentials |
| Identity tokens | ~/.claude-cli/<id>/token |
| Hub config | ~/.claude-cli/hub.json |
| Auto-update target | @guangnao/claude-cli@latest |
| Forged CLI version | 2.1.220 |
| Forged SDK version | 0.94.0 |
| Forged runtime version | v26.3.0 |
| Companion package | @guangnao/claude-hub |
| First published | Aug 1, 2026 |
| Versions | 18 (v1.0.0 through v1.0.17) |
What a defender can do
Uninstall @guangnao/claude-cli globally (npm rm -g @guangnao/claude-cli). Delete the ~/.claude-cli/ directory. Revoke all Claude OAuth tokens from the Anthropic console and re-authenticate Claude Code to generate fresh credentials. On macOS, remove the Claude Code-credentials Keychain entry and let Claude Code recreate it. Check for claude-cli entries in ~/.claude-cli/hub.json pointing to hub[.]client-llm[.]com. Audit cron jobs and launch agents for claude-cli start --bg daemon entries. Search for any @guangnao scoped packages in global and project node_modules. The refresh tokens uploaded to the hub remain valid until explicitly revoked, so credential rotation is not optional.
Where Aephix fits
Before you install a CLI tool or connect to any proxy server, Aephix Vantage gives you a free, cross-ecosystem view of what is already known to be malicious, so a component with a hostile history is something you recognize before you connect. Every week, Weekly Sleuth reports the malicious packages, models, skills, MCP servers, extensions, and containers confirmed that week alongside the wider operations behind them, with a confidence level and supporting evidence, so subscribers act against the operation rather than the single artifact.