
Milk Sad Research Team / official analytical resource milksad.info
August 2023 · Scientific and analytical article
Abstract: This article examines CVE-2023-39910 (“Milk Sad”), a critical entropy generation flaw in Libbitcoin Explorer (bx) versions 3.0.0–3.6.0. The team bx seed generated wallet material using non-cryptographic MT19937 initialized with a 32-bit system time value. Therefore, requesting 128, 192, or 256 bits of output data did not generate the corresponding entropy: the number of achievable results was limited to no more than 2^32. This transformed recovering the original BIP39/BIP32 material from a secp256k1 cryptanalysis problem into brute-force traversal of a small generator state space followed by verification of derived addresses against the public blockchain. We discuss the code path, formal attack model, confirmed on-chain consequences, connections to Trust Wallet and Ill Bloom as a general architectural class of defects, as well as design and response measures.
1. Object and boundaries of analysis
The vulnerability affects the entropy generation mechanism in bx seed Libbitcoin Explorer 3.x. NVD lists the range of 3.0.0–3.6.0, classifies the issue as High, and estimates exploitation in a real-world environment in June–July 2023. The flaw does not affect ECDSA, BIP39, BIP32, or hardware wallets; rather, it affects an earlier stage—the generation of the initial secret.
For scientific correctness, it’s necessary to distinguish between the serialized output length and the min-entropy of the source . The command could output 256 bits and then produce a 24-word BIP39 mnemonic, but the set of possible such mnemonics remained limited by the 32-bit initialization states.
Classification
| Attribute | Meaning |
|---|---|
| Identifier | CVE-2023-39910, Milk Sad |
| Component | Libbitcoin Explorer, subcommand bx seed |
| Confirmed versions | 3.0.0–3.6.0 |
| Root cause | std::mt19937 with a 32-bit seed obtained from the clock |
| CWE | CWE-331 (insufficient entropy), CWE-334 (small random space), CWE-338 (cryptographically weak PRNG) |
| Enemy Model | A remote observer using public addresses/blockchain history as a verification oracle |
2. Cryptographic context
The HD wallet’s reliability stems from the unpredictability of the initial entropy. In a typical scheme, a 256-bit secret E is selected from a virtually complete set {0,1}^256, then encoded into a BIP39 mnemonic and converted into a BIP39/BIP32 seed. Without knowledge of the secret, enumerating such a space is computationally infeasible.
Normative model: E ← CSPRNG(256) |Supp(E)| ≈ 2^256 H∞(E) ≈ 256 bits Vulnerable model: s ← Clock() mod 2^32 E = F_MT19937(s, L) |Supp(E)| ≤ 2^32 H∞(E) ≤ 32 bits
Here F_MT19937 is a deterministic expansion of the initial state into L bytes. Pseudorandomly “stretching” 32 bits into 128–256 bits changes the representation but adds no information. The loss relative to the 256-bit ideal is 224 bits:
2^256 / 2^32 = 2^224.
This is why the correct interpretation is not “Bitcoin keys were hacked,” but “some keys never had the expected cryptographic entropy.” The elliptic curve secp256k1 remains cryptographically secure; an attacker reproduces the input to the standard’s algorithms rather than inverting a discrete logarithm.
3. Defect code path
The Milk Sad study traced the path: seed::invoke() → new_seed(bit_length) → pseudo_random_fill() → pseudo_random::get_twister(). At the last link, the stream instance std::mt19937 was created with a uint32_t high-precision clock counter.
const auto now = high_resolution_clock::now(); return static_cast<uint32_t>(now.time_since_epoch().count()); ... twister.reset(new std::mt19937(get_clock_seed()));
MT19937 is designed for statistical modeling, not secret generation. Besides predictability when revealing enough output, the 32-bit initialization input itself is fundamental to this implementation. Even if the clock has the stated high precision, converting it to 32 bits doesn’t provide more space 2^32.
A reproducible historical experiment
The Milk Sad team used the official binary release bx 3.2.0 and libfaketime, locking the time at zero. Two independent runs yielded the same 24-word BIP39 mnemonic, beginning with the words “milk sad.” This is a controlled proof of determinism: the same initial state produces the same “random” wallet.
# Demonstration only on an isolated test environment export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 export FAKETIME_FMT=%s FAKETIME=0 bx seed -b 256 | bx mnemonic-new # running it again returns the same result
Ethical restriction. This example demonstrates the reproducibility of the flaw, not a method for extracting other people’s keys. It is prohibited to generate candidates for other people’s addresses, sign transactions, or move funds that do not belong to the researcher.
4. Formal model of cryptanalysis
Let S = {0,…,2^32−1} be the seed space. For each, s ∈ S the attacker deterministically obtains entropy, mnemonics, and a set of derived public addresses of standard paths. The blockchain acts as a verifiable public oracle: a match with a previously used address is confirmed by the candidate.
E_s = F_MT19937(s,L) M_s = BIP39_Encode(E_s) K_s = BIP32_Derive(PBKDF2(M_s, passphrase), path) A_s = Address(Pub(K_s)) Test: A_s ∈ A_chain ?
If the user hasn’t added a strong, independent BIP39 passphrase, an address match provides the attacker with the same source material as the owner. If a passphrase is used and has high entropy, it forms an additional secret factor; a weak, dictionary, or reusable passphrase doesn’t provide comparable protection.
Why is this practical?
- The full upper limit is 4,294,967,296 seeds, which is incomparably less
2^128or2^256. - The wallet creation time window often narrows the search even further, although this is not necessary for the attack itself.
- Addresses and transactions are public, so candidate verification does not require interaction with the owner.
- Standardized derivation paths (BIP44, BIP49, BIP84) make verification predictable.
Milk Sad noted several practical output options and paths, so the actual verification is slower than a “one-pass” test 2^32, but still feasible on a timescale of days rather than astronomical timeframes.
5. Confirmed on-chain damage
The researchers linked the main cluster on July 12, 2023, to three transactions that collectively moved approximately 29.65 BTC, or over $850,000 at the August 2023 exchange rate. Taking into account related episodes and other networks, they provided a lower estimate of over $900,000; the authors emphasized that some of the movements could have been caused by other vulnerabilities.
| Date/observation | Scientific significance |
|---|---|
| May 3, 2023 | The onset of an early suspicious pattern of small “sweep” transactions. |
| July 12, 2023, 10:41 AM | Three large Bitcoin transactions, approximately 29.65 BTC; hundreds or more than a thousand inputs in separate transactions. |
| July 21, 2023 | Discovery of a defect during an incident investigation. |
| August 7, 2023 | CVE assignment. |
| August 8, 2023 | Milk Sad technical disclosure publication. |
In a limited study of BIP44/BIP49/BIP84 Bitcoin addresses and only the first address, the team identified over 2,600 wallets used based on weak entropy. This is not an estimate of all victims: the method intentionally did not cover all coins, paths, address indexes, and possible exploits bx.
A real-life example from an investigation
The initial victims used air-gapped Linux, custom software, a 24-word BIP39 mnemonic, Ledger/Trezor, and physical backup protection. The simultaneous theft of BTC, ETH, and other assets demonstrated that the threat existed before the mnemonic was imported into the hardware wallet: the hardware device securely stored the already compromised root secret.
6. Comparison with related cases
Milk Sad is one example of a recurring anti-pattern: a non-cryptographic PRNG is included in the creation of long-lived secrets. The analogy with Ill Bloom lies in the risk class—the collapse of the initial entropy to an enumerable space; however, CVE-2023-39910 should be analyzed based on its own implementation, version, and transformation chain, rather than transferring metrics between incidents without verification.
| Happening | General cryptanalytic fact | An important distinction |
|---|---|---|
| Milk Sad / bx | MT19937 and the finite small space of initial states. | Initialization with 32-bit clock value; bx output options. |
| Trust Wallet CVE-2023-31290 | Using MT19937 for wallet entropy and practical brute force. | Another way to extract bytes from a 32-bit output: Trust Wallet has the low bits, bx has the high bits; the mnemonic sets are not identical. |
| Cake Wallet (2021) | Risk of system time fallback in a non-cryptographic generator. | A different ecosystem and implementation; a prime example of a weak seed source. |
| Ill Bloom | Architectural class: Predictable/low entropy destroys wallet security before cryptography is applied. | CVEs and exploitation methods should not be considered identical without separate auditing. |
7. Design errors
- Mixing a PRNG and a CSPRNG. The statistical properties of MT19937 do not imply cryptographic unpredictability.
- Entropy substitution by length. This parameter
-b 256specified the result size but did not provide 256 bits of min-entropy. - An implicitly dangerous API. Documentation and popular examples suggested it
bx seedas a wallet generation feature; a weak warning doesn’t compensate for the unsafe default behavior. - Insufficient security engineering. The cryptographic API should not silently generate irreversibly weak keys when a system-wide CSPRNG exists.
- Difficulty of notification. Exported BIP39 phrases do not carry origin metadata, making it impossible to reliably locate and notify all owners.
8. Recommendations
For developers
- Obtain key entropy exclusively from a CSPRNG OS: for example,
getrandom(),getentropy(),BCryptGenRandomor a proven high-level crypto library. - Clearly separate APIs for modeling and cryptography; types and names should prevent erroneous use
pseudo_randomfor secrets. - Add property tests: the same controlled time should not make the generation of key material reproducible.
- Ban or mark as dangerous legacy commands that release secrets from non-CSPRNGs; provide a migration path.
- Conduct an independent audit of all paths: entropy → mnemonic → seed → master key → derived addresses.
For wallet owners
- If a mnemonic or BIP32 seed was created using a vulnerable
bx seed3.x, consider it irreversibly compromised. - Create a new wallet using current, verified software or a hardware device that generates entropy locally using a CSPRNG; transfer assets to new addresses.
- Do not continue to use old addresses and do not accept new payments to them.
- If a BIP39 passphrase is present, evaluate it as a separate secret, but do not consider it a replacement for migration; a weak passphrase is selectable.
- Preserve evidence, logs, and transaction identifiers for forensic and legal advice; do not attempt to “rescue” other people’s funds.
9. Conclusion
CVE-2023-39910 is cryptanalytically significant not because it weakens secp256k1, but because it demonstrates the absolute dependence of applied cryptography on the quality of secret generation. Substituting 256-bit entropy with a deterministic function of 32-bit time reduced the security to an enumerable set and allowed candidates to be matched against publicly available blockchain traces.
Milk Sad’s practical lesson is universal: key materials should only come from CSPRNGs, and security should be measured not by output size or algorithm name, but by min-entropy, seed origin, and the adversary’s ability to reproduce the entire pipeline.
Sources
Milk Sad: CVE-2023-39910 – vulnerability disclosure and technical analysis: https://24bitcoin.ru/milk-sad-cve-2023-39910-vulnerability-disclosure-and-technical-analysis/
- Milk Sad Research Team. Milk Sad: home — official resource and current research materials.
- Milk Sad Research Team. CVE-2023-39910 Summary – a concise description of the mechanism, impact, and search scope.
- Milk Sad Research Team. Technical write-up , published August 8, 2023 – initial technical disclosure, code path, timeline, and on-chain analysis.
- NIST NVD. CVE-2023-39910 — CVE card and description of affected versions.
- Libbitcoin Explorer. CVE-2023-39910 wiki — project support materials.
