# bittensor-wallet 4.0.2 Compromised on PyPI - Backdoor Exfiltrates Private Keys

Malicious bittensor-wallet 4.0.2 PyPI release backdoored Rust keyfile decryption to exfiltrate Bittensor private keys via HTTPS, DGA, and DNS tunneling.

- Payout: Real-world hack
- Severity: critical
- Program: bittensor-wallet
- Analysis by: StepSecurity
- Disclosed: 2026-03-26
- Write-up quality: 8/10
- Original: https://www.stepsecurity.io/blog/bittensor-wallet-4-0-2-compromised-on-pypi---backdoor-exfiltrates-private-keys
- Library page: https://bugbountylibrary.com/r/blog%3Astepsecurity-io-blog-bittensor-wallet-4-0-2-compromised-on-pypi-backdoor-exfiltrates-private-keys
- Tags: area:oss domain:oss impact:data-leak impact:funds-loss kind:incident platform:threat-intel sev:critical tech:python tech:rust technique:oob-exfil technique:reverse-engineering technique:source-review vuln:dependency-confusion vuln:secrets-leak

**Target:** PyPI package bittensor-wallet (Rust-backed Python wallet library), src/keyfile.rs decryption path
**Root cause:** Attacker injected Rust code into the PyPI source tarball (not the GitHub repo) hooking collect_format_metrics into all keyfile decryption call sites, exfiltrating NaCl-sealed private keys; release workflow attestation was stripped to hide provenance.
**Steps:**
1. `pip install bittensor-wallet==4.0.2` (tarball SHA256 6a416b72ff24804abc12484a3b41413a8580acedd8a5f8c84224fcf0732c2f8e)
2. Trigger any wallet operation that decrypts a keyfile (stake, sign, balance)
3. Backdoor runs is_monitored(): aborts if uptime<1200s, TracerPid!=0, or strace/gdb/wireshark/frida/tcpdump/bpftrace in /proc/*/comm
4. detect_wallet_type walks Python frames via pyo3 to tag payload 'c' (coldkey) or 'h' (hotkey)
5. Payload sealed with attacker NaCl pubkey daebc8f33fd79a8ed65bd4383280cab1...
6. Dedup via SHA256 HashSet (cap 1024) to avoid re-sending
7. Exfil Method A: urllib POST to finney.opentensor-metrics.com/t etc. in non-daemon thread
8. Method B: dlopen libssl.so, raw TLS POST with UA Python/3
9. Method C: hex chunks as A queries `<hex>.<idx>.<total>.<session>.t.opentensor-cdn.com`
10. C2 resolution: hardcoded XOR(0x5A) domains, DGA sha256(day+i) under *.opentensor-cdn.com, DNS TXT _dmarc.opentensor-cdn.com
11. Failed sends queued (64) and retried by thread named cache-gc every 2-10min with jitter
12. Buffer zeroed with write_volatile after send
**How found:** Socket automated scanning flagged the release; write-up is a source-tarball diff of 4.0.1 vs 4.0.2 plus dynamic run under Harden Runner, revealing the stripped attestation step in release.yml and the injected collect_format_metrics calls.
**Exploitation:** Attacker published a trojanized PyPI release; on any keyfile decryption the native Rust code sealed the raw private key JSON to the attacker's NaCl pubkey and exfiltrated it over three redundant channels (HTTPS, raw TLS, DNS tunneling) with DGA/TXT C2 fallback. Attacker decrypts, imports key, drains staked TAO.
**Escalation:** Three independent exfil channels plus three C2 resolution layers (hardcoded, DGA, DNS TXT) ensure delivery even if one path is blocked; anti-analysis checks evade sandboxes.
**Insight:** Diff PyPI sdist against the upstream GitHub tag — this backdoor lived only in the published artifact, and the release workflow's attestation step was removed to hide it. Also watch for non-daemon threads spawned at crypto operations and DNS queries to _dmarc.* subdomains of lookalike domains.
