\n\n\n\n\n\n\n\n\n\n\n
flawopen.com/シミュレーター/Heartbleed バッファ過剰読み取り
ブラウザ上で動作するメモリモデルとパケット検証シミュレーター。未検証の TLS Heartbeat 長さフィールドがサーバーヒープの過剰読み取りを引き起こす仕組みをステップ実行。
訓練されたオウムに4文字の単語「BIRD」を伝え、「この単語を繰り返して。ただし回答は64,000文字にしてね」と指示したと想像してください。オウムは「BIRD」と鳴いた後、直近の記憶に残っている次の63,996文字(他の訪問者が話した秘密のパスワードや暗号鍵、口座情報など)を一気に喋り出します。Heartbleed では、OpenSSL がパケットの実際の長さを検証せずにクライアントの自己申告した長さを盲信し、サーバーの生メモリを攻撃者にそのままコピーして返してしまいました。
攻撃者は1バイト('A')のみを送信しつつ、長さフィールドを65,535バイト(0xFFFF)と偽装したパケットを送信。
OpenSSL は n2s(p, payload) で長さを取得するが、1 + 2 + payload + 16 <= rrec.length の確認を怠る。
サーバーは申告値を盲信してヒープ上に64KBのバッファを確保し、応答ヘッダーを構築。
memcpy() がパケット末尾を超えて隣接メモリを走査し、SSL 秘密鍵やセッショントークンを攻撃者に送出。
/* クライアントパケットから境界検証なしに16ビット長を直接取得 */
n2s(p, payload);
pl = p;
/* クライアント申告サイズに基づいて応答バッファを確保 */
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
bp = buffer;
/* バグ:隣接するサーバーヒープ領域を最大64KB読み取ってコピー! */
memcpy(bp, pl, payload);
r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
/* パケットから16ビット長を読み取り */
n2s(p, payload);
pl = p;
/* 修正:申告されたペイロード長が実際に受信したレコードサイズを超えていないか検証 */
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0; /* 不正なハートビートパケットを通知なく破棄 */
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
bp = buffer;
memcpy(bp, pl, payload);