何が起きたのか
2010年8月15日、ブロック74638に異常なトランザクションが記録された。
184,467,440,737 BTC——総供給量2100万BTCの約9000倍が、一瞬で生成され、攻撃者に送信された。現在の価格(1BTC = 1500万円)で換算すると、約2800京円に相当する。
Kindle 小説:ジェネシス — 創設者の消失と約束
脆弱性の原因
ビットコインには「入力 ≧ 出力」の検証がある。無から有を生み出すことは許されない。普通なら、100億BTCを出力しようとしても、入力がなければ弾かれる。
攻撃者は、自分が管理する2つのアドレスに対して、それぞれ約922億BTCを送金する形で攻撃を実行した。
| 送金先アドレス | 金額 |
|---|---|
1DkyBEKt5S2GDtv7aQw6rQepAvnsRyHoYM |
92,233,720,368.54277039 BTC |
1GJRs4oCsd1GTO1koKMfm6qGB1F8V7UAMD |
92,233,720,368.54277039 BTC |
| 合計 | 184,467,440,737.09551616 BTC |
int64型(符号付き64ビット整数)の最大値は 9,223,372,036,854,775,807(約922京)。2つの出力を加算すると、この上限を超えて整数オーバーフローが発生し、合計値が負になる。
入力: 0.5 BTC
出力: 92,233,720,368 + 92,233,720,368 = -0.01 BTC(オーバーフロー後)
入力 ≧ 出力?
0.5 ≧ -0.01 → true(検証通過)
「入力 ≧ 出力」のチェックは存在したが、オーバーフロー後の値で検証していたため突破された。
よくある誤解
「もし攻撃者がもっと控えめな金額を狙っていたら、発見が遅れたかもしれない」という記述を見かけるが、これは誤り。
この攻撃の成立条件は「出力の合計がINT64_MAX(約922京 satoshi = 約922億BTC)を超えてオーバーフローを起こすこと」。合計を負にしなければ「入力 ≧ 出力」のチェックを突破できない。つまり、攻撃者に「控えめな金額」という選択肢はなかった。
修正コード
発見からわずか約5時間後、サトシ・ナカモトはバージョン0.3.10をリリースした。MAX_MONEY定数を導入し、個別値と合計値の両方を検証するようにした。
// main.h
+ static const int64 MAX_MONEY = 21000000 * COIN;
// ConnectInputs() - 入力値の検証を追加
nValueIn += txPrev.vout[prevout.n].nValue;
+ if (txPrev.vout[prevout.n].nValue < 0)
+ return error("ConnectInputs() : txin.nValue negative");
+ if (txPrev.vout[prevout.n].nValue > MAX_MONEY)
+ return error("ConnectInputs() : txin.nValue too high");
+ if (nValueIn > MAX_MONEY)
+ return error("ConnectInputs() : txin total too high");
// CheckTransaction() - 出力値の検証を強化
+ int64 nValueOut = 0;
foreach(const CTxOut& txout, vout)
+ {
if (txout.nValue < 0)
return error("CheckTransaction() : txout.nValue negative");
+ if (txout.nValue > MAX_MONEY)
+ return error("CheckTransaction() : txout.nValue too high");
+ nValueOut += txout.nValue;
+ if (nValueOut > MAX_MONEY)
+ return error("CheckTransaction() : txout total too high");
+ }
結果
修正版をリリースしても、それだけでは不正なブロック74638は消えない。ビットコインには中央管理者がいないため、世界中のノード(ビットコインソフトウェアを動かしているコンピュータ)が自発的にアップデートする必要がある。
修正版は「ブロック74638を無効とみなす」新ルールを含んでいた。これはソフトフォークと呼ばれる手法だ。ソフトフォークとは、ルールを厳しくする変更のこと。旧バージョンが「OK」と判断するブロックの一部を、新バージョンが「NG」と判断するようになる。逆に、新バージョンが作ったブロックは旧バージョンでも有効と判断される。
対義語はハードフォーク。こちらはルールを緩くする(または互換性のない変更をする)ため、旧バージョンと新バージョンに互換性がなく、必ず別のコインに分裂する。2017年のBitcoin Cash誕生が有名な例で、ブロックサイズ上限を巡る対立からビットコインと別のコインに分裂した。
今回はビットコインを分裂させるわけにはいかなかった。だからソフトフォークが選ばれた。新バージョンが計算力で勝てば、旧バージョン側も最終的に同じチェーンに合流できる。
旧バージョンのノードは不正ブロックを含むチェーンを伸ばし続け、新バージョンのノードは不正ブロックを除外した別のチェーンを伸ばす。2つのチェーンが並行して存在する状態になった。
ビットコインでは「最も長いチェーンが正しい歴史」というルールがある。どちらが生き残るかは、計算力(マイニングパワー)の多数決で決まる。
結果、修正版ノードの計算力が過半数を超え、ブロック74691で正規チェーンが不正チェーンを追い越した。1844億BTCは歴史から消えた。
教訓
- 整数オーバーフローは金融システムでは致命的
- 合計値の検証は個別値の検証とは別に必要
- オープンソースの強み:発見から約5時間で修正・デプロイ
注意: この脆弱性は2010年に修正済みです(CVE-2010-5139)。現在のBitcoin Coreでは再現できません。