
Lehrreiche Studie zur Bitcoin-Schlüsselentropie: Enumeration von Wallets mit schwachem RNG (Klasse Milk Sad / CVE-2023-39910) im Vergleich zur Undurchführbarkeit des Brute-Forcing eines guten Schlüssels.
Eine lehrreiche Studie darüber, woher die Sicherheit von Bitcoin-Schlüsseln tatsächlich kommt — und woher nicht. Aufgebaut rund um echte BIP39/BIP32-Ableitung, mit zwei Experimenten, die an entgegengesetzten Enden derselben Idee sitzen:
| Experiment | Suchraum | Machbar? | Was es lehrt |
|---|---|---|---|
weak_entropy — einen Wallet-Raum mit schwachem RNG enumerieren | 2³² (ein schlechtes RNG) | Ja, in Tagen | Ein Wallet ist nur so stark wie die Entropie dahinter. |
blind_search — einen guten Schlüsselraum per Brute-Force durchsuchen | 2¹⁶⁰ (ein gutes RNG) | Nein, niemals | Mit echter Entropie ist der Schlüsselraum physikalisch nicht durchsuchbar. |
Der springende Punkt ist der Kontrast: Schlüssel werden gestohlen, indem man schlechte Zufälligkeit angreift, niemals, indem man gute Zufälligkeit rechnerisch überwindet. Beide Experimente leiten dieselbe Art von echten Mainnet-Adressen ab, sodass die einzige Variable die Entropie ist.
Dieses Repository dient der defensiven Ausbildung und Sicherheitsforschung: Es geht darum zu verstehen, warum Wallet-Bugs mit schwachem RNG (Milk Sad / CVE-2023-39910, der Bug der Trust-Wallet-Erweiterung von 2018 und ähnliche) katastrophal waren — und warum das Brute-Forcen eines korrekt erzeugten Schlüssels es nicht ist.
weak_entropy enumeriert einen in sich geschlossenen synthetischen Raum. Seine weak_entropy()-Funktion ist ein bewusstes Modell der Schwachstellenklasse — ein 32-Bit-PRNG, der BIP39 speist — kein bytegenauer Klon des RNG eines echten Wallets. Die Adressen, die es erzeugt, entsprechen keinem echten Wallet irgendeiner Person.blind_search ist eine schreibgeschützte Guthaben-Abfrage gegen deinen eigenen Node. Es leitet keine Schlüssel ab und kann nichts ausgeben; sein Hauptmodus misst die Sinnlosigkeit der Blindsuche.Verwende es auf Wallets, die du kontrollierst, im Testnetz oder als Sandbox. Richte es nicht auf das Geld anderer Leute.
Nur für Bildungszwecke und autorisierte Sicherheitsforschung. Verwende es ausschließlich auf Schlüsseln, Wallets und Systemen, die du besitzt oder für die du die ausdrückliche schriftliche Erlaubnis zum Testen hast. Unbefugter Zugriff auf die Wallets oder Systeme anderer sowie der Diebstahl digitaler Vermögenswerte sind nach Gesetzen wie dem US-amerikanischen CFAA (18 U.S.C. § 1030), dem britischen Computer Misuse Act 1990 und der EU-Richtlinie 2013/40/EU illegal — unabhängig von den technischen Mitteln. Bereitgestellt "wie besehen", ohne Gewährleistung; die Autoren übernehmen keine Haftung für Missbrauch. Dies ist keine Rechtsberatung. Vollständige Bedingungen und Hinweise zur verantwortungsvollen Offenlegung: DISCLAIMER.md.
Die Sicherheit eines Wallets wird durch die Entropie begrenzt, die in seinen Seed eingeflossen ist, nicht durch die Stärke von SHA-256 / PBKDF2 / secp256k1 in den nachgelagerten Schritten.
BIP39 wandelt Entropie in ein Mnemonic um, PBKDF2 streckt es zu einem Seed, und BIP32 leitet einen Baum von Schlüsseln ab. All das ist stark. Aber nichts davon fügt Entropie hinzu — wenn der Seed aus einer kleinen Menge gezogen wurde, ist jede Adresse, die das Wallet jemals erzeugen kann, aufzählbar, und keine noch so große Menge an Hashing verbirgt das.
bitcoin-security/
├── bitcoin_security/ # the package
│ ├── derivation.py # shared core: BIP39 + BIP32 + P2PKH/WIF
│ ├── data/english.txt # BIP39 English wordlist (2048 words)
│ ├── weak_entropy/ # Experiment 1 — enumerable weak space
│ │ ├── enumerate.py # recovery demo + full-space dump
│ │ └── README.md
│ └── blind_search/ # Experiment 2 — futile brute-force search
│ ├── scan.py # UTXO-set balance scanner + odds
│ └── README.md
├── tests/ # offline test suites (no node/network)
│ ├── test_blind_search_addresses.py
│ └── test_blind_search_pipeline.py
├── docs/FINDINGS.md # benchmarks + exact full-space size derivation
├── pyproject.toml
├── requirements.txt
└── LICENSE
Die Struktur spiegelt die Idee wider: ein gemeinsamer derivation-Kern, zwei Experimente, die ihn nutzen, um zu gegensätzlichen Schlussfolgerungen über denselben Adressraum zu gelangen.
pip install -r requirements.txt # runtime deps
# optional — installs the package + `bsec-enumerate` / `bsec-scan` commands:
pip install -e .
coincurve (natives libsecp256k1) macht die Ableitung ~5× schneller, ist aber optional — derivation.py fällt auf reines Python-ecdsa mit identischer Ausgabe zurück. Nichts hiervon berührt das Netzwerk, außer dem optionalen --check-balance-Flag und blind_search (das einen lokalen Bitcoin-Node benötigt).
Alles läuft als Modul aus dem Repository-Stammverzeichnis; keine Installation erforderlich:
python3 -m bitcoin_security.weak_entropy --help
python3 -m bitcoin_security.blind_search --help
weak_entropy: Schwache Entropie ist aufzählbarDie "zufälligen" 16 Bytes BIP39-Entropie des Wallets stammen tatsächlich aus einem 32-Bit-Seed. Die vollständige Ableitung ist real und gegen Spezifikationsvektoren verifiziert.
seed32 ──MT19937──▶ 16-byte entropy ──BIP39──▶ mnemonic ──PBKDF2(2048)──▶ 64-byte seed
│
address ◀──hash160──── pubkey ◀──BIP32 m/44'/0'/0'/0/0
# Recovery: derive a victim wallet from a weak seed, then recover its full
# mnemonic + private key knowing only the public address.
python3 -m bitcoin_security.weak_entropy
# Expand one seed into every form.
python3 -m bitcoin_security.weak_entropy --show 3571
# Materialize the whole 2**32 space to sharded, resumable files.
python3 -m bitcoin_security.weak_entropy --dump-all --out-dir space_dump --max-file-mb 1000
Die Zeilen sind minimal gehalten (address <TAB> seed32), da jede andere Form eine deterministische Funktion von seed32 ist, die bei Bedarf mit --show neu erzeugt wird. Die Dumps sind auf Shard-Ebene fortsetzbar (verifiziert, um eine zusammenhängende, lückenlose, duplikatfreie Abdeckung zu reproduzieren). Die vollständige Flag-Referenz findest du in weak_entropy/README.md.
Größenwarnung. Der vollständige 2³²-Dump ist ~196 GB (minimal) oder ~1.05 TB (
--full) und dauert selbst mit dem nativen Backend Tage — siehedocs/FINDINGS.md. Du brauchst fast nie das Ganze; dump einen begrenzten Ausschnitt zur Inspektion.
blind_search: Einen guten Schlüssel kann man nicht per Brute-Force knackenEin echter, funktionierender Guthaben-Scanner über den UTXO-Satz eines lokalen Nodes (Pruning ist in Ordnung) via scantxoutset. Sein Zweck ist das Null-Ergebnis: den Adressraum blind durchsuchen, messen, dass nichts gefunden wird, und die Rechnung ausgeben, die zeigt, dass das immer so sein wird.
python3 -m bitcoin_security.blind_search --odds-only # just the math, no node
python3 -m bitcoin_security.blind_search --random 200000 --odds # search 200k random addrs, then the math
python3 -m bitcoin_security.blind_search --control # verify the pipeline finds known-funded addrs
Das Ergebnis: Der Raum ist 2¹⁶⁰ ≈ 1.46×10⁴⁸; ~55M Adressen weisen ein Guthaben auf; eine zufällige Adresse ist also mit einer Wahrscheinlichkeit von ~3.8×10⁻⁴¹ finanziert. Selbst bei einer Milliarde Adressen pro Sekunde würde man ~8×10²³ Jahre warten — etwa 61 Billionen Mal das Alter des Universums. Kein Hardware-Problem: Nach der Landauer-Grenze übersteigt das bloße Zählen bis 2¹⁶⁰ die verbleibende Energieabgabe der Sonne. Node-Einrichtung in blind_search/README.md.
Beide Experimente leiten dieselbe Art von Adresse ab. Der einzige Unterschied ist die Größe der Menge, aus der der Seed gezogen wurde:
Diese Lücke — 128 Bits — entscheidet alles. Jeder reale Diebstahl dieser Klasse fand in ihr statt.
python3 tests/test_blind_search_addresses.py # BIP-173/350/380 address vectors
python3 tests/test_blind_search_pipeline.py # scan pipeline, RPC stubbed
python3 -m bitcoin_security.derivation # derivation self-test (spec vectors)
Vollständige Details, Methoden und Maschinenspezifikationen in docs/FINDINGS.md.
ecdsa durch coincurve (libsecp256k1) reduzierte die Ableitung pro Wallet von 3.02 ms → 0.591 ms (~5.1×) auf einem Apple M3 Max; die verbleibende Untergrenze ist PBKDF2-HMAC-SHA512 ×2048 mit 0.463 ms/Wallet.--full, aus einer exakten geschlossenen Form für die seed32-Ziffern plus einer gemessenen mittleren Adresslänge.bx seed (32-Bit-MT19937, mit der aktuellen Zeit geseedet). Echte Gelder wurden gestohlen. https://milksad.info/MIT — siehe LICENSE. Nutzungsbedingungen und Hinweise zur verantwortungsvollen Offenlegung: DISCLAIMER.md. Sicherheitsmeldungen: SECURITY.md.