Worked example · 2023-08
Milk Sad — bx seed documents 128–256 bits of entropy and delivers 32, because Mersenne Twister is seeded on the system clock: same second, same "random" wallet — Libbitcoin Explorer (CVE-2023-39910) — theft 2023-07-12, disclosed 2023-08-08
Summary
Libbitcoin Explorer (bx) is a widely used Bitcoin command-line tool. Its bx seed command generates wallet seeds and documented itself as producing 128 to 256 bits of entropy.
It did not. bx seed used the Mersenne Twister (MT19937) PRNG seeded with 32 bits of system clock time. Effective entropy was therefore 2^32 — about 4.3 billion possible seeds — rather than the advertised 2^128–2^256. An attacker can enumerate that entire keyspace in days on consumer hardware, deriving every wallet the tool ever produced.
The researchers demonstrated the defect directly: running bx seed under a fixed clock using libfaketime produced identical mnemonics. Same second, same wallet.
The vulnerability affects bx versions 3.0.0 through 3.6.0, with potential exposure in earlier 2.x versions, covering seeds generated from roughly 2017 to August 2023. Because the output is a standard BIP-39 mnemonic, the weakness propagates to every chain derived from it — Bitcoin, Ethereum, XRP, Dogecoin, Solana, Litecoin, Bitcoin Cash, Zcash and others.
Exploitation ran through June and July 2023, probably beginning at small scale in May 2023. The principal theft on 2023-07-12 took approximately 29.65 BTC (~$850,000), and bx users were drained alongside other weak-wallet cohorts in the same coordinated action. Confirmed impact reached 2,600+ distinct Bitcoin wallets and $900,000+ across chains.
The defect was found on 2023-07-21, during incident response to the theft, by a team led by Distrust LLC. Coordinated disclosure with the Libbitcoin maintainers ran 2023-07-22 to 2023-08-05; CVE-2023-39910 was assigned on 2023-08-07; public disclosure followed on 2023-08-08.
Timeline (UTC)
| When | Event | OAK ref |
|---|---|---|
| ~2017 | bx 3.0.0 line in circulation; bx seed uses Mersenne Twister seeded on 32 bits of system clock time while documenting 128–256 bits of entropy |
T11.004 defect standing |
| 2017 → 2023-08 | Six years of seed generation at 2^32 effective keyspace across every chain derivable from the mnemonic | (standing exposure) |
| ~2023-05 | Exploitation likely begins at small scale | T5.001 |
| 2023-06 → 2023-07 | Ongoing exploitation in the wild | T5.001 |
| 2023-07-12 | Principal coordinated theft: bx wallets drained alongside other weak-wallet cohorts |
T11.004 → T5.001 |
| 2023-07-21 | Distrust LLC team discovers the root cause during incident response | (discovery) |
| 2023-07-22 → 2023-08-05 | Coordinated disclosure with the Libbitcoin team | (disclosure process) |
| 2023-08-07 | CVE-2023-39910 assigned | (CVE) |
| 2023-08-08 | Public disclosure published; 2,600+ affected wallets and $900,000+ losses documented | (public disclosure) |
What defenders observed
- Pre-event (a documented entropy claim nobody tested). The command advertised 128–256 bits and delivered 32 for approximately six years. Documentation is not a control. Generators should be tested for the property they claim, and the test here is trivial: fix the clock, generate twice, compare outputs (M23).
- Pre-event (Mersenne Twister anywhere near key derivation is the finding). MT19937 is a simulation-grade PRNG with no cryptographic guarantees, and is trivially predictable from observed output. Its presence in a seed path is a defect independent of the seeding question, and a grep-level review item for any wallet codebase.
- Pre-event (the same mistake, independently, twice). CVE-2023-31290 (Trust Wallet) is the identical Mersenne Twister misuse in a different product, discovered earlier, with a different affected population purely because the two consumed PRNG output differently. That two unrelated teams reached the same wrong primitive suggests the failure is one of default library ergonomics — the convenient generator is the wrong one — rather than of any single team's competence.
- At-event (multi-chain blast radius from one mnemonic). A weak BIP-39 mnemonic compromises every chain derived from it. Users who generated with
bxfor Bitcoin and reused the mnemonic for Ethereum or Solana lost across all of them. Incident scoping for this class must enumerate derivation paths, not addresses on one chain. - At-event (portfolio sweeping across weak-wallet cohorts).
bxwallets were drained in the same coordinated action as other weak-key classes, which indicates an operator maintaining a standing inventory of known-weak key populations and sweeping them together. That behaviour makes disclosure timing genuinely dangerous — publication adds a new cohort to an existing pipeline. - Response (found during incident response, not by review). The defect surfaced only because someone investigated a theft already in progress. Six years of code review, packaging, and use by security-literate operators did not surface it, which is the recurring property of this class.
Public references
[milksaddisclosure2023]— Milk Sad research team, "Milk Sad — full disclosure write-up" (the primary technical source: CVE-2023-39910,bx3.0.0–3.6.0, Mersenne Twister MT19937 seeded with 32 bits of system clock time against a documented 128–256 bits, thelibfaketime"same time — same 'random' wallet" demonstration, 2,600+ affected wallets, $900,000+ losses, the 2023-07-12 ~29.65 BTC theft, the full 07-21 discovery → 08-07 CVE → 08-08 disclosure timeline, the multi-chain scope, the CVE-2023-31290 Trust Wallet parallel, and attribution of the research to Distrust LLC — Anton Livaja, Lance Vick, Ryan Heywood, Shane Engelman): https://milksad.info/disclosure.html[milksadsummary2023]— Milk Sad research team, "bxsummary" (condensed CVE-2023-39910 summary and affected-version matrix): https://milksad.info/cve_2023_39910_summary.html[ghsamilksad2023]— GitHub Security Advisory GHSA-prgj-h7jq-7p9h (the advisory record for the Libbitcoin Explorer weak-entropy vulnerability): https://github.com/advisories/GHSA-prgj-h7jq-7p9h[nobsbitcoinmilksad2023]— NOBSBitcoin, "Milk Sad: Wallet Theft Enabled By Weak Entropy" (independent coverage of the disclosure and its practical implications for users): https://www.nobsbitcoin.com/milk-sad-vulnerability-disclosure/[invdmilksad2023]— invd blog, "Milk Sad — How Weak Entropy can Ruin Your Savings (CVE-2023-39910)" (researcher-side write-up of the defect and its exploitation): https://blog.inhq.net/posts/milk-sad-vuln1/
Discussion
Milk Sad enters OAK as a backfill prompted by the Coldcard incident of 2026-07/08, which public analysis identified as the third major failure of this class after Milk Sad and Ill Bloom. The corpus held none of the three; it now holds all of them, and the value is in reading them together at examples/2026-07-coldcard-firmware-rng-fallback-entropy-collapse-sweep.md and examples/2026-05-ill-bloom-mobile-wallet-weak-prng-recovery-phrase-sweep.md.
The three share a structure that is now specific enough to state as a prediction rather than an observation. The defect is introduced quietly, in a migration, a library default, or a convenience call. It persists for years — Milk Sad roughly 2017 to 2023, Coldcard 2021 to 2026 — because nothing in normal operation distinguishes a weak seed from a strong one. It is found by an outsider, usually during incident response, not by the vendor and not by review. And it is unfixable in place: patched software generates good keys going forward and does nothing whatsoever for keys already produced, so remediation is always a migration and the affected population always includes people who will never hear about it.
Milk Sad's distinct contribution to the cohort is the documentation gap. Coldcard's flaw was a silent regression from a correct design; bx seed's was a generator that advertised a specific entropy figure it never provided, in its own manual, for six years. That makes it the cleanest argument in the corpus for treating stated cryptographic properties as testable assertions rather than specifications, and the libfaketime method is the cheapest verification technique OAK documents anywhere — pin the clock, generate twice, compare. It would have taken minutes and saved 2,600 wallets.
The CVE-2023-31290 parallel deserves to stay attached to this entry. Trust Wallet reached the identical Mersenne Twister misuse independently, and the two cases differ in affected population only because of how each consumed the generator's output. When the same wrong primitive appears in unrelated codebases, the useful conclusion is about the ecosystem's defaults rather than about either team — and it suggests that a survey of seed-generation paths across wallet software for general-purpose PRNGs would still be worth running today.