
Bitcoin 2009-2012 में CVE-2008-0166 का तकनीकी विश्लेषण, OpenSSL 0.9.8c PRNG कमजोरियों, Windows एंट्रॉपी विफलताओं, और key-space पुनर्निर्माण की जाँच।
लेखक: bitcoin-code-r252 (0.1.5 / 0.2.0 / 0.3.24) और मूल make-OpenSSL-0-9-8c-vulnerable-again.diff (THC) पर आधारित विश्लेषण दिनांक: 2026-09-10
Bitcoin 0.1.5 (जनवरी 2009) विशेष रूप से Windows पर चलता था, EC कुंजी निर्माण
(EC_KEY_generate_key) के लिए OpenSSL 0.9.8c का उपयोग करता था। 2008 में,
Debian के OpenSSL पैकेज में CVE-2008-0166 की खोज की गई थी, लेकिन Windows पर
इस समस्या का मूल कारण भिन्न था — न कि अनुपस्थित /dev/urandom, बल्कि
Bitcoin स्रोत कोड में ही RandAddSeed() का कार्यान्वयन।
bitcoin-code-r252 (tags-0.1.5) में RNG आरंभीकरण अनुक्रम
(util.cpp):
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)
-> यदि ERROR_SUCCESS: SHA256(pdata) -> RAND_add(&hash, 32, entropy)कुंजी निर्माण (key.h:75-78):
CKey::MakeNewKey() {
EC_KEY_generate_key(pkey); // internally: RAND_bytes(32)
}
महत्वपूर्ण: संस्करण 0.1.5 से 0.3.24 तक कोई keypool नहीं था। GetRand()
अन्य प्रयोजनों (नेटवर्क nonces, IRC उपनाम) के लिए RAND_bytes(8) का उपयोग
करता है, जिससे क्रमिक GenerateNewKey() कॉलों के बीच PRNG स्थिति बाधित
होती है।
संस्करण 0.4.0 (सितंबर 2011) ने TopUpKeyPool() को 100 कुंजियों के डिफ़ॉल्ट
पूल के साथ पेश किया (GetArg("-keypool", 100)).
THC समूह (The Hackers Choice) ने thc-btc-rng-bruteforce विकसित किया,
जिसमें संशोधित OpenSSL 0.9.8c का उपयोग किया गया। तीन महत्वपूर्ण परिवर्तन:
THC_hitme() का परिचय (md_rand.c:133-180)THC_hitme(0): state_num, state_index, entropy,
initialized, stirred_pool, md_count[0..1], md[], state[]
को शून्य करता है — पूर्ण PRNG रीसेट।THC_hitme(pid): pid को स्थिर चर thc_pid में संग्रहीत करता है।ssleay_rand_add() — बफ़र सामग्री अनदेखी (पंक्ति 344)- MD_Update(&m, buf, j);
+ //MD_Update(&m, buf, j); // commented out!
RAND_add अभी भी state_index को num से बढ़ाता है और
md_count[1] को बढ़ाता है, लेकिन बफ़र सामग्री का PRNG स्थिति पर
कोई प्रभाव नहीं पड़ता।
ssleay_rand_bytes() — PID प्रतिस्थापन (पंक्तियाँ 550-561)getpid() कॉल हटा दिया गया।curr_pid = thc_pid — THC_hitme से स्थिर मान।MD_Update(&m, &curr_pid, sizeof(curr_pid)) PID को
एकमात्र बाहरी चर इनपुट के रूप में इंजेक्ट करता है।पैच किए गए OpenSSL में, PRNG स्थिति केवल इन पर निर्भर करती है:
THC_hitme के माध्यम से)RAND_add कॉलों का क्रम और आकार (num पैरामीटर, बफ़र सामग्री नहीं)RAND_bytes कॉलों की संख्याOpenSSL 0.9.8c में, BN_ULONG को opensslconf.h द्वारा परिभाषित किया जाता है:
#ifdef SIXTY_FOUR_BIT_LONG -> BN_ULONG = unsigned long (64-bit)
#ifdef THIRTY_TWO_BIT -> BN_ULONG = unsigned long (32-bit)
64-बिट Linux पर, OpenSSL डिफ़ॉल्ट रूप से SIXTY_FOUR_BIT_LONG का उपयोग
करता है, जिससे sizeof(BN_ULONG) 4 से 8 बाइट में बदल जाता है। यह प्रभावित
करता है:
static long md_count[2] का आकार (8 बनाम 16 बाइट)1FkLYqPpfKPAR6EZh2RC6sDwTUA6Axb1XQ1JbxBrkBiSwpJUJN2bXrYJc51pH5rjrzTDBitcoin 2009 Windows XP 32-bit पर चलता था, इसलिए सही कुंजी पुनर्निर्माण
के लिए THIRTY_TWO_BIT लागू करके संकलन आवश्यक है (निर्माण से पहले
include/openssl/opensslconf.h संपादित करें)।
27 सितंबर 2012 की एक पोस्ट (topic=113496) में, Sergio Lerner ने वर्णन किया:
(a) RandAddSeed() QueryPerformanceCounter() को कॉल करता है — इसके
तर्क के QWORD संरेखण की आवश्यकता होती है। Windows पर gcc कंपाइलर स्टैक
चर को 8-बाइट संरेखित करने में विफल हो सकता है, जिससे मौन विफलता होती है।
(b) RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", ...) 250,000
बाइट का निश्चित बफ़र उपयोग करता है। Windows XP पर, प्रदर्शन डेटा ~280 KB
था — फ़ंक्शन ने ERROR_MORE_DATA लौटाया और बड़े बफ़र के साथ कभी
दोबारा कॉल नहीं किया गया। debug.log में कोई चेतावनी नहीं थी।
यदि दोनों तंत्र विफल हो गए, तो एकमात्र एन्ट्रॉपी स्रोत RAND_screen()
(स्क्रीन बिटमैप) था। पैच किए गए OpenSSL में RAND_screen भी अप्रासंगिक है
क्योंकि बफ़र सामग्री अनदेखी की जाती है।
Lerner ने इन विफलताओं को debug.log में लॉग करने की सिफारिश की — यह सुधार
बाद के Bitcoin Core संस्करणों में शामिल किया गया।
bitcoin-code-r252 (tags: 0.1.5, 0.2.0, 0.3.0) और bitcoin/bitcoin रिपॉजिटरी (tags: 0.3.24, 0.4.0, 0.5.0, 0.6.0) में सत्यापित:
| संस्करण | दिनांक | Init RNG (Windows) | Keypool |
|---|---|---|---|
| 0.1.5 | 2009-01 | RAND_screen + RandAddSeed(true) | कोई नहीं |
QPC->RAND_add(8) + perfmon->SHA256->RAND_add(32) | |||
| 0.2.0 | 2009-12 | RAND_screen (__WXMSW__ only) | कोई नहीं |
QPC->RAND_add(8), perfmon हर 10 मिनट में अलग | |||
| 0.3.0 | 2010 | समान | कोई नहीं |
| 0.3.24 | 2011-07 | perfmon हर 10 मिनट, RegQueryValueExA | कोई नहीं |
| 0.4.0 | 2011-09 | समान | TopUpKeyPool 100 |
| 0.5.0 | 2011-12 | समान | 100 |
| 0.6.0 | 2012-03 | समान | 100 |
RAND_screen() का उपयोग 2012 तक सभी Windows संस्करणों पर किया गया।SHA256(pdata) -> RAND_add(32) का उपयोग किया, बाद के
संस्करणों ने कच्चे pdata को RAND_add को पास किया (पैच किए गए OpenSSL
में भिन्न num आकार मायने रखता है क्योंकि केवल आकार, सामग्री नहीं, स्थिति
को प्रभावित करता है)।हमले के लिए कुंजी स्थान (पैच किए गए OpenSSL में):
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)
tick, screen, cursor, hwnd, queue पैरामीटर इस मॉडल में
अप्रासंगिक हैं क्योंकि RAND_add बफ़र सामग्री अनदेखी की जाती है।
मूल THC लेखकों ने लिखा: "We did not find any." — उन्हें ब्लॉकचेन पर कोई कुंजी नहीं मिली। विश्लेषण उनके निष्कर्ष की पुष्टि करता है: ऑन-चेन भेद्य कुंजी के अस्तित्व की संभावना पूरी तरह इस पर निर्भर करती है कि क्या वास्तव में कोई Bitcoin वॉलेट टूटे हुए OpenSSL और मौन-विफल perfmon/QPC वाले Windows XP सिस्टम पर उत्पन्न किया गया था।
[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