Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2008-0166-BTC-satoshi-mining-wallets — Technical analysis of CVE-2008-0166 in Bitcoin 2009-2012, examining OpenSSL 0.9.8c PRNG weaknesses, Windows entropy failures, and key-space reconstruction. | Kitploit
Tools/GitHubGitHub/ethicbrudhack/cve-2008-0166-btc-satoshi-mining-wallets
Vulnerability AnalysisExploitationHash AnalysisReverse EngineeringCryptographyPapers & ResearchLearning & Education
GitHubethicbrudhack/cve-2008-0166-btc-satoshi-mining-wallets

CVE-2008-0166-BTC-satoshi-mining-wallets

Technical analysis of CVE-2008-0166 in Bitcoin 2009-2012, examining OpenSSL 0.9.8c PRNG weaknesses, Windows entropy failures, and key-space reconstruction.

View Repository
114h 49m agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

GhostRNG — Vulnerability Analysis: OpenSSL 0.9.8c (CVE-2008-0166) in Bitcoin 2009-2012

Author: analysis based on bitcoin-code-r252 (0.1.5 / 0.2.0 / 0.3.24) and the original make-OpenSSL-0-9-8c-vulnerable-again.diff (THC) Date: 2026-09-10


1. Introduction

Bitcoin 0.1.5 (January 2009) ran exclusively on Windows, using OpenSSL 0.9.8c for EC key generation (EC_KEY_generate_key). In 2008, CVE-2008-0166 was discovered in Debian's OpenSSL package, but on Windows the issue had a different root cause — not a missing /dev/urandom, but the implementation of RandAddSeed() in the Bitcoin source code itself.


2. PRNG State Before Key Generation (0.1.5)

In bitcoin-code-r252 (tags-0.1.5) the RNG initialization sequence (util.cpp):

root@kitploit:~
CInit::CInit() {
    RAND_screen();                    // (a) screen bitmap scrape
    RandAddSeed(true);                // (b) QPC + PerfMon
}

RandAddSeed (util.cpp:57-92):

  • QueryPerformanceCounter -> RAND_add(&PerformanceCount, 8, 1.5)
  • RegQueryValueEx(HKEY_PERFORMANCE_DATA, "Global", ..., buf=250000) -> if ERROR_SUCCESS: SHA256(pdata) -> RAND_add(&hash, 32, entropy)

Key generation (key.h:75-78):

root@kitploit:~
CKey::MakeNewKey() {
    EC_KEY_generate_key(pkey);  // internally: RAND_bytes(32)
}

Important: versions 0.1.5 through 0.3.24 had no keypool. GetRand() uses RAND_bytes(8) for other purposes (network nonces, IRC nicknames), thus disrupting the PRNG state between successive GenerateNewKey() calls.

Version 0.4.0 (September 2011) introduced TopUpKeyPool() with a default pool of 100 keys (GetArg("-keypool", 100)).


3. The Patch: make-OpenSSL-0-9-8c-vulnerable-again.diff

The THC group (The Hackers Choice) developed thc-btc-rng-bruteforce, using a modified OpenSSL 0.9.8c. Three critical changes:

A. Introduction of THC_hitme() (md_rand.c:133-180)

  • THC_hitme(0): zeroes state_num, state_index, entropy, initialized, stirred_pool, md_count[0..1], md[], state[] — complete PRNG reset.
  • THC_hitme(pid): stores pid in the static variable thc_pid.

B. ssleay_rand_add() — buffer content ignored (line 344)

root@kitploit:~
- MD_Update(&m, buf, j);
+ //MD_Update(&m, buf, j);   // commented out!

RAND_add still advances state_index by num and increments md_count[1], but the buffer content has no effect on PRNG state.

C. ssleay_rand_bytes() — PID substitution (lines 550-561)

  • Original getpid() call removed.
  • curr_pid = thc_pid — constant value from THC_hitme.
  • Additional MD_Update(&m, &curr_pid, sizeof(curr_pid)) injects PID as the only external variable input.

Conclusion

In the patched OpenSSL, PRNG state depends only on:

  1. PID value (via THC_hitme)
  2. Order and size of RAND_add calls (num parameter, not buffer content)
  3. Number of RAND_bytes calls before key generation

4. Architecture Difference: le32 vs le64

In OpenSSL 0.9.8c, BN_ULONG is defined by opensslconf.h:

