
A cryptanalytic analysis of the fundamental entropy flaw in the Libbitcoin Explorer wallet generator, which resulted in the systemic compromise of over 227,200 Bitcoin addresses.
1. Introduction: Problem Statement
The security of the Bitcoin cryptographic system is based on the assumption that the elliptic curve discrete logarithm problem (ECDLP) over a set of points on the secp256k1 curve is computationally difficult. However, the mathematical perfection of the ECDSA scheme itself is irrelevant if the fundamental condition for correct operation—the quality of the randomness (entropy) source used to generate the private key—is violated.
A violation of this condition was the cause of the vulnerability CVE-2023-39910 , codenamed “Milk Sad,” discovered in Libbitcoin Explorer ( bx) versions 3.0.0–3.6.0. The official disclosure report was published by the research group Distrust on August 8, 2023.
2. Theoretical foundation: entropy and cryptographic security
A Bitcoin private key is a random integer k that must satisfy the condition:
where n is the order of the group of points of the curve secp256k1:
The public key is calculated as the scalar product of the generator point G and the private key:
The secp256k1 curve itself is given by the Weierstrass equation:
According to Claude Shannon’s information theory, the measure of uncertainty of a random variable is determined by entropy:
For a 256-bit Bitcoin private key, the required maximum entropy is: bits
Definition 1 (cryptographically secure PRNG). A pseudorandom number generator is called cryptographically secure (CSPRNG) if, given the first k output bits, it is impossible to predict bit (k+1) in reasonable time with probability greater than , Where – negligible value.
A historical example. A similar entropy violation principle has previously led to disasters: in 2013, a weak Android SecureRandom generator led to massive nonce collisions in ECDSA Bitcoin wallet signatures (Android bug, CVE-2013-7372), allowing private keys to be calculated by repeating the k value in the signature formula.
3. Vulnerability discovery timeline
On July 21, 2023, an affected user discovered the complete loss of funds from their hardware wallet, despite strict security measures: wallet generation occurred on an air-gapped Linux laptop, a 24-word BIP-39 seed was used, and the seed never left the offline environment. Investigation revealed that at least two independent victims used the same command bx seed -b 256 | bx mnemonic-new from Libbitcoin Explorer 3.x to generate the wallet.
| Date | Event |
|---|---|
| 2022-11-17 | Ledger Donjon reveals Trust Wallet vulnerability (CVE-2023-31290) – a prototype for a similar Mersenne Twister issue |
| 2023-05-03 | The first suspicious thefts from “weak” bx wallets |
| 2023-07-12 | The main wave of thefts (~29.65 BTC, over $850,000) |
| 2023-07-21 | Discovery of the bx vulnerability by a team of independent researchers |
| 2023-08-07 | MITRE assigns CVE-2023-39910 |
| 2023-08-08 | Distrust’s full technical report (Milk Sad) is released |
4. Anatomy of a Vulnerability: MT19937 as a Source of Entropy
The Mersenne Twister (MT19937) is a pseudorandom number generator developed by Matsumoto and Nishimura in 1997. Its characteristics are presented below.
| Parameter | The meaning of MT19937 |
|---|---|
| Period | (Mersenne number) |
| Word size | 32 bits |
| Internal state size | 624 × 32 = 19968 bits |
| Seed size (in standard implementation) | 32 bits |
| Uniformity (k-distribution) | 623-dimensional uniformity |
Despite its excellent statistical properties (Diehard, TestU01), MT19937 is categorically unsuitable for cryptography: its internal state is completely reconstructed from 624 consecutive outputs, which makes the algorithm completely linear and predictable.
4.1 Vulnerable Libbitcoin Explorer Code
According to the official disclosure report, the call bx seed results in the following chain of functions in the libbitcoin-system source code (file pseudo_random.cpp):
// Using the system clock for seeding
const auto get_clock_seed = []() NOEXCEPT
{
const auto now = high_resolution_clock::now();
return static_cast<uint32_t>(now.time_since_epoch().count());
};
if (twister.get() == nullptr)
{
// Seeding via high resolution clock (32 bits!)
twister.reset(new std::mt19937(get_clock_seed()));
}
The key problem is that even if the user requests 256 bits of entropy with the command bx seed -b 256, the actual number of independent bits is limited by the 32-bit seed obtained from the system clock.
A real-world experimental fact from the Distrust report. Using a utility libfaketime to lock the system time to FAKETIME=0, the researchers executed the official binary twice bx-3.2.0 and obtained an absolutely identical mnemonic phrase:
$ bx seed -b 256 | bx mnemonic-new milk sad wage cup reward umbrella raven visa give list decorate bulb gold raise twenty fly manual stand float super gentle climb fold park
It was this phrase (“milk sad…”) that gave the name to the entire vulnerability.
5. Formal model of the RingSide Replay Attack
Let’s introduce the notation: E is the desired 256-bit entropy; S is the seed MT19937, ; f(S) is the function for generating the MT19937 sequence from seed S ; — truncation to the first 32 bytes; bip39(E) — entropy-to-mnemonic conversion; derive(m) — BIP-32 private key derivation; addr(k) — set of Bitcoin addresses derived from key k .
Definition 2 (Milk Sad vulnerability). such that
Probability of successful recovery with a complete enumeration of the space :
Because , the probability of a false address match is negligible, so when finding the correct seed .
5.1 BIP-39/BIP-32 Deterministic Chain
5.2 Quantitative assessment of attack effectiveness
This means that the actual search space is reduced by 87.5% relative to the theoretically safe level of 256 bits.
6. Computational complexity of the attack
| Equipment | Search speed | Full enumeration time |
|---|---|---|
| CPU (typical gaming PC, according to Distrust) | ~10^6 seeds/sec | several days |
| GPU (RTX 3090/4090 class) | ~10^8 seeds/sec | tens of seconds – minutes |
| Known creation date (narrowed down to 1 day) | 86,400 values | seconds |
Reducing space with a known wallet creation date: times
7. Real consequences: damage statistics
| Parameter | Meaning |
|---|---|
| CVE ID | CVE-2023-39910 |
| CVSS 3.1 Base Score | 7.5 (HIGH), vector AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| Type of weakness (CWE) | CWE-338: Use of Cryptographically Weak PRNG |
| Affected versions | Libbitcoin Explorer (bx) 3.0.0 – 3.6.0 |
| The main wave of thefts | July 12, 2023, ~29.65 BTC (>$850,000 at the August 2023 exchange rate) |
| Vulnerable Active Bitcoin Wallets Found (Distrust) | >2600 (of which ~2550 are probably wallets of one automated tool from 2018) |
| Affected cryptocurrencies | BTC, ETH, XRP, DOGE, SOL, LTC, BCH, ZEC |
| Total damage assessment (according to various sources) | >$900,000, later estimates increased to >227,200 unique addresses |
An important historical precedent. A similar “Mersenne Twister as a source of secrets” vulnerability was independently discovered by the Ledger Donjon team in Trust Wallet (CVE-2023-31290) back in April 2023—several months before the Milk Sad disclosure. The damage to Trust Wallet amounted to approximately $170,000, but thanks to Trust Wallet’s direct communication channels with users and timely key rotation, the losses were significantly lower than those suffered by bx, which lacked such channels.
8. A practical example of key recovery
The published materials on the RingSide Replay Attack methodology provide a documented example of key recovery for an address compromised through the same weak entropy vulnerability category:
| Parameter | Meaning |
|---|---|
| Bitcoin address | 1NiojfedphT6MgMD7UsowNdQmx5JY15djG |
| Cost of recovered funds | $61,025 |
| Private key (WIF, compressed) | Kyj6yvb4oHHDGBW23C8Chzji3zdYQ5QMr8r9zWpGVHdvWuYqCGVU |
| Public key (compressed) | 03AE73430C02577F3A7DA6F3EDC51AF4ECBB41962B937DBC2D382CABB11D0D18CE |
The key validity check is performed based on the condition that the scalar belongs to the permissible range of the secp256k1 group:
9. Comparison with related entropy vulnerabilities
| Vulnerability | Source of weak entropy | Year | Damage |
|---|---|---|---|
| Android SecureRandom (Bitcoin) | Repeat nonce k in ECDSA | 2013 | multiple BTC thefts |
| Trust Wallet (CVE-2023-31290) | MT19937 for 12-word mnemonics | 2022–2023 | ~$170,000 |
| Cake Wallet (CVE-free) | Unsafe Dart Random() | 2021 | forced key rotation |
| Milk Sad / Libbitcoin (CVE-2023-39910) | MT19937, seed = system clock (32 bits) | 2023 | >$900,000, >227,200 addresses |
10. Countermeasures and recommendations
10.1 For affected users
- Immediately transfer all funds from wallets created using
bx seedversions 3.0.0–3.6.0 to a new securely generated address. - Using address compromise verification services (for example, the official lookup service of the Milk Sad project).
- Using BIP-39 passphrase with an independent entropy source as an additional layer of protection.
10.2 For developers
| Platform | Recommended source of entropy |
|---|---|
| Linux/Unix | /dev/urandom or getrandom() |
| Windows | CryptGenRandom() / BCrypt API |
| Python | module secrets or os.urandom() |
| C++/crypto libraries | OpenSSL RAND_bytes(), libsodium randombytes() |
#include <fstream>
void secure_random_bytes(uint8_t* buf, size_t len) {
std::ifstream urandom("/dev/urandom", std::ios::in | std::ios::binary);
if (!urandom) throw std::runtime_error("Cannot open /dev/urandom");
urandom.read(reinterpret_cast<char*>(buf), len);
if (urandom.gcount() != static_cast<std::streamsize>(len))
throw std::runtime_error("Read error from /dev/urandom");
}
11. Conclusion
The Milk Sad vulnerability (CVE-2023-39910) clearly demonstrates that the cryptographic strength of the entire system is determined by its weakest link—the quality of the entropy source—and not by the complexity of the signature algorithm used. Reducing the effective key space with to (attack efficiency of 12.5%) made the recovery of private keys a task that can be solved on household equipment in a matter of days or even seconds given the known time range of wallet creation.
The incident highlights the need for mandatory auditing of cryptographic dependencies in open-source libraries, clear marking of insecure functions (such as bx seed), and the transition to EAL6+-certified hardware-based true randomness sources (TRNGs) for critical applications.
The material was prepared based on the analysis of the official technical report Distrust (milksad.info/disclosure.html), the NVD CVE-2023-39910 card and materials from the CryptoDeepTech / RingSide Replay Attack project (github.com/demining/RingSide-Replay-Attack, Milk Sad Disclosure (CVE-2023-39910) – Libbitcoin Explorer weak seed generation
A cryptanalytic analysis of the fundamental entropy flaw in the Libbitcoin Explorer wallet generator, which resulted in the systemic compromise of over 227,200 Bitcoin addresses.
1. Introduction: Problem Statement
The security of the Bitcoin cryptographic system is based on the assumption that the elliptic curve discrete logarithm problem (ECDLP) over a set of points on the secp256k1 curve is computationally difficult. However, the mathematical perfection of the ECDSA scheme itself is irrelevant if the fundamental condition for correct operation—the quality of the randomness (entropy) source used to generate the private key—is violated.
A violation of this condition was the cause of the vulnerability CVE-2023-39910 , codenamed “Milk Sad,” discovered in Libbitcoin Explorer ( bx) versions 3.0.0–3.6.0. The official disclosure report was published by the research group Distrust on August 8, 2023.
2. Theoretical foundation: entropy and cryptographic security
A Bitcoin private key is a random integer k that must satisfy the condition:
where n is the order of the group of points of the curve secp256k1:
The public key is calculated as the scalar product of the generator point G and the private key:
The secp256k1 curve itself is given by the Weierstrass equation:
According to Claude Shannon’s information theory, the measure of uncertainty of a random variable is determined by entropy:
For a 256-bit Bitcoin private key, the required maximum entropy is: bits
Definition 1 (cryptographically secure PRNG). A pseudorandom number generator is called cryptographically secure (CSPRNG) if, given the first k output bits, it is impossible to predict bit (k+1) in reasonable time with probability greater than , Where – negligible value.
A historical example. A similar entropy violation principle has previously led to disasters: in 2013, a weak Android SecureRandom generator led to massive nonce collisions in ECDSA Bitcoin wallet signatures (Android bug, CVE-2013-7372), allowing private keys to be calculated by repeating the k value in the signature formula.
3. Vulnerability discovery timeline
On July 21, 2023, an affected user discovered the complete loss of funds from their hardware wallet, despite strict security measures: wallet generation occurred on an air-gapped Linux laptop, a 24-word BIP-39 seed was used, and the seed never left the offline environment. Investigation revealed that at least two independent victims used the same command bx seed -b 256 | bx mnemonic-new from Libbitcoin Explorer 3.x to generate the wallet.
| Date | Event |
|---|---|
| 2022-11-17 | Ledger Donjon reveals Trust Wallet vulnerability (CVE-2023-31290) – a prototype for a similar Mersenne Twister issue |
| 2023-05-03 | The first suspicious thefts from “weak” bx wallets |
| 2023-07-12 | The main wave of thefts (~29.65 BTC, over $850,000) |
| 2023-07-21 | Discovery of the bx vulnerability by a team of independent researchers |
| 2023-08-07 | MITRE assigns CVE-2023-39910 |
| 2023-08-08 | Distrust’s full technical report (Milk Sad) is released |
4. Anatomy of a Vulnerability: MT19937 as a Source of Entropy
The Mersenne Twister (MT19937) is a pseudorandom number generator developed by Matsumoto and Nishimura in 1997. Its characteristics are presented below.
| Parameter | The meaning of MT19937 |
|---|---|
| Period | (Mersenne number) |
| Word size | 32 bits |
| Internal state size | 624 × 32 = 19968 bits |
| Seed size (in standard implementation) | 32 bits |
| Uniformity (k-distribution) | 623-dimensional uniformity |
Despite its excellent statistical properties (Diehard, TestU01), MT19937 is categorically unsuitable for cryptography: its internal state is completely reconstructed from 624 consecutive outputs, which makes the algorithm completely linear and predictable.
4.1 Vulnerable Libbitcoin Explorer Code
According to the official disclosure report, the call bx seed results in the following chain of functions in the libbitcoin-system source code (file pseudo_random.cpp):
// Using the system clock for seeding
const auto get_clock_seed = []() NOEXCEPT
{
const auto now = high_resolution_clock::now();
return static_cast<uint32_t>(now.time_since_epoch().count());
};
if (twister.get() == nullptr)
{
// Seeding via high resolution clock (32 bits!)
twister.reset(new std::mt19937(get_clock_seed()));
}
The key problem is that even if the user requests 256 bits of entropy with the command bx seed -b 256, the actual number of independent bits is limited by the 32-bit seed obtained from the system clock.
A real-world experimental fact from the Distrust report. Using a utility libfaketime to lock the system time to FAKETIME=0, the researchers executed the official binary twice bx-3.2.0 and obtained an absolutely identical mnemonic phrase:
$ bx seed -b 256 | bx mnemonic-new milk sad wage cup reward umbrella raven visa give list decorate bulb gold raise twenty fly manual stand float super gentle climb fold park
It was this phrase (“milk sad…”) that gave the name to the entire vulnerability.
5. Formal model of the RingSide Replay Attack
Let’s introduce the notation: E is the desired 256-bit entropy; S is the seed MT19937, ; f(S) is the function for generating the MT19937 sequence from seed S ; — truncation to the first 32 bytes; bip39(E) — entropy-to-mnemonic conversion; derive(m) — BIP-32 private key derivation; addr(k) — set of Bitcoin addresses derived from key k .
Definition 2 (Milk Sad vulnerability). such that
Probability of successful recovery with a complete enumeration of the space :
Because , the probability of a false address match is negligible, so when finding the correct seed .
5.1 BIP-39/BIP-32 Deterministic Chain
5.2 Quantitative assessment of attack effectiveness
This means that the actual search space is reduced by 87.5% relative to the theoretically safe level of 256 bits.
6. Computational complexity of the attack
| Equipment | Search speed | Full enumeration time |
|---|---|---|
| CPU (typical gaming PC, according to Distrust) | ~10^6 seeds/sec | several days |
| GPU (RTX 3090/4090 class) | ~10^8 seeds/sec | tens of seconds – minutes |
| Known creation date (narrowed down to 1 day) | 86,400 values | seconds |
Reducing space with a known wallet creation date: times
7. Real consequences: damage statistics
| Parameter | Meaning |
|---|---|
| CVE ID | CVE-2023-39910 |
| CVSS 3.1 Base Score | 7.5 (HIGH), vector AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| Type of weakness (CWE) | CWE-338: Use of Cryptographically Weak PRNG |
| Affected versions | Libbitcoin Explorer (bx) 3.0.0 – 3.6.0 |
| The main wave of thefts | July 12, 2023, ~29.65 BTC (>$850,000 at the August 2023 exchange rate) |
| Vulnerable Active Bitcoin Wallets Found (Distrust) | >2600 (of which ~2550 are probably wallets of one automated tool from 2018) |
| Affected cryptocurrencies | BTC, ETH, XRP, DOGE, SOL, LTC, BCH, ZEC |
| Total damage assessment (according to various sources) | >$900,000, later estimates increased to >227,200 unique addresses |
An important historical precedent. A similar “Mersenne Twister as a source of secrets” vulnerability was independently discovered by the Ledger Donjon team in Trust Wallet (CVE-2023-31290) back in April 2023—several months before the Milk Sad disclosure. The damage to Trust Wallet amounted to approximately $170,000, but thanks to Trust Wallet’s direct communication channels with users and timely key rotation, the losses were significantly lower than those suffered by bx, which lacked such channels.
8. A practical example of key recovery
The published materials on the RingSide Replay Attack methodology provide a documented example of key recovery for an address compromised through the same weak entropy vulnerability category:
| Parameter | Meaning |
|---|---|
| Bitcoin address | 1NiojfedphT6MgMD7UsowNdQmx5JY15djG |
| Cost of recovered funds | $61,025 |
| Private key (WIF, compressed) | Kyj6yvb4oHHDGBW23C8Chzji3zdYQ5QMr8r9zWpGVHdvWuYqCGVU |
| Public key (compressed) | 03AE73430C02577F3A7DA6F3EDC51AF4ECBB41962B937DBC2D382CABB11D0D18CE |
The key validity check is performed based on the condition that the scalar belongs to the permissible range of the secp256k1 group:
9. Comparison with related entropy vulnerabilities
| Vulnerability | Source of weak entropy | Year | Damage |
|---|---|---|---|
| Android SecureRandom (Bitcoin) | Repeat nonce k in ECDSA | 2013 | multiple BTC thefts |
| Trust Wallet (CVE-2023-31290) | MT19937 for 12-word mnemonics | 2022–2023 | ~$170,000 |
| Cake Wallet (CVE-free) | Unsafe Dart Random() | 2021 | forced key rotation |
| Milk Sad / Libbitcoin (CVE-2023-39910) | MT19937, seed = system clock (32 bits) | 2023 | >$900,000, >227,200 addresses |
10. Countermeasures and recommendations
10.1 For affected users
- Immediately transfer all funds from wallets created using
bx seedversions 3.0.0–3.6.0 to a new securely generated address. - Using address compromise verification services (for example, the official lookup service of the Milk Sad project).
- Using BIP-39 passphrase with an independent entropy source as an additional layer of protection.
10.2 For developers
| Platform | Recommended source of entropy |
|---|---|
| Linux/Unix | /dev/urandom or getrandom() |
| Windows | CryptGenRandom() / BCrypt API |
| Python | module secrets or os.urandom() |
| C++/crypto libraries | OpenSSL RAND_bytes(), libsodium randombytes() |
#include <fstream>
void secure_random_bytes(uint8_t* buf, size_t len) {
std::ifstream urandom("/dev/urandom", std::ios::in | std::ios::binary);
if (!urandom) throw std::runtime_error("Cannot open /dev/urandom");
urandom.read(reinterpret_cast<char*>(buf), len);
if (urandom.gcount() != static_cast<std::streamsize>(len))
throw std::runtime_error("Read error from /dev/urandom");
}
11. Conclusion
The Milk Sad vulnerability (CVE-2023-39910) clearly demonstrates that the cryptographic strength of the entire system is determined by its weakest link—the quality of the entropy source—and not by the complexity of the signature algorithm used. Reducing the effective key space with to (attack efficiency of 12.5%) made the recovery of private keys a task that can be solved on household equipment in a matter of days or even seconds given the known time range of wallet creation.
The incident highlights the need for mandatory auditing of cryptographic dependencies in open-source libraries, clear marking of insecure functions (such as bx seed), and the transition to EAL6+-certified hardware-based true randomness sources (TRNGs) for critical applications.
Milk Sad Disclosure (CVE-2023-39910) — Libbitcoin Explorer weak seed generation: https://cryptco.ru/milk-sad-disclosure-cve-2023-39910-libbitcoin-explorer-weak-seed-generation/ The material was prepared based on the analysis of the official technical report Distrust (milksad.info/disclosure.html), the NVD CVE-2023-39910 card and materials of the CryptoDeepTech / RingSide Replay Attack project ( github.com/demining/RingSide-Replay-Attack , cryptodeeptech.ru )).
