
비트코인 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)
-> if 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월)은 기본 100개의 키 풀
(GetArg("-keypool", 100))을 가진 TopUpKeyPool()을 도입했다.
THC 그룹 (The Hackers Choice)은 수정된 OpenSSL 0.9.8c를 사용하여
thc-btc-rng-bruteforce를 개발했다. 세 가지 중요한 변경 사항:
THC_hitme()의 도입 (md_rand.c:133-180)THC_hitme(0): state_num, state_index, entropy,
initialized, stirred_pool, md_count[0..1], md[], state[]를
0으로 초기화 — 완전한 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 vs 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__ only) | 없음 |
QPC->RAND_add(8), perfmon separate every 10 min | |||
| 0.3.0 | 2010 | 동일 | 없음 |
| 0.3.24 | 2011-07 | perfmon every 10 min, 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