Milk Sad Disclosure (CVE-2023-39910) — Libbitcoin Explorer weak seed generation

11.09.2026

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:1≤k<n

where  n  is the order of the group of points of the curve secp256k1:n=0xFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141≈1.158×1077

The public key is calculated as the scalar product of the generator point  G  and the private key:Q=k⋅G

The secp256k1 curve itself is given by the Weierstrass equation:y2≡x3+7(modp),p=2256–232–977

According to Claude Shannon’s information theory, the measure of uncertainty of a random variable is determined by entropy:H(X)=–∑ip(xi)log2⁡p(xi)

For a 256-bit Bitcoin private key, the required maximum entropy is: bitsHmax=log2⁡(2256)=256 bit

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 12+ε, 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.

DateEvent
2022-11-17Ledger Donjon reveals Trust Wallet vulnerability (CVE-2023-31290) – a prototype for a similar Mersenne Twister issue
2023-05-03The first suspicious thefts from “weak” bx wallets
2023-07-12The main wave of thefts (~29.65 BTC, over $850,000)
2023-07-21Discovery of the bx vulnerability by a team of independent researchers
2023-08-07MITRE assigns CVE-2023-39910
2023-08-08Distrust’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.

ParameterThe meaning of MT19937
Period219937–1 (Mersenne number)
Word size32 bits
Internal state size624 × 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, S∈[0,232);  f(S)  is the function for generating the MT19937 sequence from seed  S ; trunc32(x) — 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∃k,S:trunc32(f(S))≡E(mod2256)such thataddr(derive(bip39(E)))=Atarget

Probability of successful recovery with a complete enumeration of the space 232:P(success)=1–(1–1N)232,N=2160

Because 232≪2160, the probability of a false address match is negligible, so when finding the correct seed P(success)≈1.

5.1 BIP-39/BIP-32 Deterministic Chain

S32bit→MT19937→E256bit→SHA256→Mnemonic24→PBKDF2→Seed512bit→HMAC—SHA512→(k,c)→BIP—44→kfinal

5.2 Quantitative assessment of attack effectiveness

Efficiency=log2⁡(232)log2⁡(2256)=32256=0.125=12.5%

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

EquipmentSearch speedFull enumeration time 232
CPU (typical gaming PC, according to Distrust)~10^6 seeds/secseveral days
GPU (RTX 3090/4090 class)~10^8 seeds/sectens of seconds – minutes
Known creation date (narrowed down to 1 day)86,400 valuesseconds

Reducing space with a known wallet creation date: times23286400≈49710once

7. Real consequences: damage statistics

ParameterMeaning
CVE IDCVE-2023-39910
CVSS 3.1 Base Score7.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 versionsLibbitcoin Explorer (bx) 3.0.0 – 3.6.0
The main wave of theftsJuly 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 cryptocurrenciesBTC, 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:

ParameterMeaning
Bitcoin address1NiojfedphT6MgMD7UsowNdQmx5JY15djG
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:1≤k<n

9. Comparison with related entropy vulnerabilities

VulnerabilitySource of weak entropyYearDamage
Android SecureRandom (Bitcoin)Repeat nonce k in ECDSA2013multiple BTC thefts
Trust Wallet (CVE-2023-31290)MT19937 for 12-word mnemonics2022–2023~$170,000
Cake Wallet (CVE-free)Unsafe Dart Random()2021forced 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 seed versions 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

PlatformRecommended source of entropy
Linux/Unix/dev/urandom or getrandom()
WindowsCryptGenRandom() / BCrypt API
Pythonmodule  secrets or os.urandom()
C++/crypto librariesOpenSSL  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 2256 to 232 (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:1≤k<n

where  n  is the order of the group of points of the curve secp256k1:n=0xFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141≈1.158×1077

The public key is calculated as the scalar product of the generator point  G  and the private key:Q=k⋅G

The secp256k1 curve itself is given by the Weierstrass equation:y2≡x3+7(modp),p=2256–232–977

According to Claude Shannon’s information theory, the measure of uncertainty of a random variable is determined by entropy:H(X)=–∑ip(xi)log2⁡p(xi)

For a 256-bit Bitcoin private key, the required maximum entropy is: bitsHmax=log2⁡(2256)=256 bit

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 12+ε, 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.

DateEvent
2022-11-17Ledger Donjon reveals Trust Wallet vulnerability (CVE-2023-31290) – a prototype for a similar Mersenne Twister issue
2023-05-03The first suspicious thefts from “weak” bx wallets
2023-07-12The main wave of thefts (~29.65 BTC, over $850,000)
2023-07-21Discovery of the bx vulnerability by a team of independent researchers
2023-08-07MITRE assigns CVE-2023-39910
2023-08-08Distrust’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.

ParameterThe meaning of MT19937
Period219937–1 (Mersenne number)
Word size32 bits
Internal state size624 × 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, S∈[0,232);  f(S)  is the function for generating the MT19937 sequence from seed  S ; trunc32(x) — 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∃k,S:trunc32(f(S))≡E(mod2256)such thataddr(derive(bip39(E)))=Atarget

Probability of successful recovery with a complete enumeration of the space 232:P(success)=1–(1–1N)232,N=2160

Because 232≪2160, the probability of a false address match is negligible, so when finding the correct seed P(success)≈1.

5.1 BIP-39/BIP-32 Deterministic Chain

S32bit→MT19937→E256bit→SHA256→Mnemonic24→PBKDF2→Seed512bit→HMAC—SHA512→(k,c)→BIP—44→kfinal

5.2 Quantitative assessment of attack effectiveness

Efficiency=log2⁡(232)log2⁡(2256)=32256=0.125=12.5%

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

EquipmentSearch speedFull enumeration time 232
CPU (typical gaming PC, according to Distrust)~10^6 seeds/secseveral days
GPU (RTX 3090/4090 class)~10^8 seeds/sectens of seconds – minutes
Known creation date (narrowed down to 1 day)86,400 valuesseconds

Reducing space with a known wallet creation date: times23286400≈49710once

7. Real consequences: damage statistics

ParameterMeaning
CVE IDCVE-2023-39910
CVSS 3.1 Base Score7.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 versionsLibbitcoin Explorer (bx) 3.0.0 – 3.6.0
The main wave of theftsJuly 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 cryptocurrenciesBTC, 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:

ParameterMeaning
Bitcoin address1NiojfedphT6MgMD7UsowNdQmx5JY15djG
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:1≤k<n

9. Comparison with related entropy vulnerabilities

VulnerabilitySource of weak entropyYearDamage
Android SecureRandom (Bitcoin)Repeat nonce k in ECDSA2013multiple BTC thefts
Trust Wallet (CVE-2023-31290)MT19937 for 12-word mnemonics2022–2023~$170,000
Cake Wallet (CVE-free)Unsafe Dart Random()2021forced 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 seed versions 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

PlatformRecommended source of entropy
Linux/Unix/dev/urandom or getrandom()
WindowsCryptGenRandom() / BCrypt API
Pythonmodule  secrets or os.urandom()
C++/crypto librariesOpenSSL  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 2256 to 232 (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 )).