Bitcoin 2009-2012におけるCVE-2008-0166の技術的分析。OpenSSL 0.9.8cのPRNG脆弱性、Windowsのエントロピー障害、および鍵空間の再構築を検証。
著者: 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年1月) は Windows 専用で動作し、EC 鍵生成
(EC_KEY_generate_key) に OpenSSL 0.9.8c を使用していた。2008年、
CVE-2008-0166 は Debian の OpenSSL パッケージで発見されたが、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()
は他の目的 (ネットワーク nonce、IRC ニックネーム) で RAND_bytes(8) を使用するため、
連続する GenerateNewKey() 呼び出しの間で PRNG 状態が乱される。
バージョン 0.4.0 (2011年9月) で TopUpKeyPool() が導入され、デフォルトで
100 鍵のプール (GetArg("-keypool", 100)) を持つようになった。
THC グループ (The Hackers Choice) は、改変された OpenSSL 0.9.8c を用いて
thc-btc-rng-bruteforce を開発した。3つの重要な変更点:
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ビットで動作していたため、正しい鍵の再構築には
THIRTY_TWO_BIT を強制してコンパイルする必要がある (ビルド前に
include/openssl/opensslconf.h を編集する)。
2012年9月27日の投稿 (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) で確認済み:
| バージョン | 日付 | 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__ のみ) | なし |
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 (init における RAND_add 呼び出し回数、WinXP 上の Toolhelp32 をモデル化)
x keypool 位置 (0.4.0 以前は 1、0.4.0 以降は 100)
x プロファイル (各 Bitcoin バージョンの RAND 呼び出しシーケンス)
x アーキテクチャ (le32 / le64)
tick、screen、cursor、hwnd、queue パラメータは、RAND_add の
バッファ内容が無視されるため、このモデルでは無関係である。
オリジナルの THC の著者たちは次のように書いている: 「我々は何も見つけられなかった。」 — 彼らは ブロックチェーン上で鍵を一切見つけられなかった。この分析は彼らの発見を裏付ける: 脆弱な鍵がオンチェーンに存在する確率は、壊れた OpenSSL とサイレントに失敗する perfmon/QPC を備えた Windows XP システム上で、Bitcoin ウォレットが実際に 生成されたかどうかに完全に依存する。
[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