root@kitploit:~
#ifdef SIXTY_FOUR_BIT_LONG   -> BN_ULONG = unsigned long (64-bit)
#ifdef THIRTY_TWO_BIT        -> BN_ULONG = unsigned long (32-bit)

On 64-bit Linux, OpenSSL defaults to SIXTY_FOUR_BIT_LONG, changing sizeof(BN_ULONG) from 4 to 8 bytes. This affects:

  • Size of static long md_count[2] (8 vs 16 bytes)
  • Internal bignum representation in SHA
  • The entire PRNG output stream

THC SelfCheck Test Vector (profile 0.3.24, PID 31337):

  • le32: 1FkLYqPpfKPAR6EZh2RC6sDwTUA6Axb1XQ
  • le64: 1JbxBrkBiSwpJUJN2bXrYJc51pH5rjrzTD

Bitcoin 2009 ran on Windows XP 32-bit, so correct key reconstruction requires compilation with THIRTY_TWO_BIT enforced (edit include/openssl/opensslconf.h before building).


5. Sergio Lerner's Bug (Bitcointalk 2012)

In a post on 27 September 2012 (topic=113496), Sergio Lerner described:

(a) RandAddSeed() calls QueryPerformanceCounter() — requires QWORD alignment of its argument. The gcc compiler on Windows could fail to 8-byte align the stack variable, causing silent failure.

(b) RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", ...) uses a fixed buffer of 250,000 bytes. On Windows XP, the performance data was ~280 KB — the function returned ERROR_MORE_DATA and was never called again with a larger buffer. debug.log contained no warning.

If both mechanisms failed, the only entropy source was RAND_screen() (screen bitmap). In the patched OpenSSL even RAND_screen is irrelevant because buffer content is ignored.

Lerner recommended logging these failures to debug.log — this fix was incorporated into later Bitcoin Core versions.


6. Evolution of RNG Init in Bitcoin Core (2009-2012)

Verified in bitcoin-code-r252 (tags: 0.1.5, 0.2.0, 0.3.0) and the bitcoin/bitcoin repository (tags: 0.3.24, 0.4.0, 0.5.0, 0.6.0):

VersionDateInit RNG (Windows)Keypool
0.1.52009-01RAND_screen + RandAddSeed(true)none
QPC->RAND_add(8) + perfmon->SHA256->RAND_add(32)
0.2.02009-12RAND_screen (__WXMSW__ only)none
QPC->RAND_add(8), perfmon separate every 10 min
0.3.02010samenone
0.3.242011-07perfmon every 10 min, RegQueryValueExAnone
0.4.02011-09sameTopUpKeyPool 100
0.5.02011-12same100
0.6.02012-03same100

Key Findings

  • RAND_screen() was used on all Windows versions through 2012.
  • The keypool (100 keys pre-generated without PRNG reset) appeared only in 0.4.0 — earlier versions generated one key per request.
  • Perfmon: 0.1.5 used SHA256(pdata) -> RAND_add(32), later versions passed raw pdata to RAND_add (different num size matters in the patched OpenSSL because only the size, not the content, affects state).

7. Conclusions

The key space for the attack (in patched OpenSSL):

root@kitploit:~
  PID (1..32767)
  x poll (count of RAND_add calls in init, modeling Toolhelp32 on WinXP)
  x keypool position (1 for pre-0.4.0, 100 for 0.4.0+)
  x profile (RAND call sequence for each Bitcoin version)
  x architecture (le32 / le64)

The tick, screen, cursor, hwnd, queue parameters are irrelevant in this model because RAND_add buffer content is ignored.

The original THC authors wrote: "We did not find any." — they did not find any keys on the blockchain. The analysis confirms their finding: the probability of a vulnerable key existing on-chain depends entirely on whether any Bitcoin wallet was actually generated on a Windows XP system with the broken OpenSSL and silently-failing perfmon/QPC.


References

[1] Sergio Lerner, "Possible new vulnerability: poor entropy in Windows generated keypairs", Bitcointalk 2012-09-27: link

[2] Bitcoin StackExchange — "Was Satoshi using Windows or Linux?": link

[3] THC, thc-btc-rng-bruteforce: link

[4] Bitcoin Core tags (0.1.5, 0.2.0, 0.3.0, 0.3.24, 0.4.0, 0.5.0, 0.6.0): link

[5] OpenSSL 0.9.8c + patch make-OpenSSL-0-9-8c-vulnerable-again.diff

[6] Analiza_entropy_win.txt — technical report, 2026-09-10

Download Tool