ブラウザ、OSカーネル、組み込み機器を問わず、メモリ破壊系の脆弱性は今も現役の攻撃経路です。その中でも「ヒープオーバーフロー」は、バッファオーバーフローの一種でありながら、スタックオーバーフローとは異なる特性を持ち、検知が難しい攻撃として知られています。
「バッファオーバーフローは聞いたことがあるけど、ヒープとスタックの違いがよくわからない」
「Cで書いたコードを社内サービスで使っているが、どこを直せばいいのか見当もつかない」
この記事では、ヒープオーバーフローの仕組みをスタックオーバーフローとの比較で整理したうえで、攻撃者がどう悪用するか、そして現場で実践できる防御手順までを解説します。

ヒープオーバーフローとは?
ヒープオーバーフローとは、プログラムが動的に確保したメモリ領域(ヒープ)に対して、確保サイズを超えるデータを書き込むことで隣接するメモリ領域を破壊してしまう脆弱性です。
プログラムが使うメモリには大きく2種類あります。
・スタック(Stack): 関数の呼び出しと連動して自動的に確保・解放される領域。関数の戻り先アドレス(リターンアドレス)や局所変数が置かれる
・ヒープ(Heap): malloc / new / calloc などで実行時に動的に確保・解放される領域。オブジェクトや可変長データを格納する
ヒープオーバーフローは後者のヒープ上で起きる溢れです。スタックとは異なり、リターンアドレスが直接上書きされるわけではないため、「スタックオーバーフローよりも難しそう」という印象を持つ方も多いです。しかし実際には、ヒープの内部管理構造(メタデータ)や隣接するオブジェクトのポインタを書き換えることで、最終的に任意コード実行(RCE)に持ち込む攻撃が確立しています。
なぜ今も重要な脅威なのか
近年のブラウザ脆弱性の多くはメモリ安全性の問題に起因しており、Use-After-Free(解放済みメモリへのアクセス)と並んでヒープ破壊系がその中核を占めます。C / C++ で書かれたネイティブコードを持つアプリケーション、ネットワーク機器のファームウェア、PDF・動画のパーサーなど、「ユーザー入力を処理するあらゆる箇所」が潜在的なターゲットです。
スタックオーバーフローとの違い
混同しやすい2つの違いを整理します。
| 比較軸 | スタックオーバーフロー | ヒープオーバーフロー |
|---|---|---|
| 発生場所 | スタック領域(関数フレーム内) | ヒープ領域(動的確保メモリ) |
| 主な上書き対象 | リターンアドレス・保存済みベースポインタ | ヒープメタデータ・隣接オブジェクトのポインタ |
| 典型的な攻撃手法 | リターンアドレスを制御フローに乗っ取る | メタデータ改ざんによるポインタ書き換え、オブジェクトの仮想関数テーブル(vtable)汚染 |
| 検知難易度 | スタックカナリアで比較的検知しやすい | ヒープメタデータ破壊はカナリア非対象で見落としやすい |
| 緩和策の効果 | スタックカナリア・NXビットが有効 | ASLR・ヒープガードページ・安全なアロケータが重要 |
スタックオーバーフローの方が「教科書に載りやすい」ため有名ですが、現代の攻撃では標準的な緩和策(スタックカナリアなど)でスタックが守られているため、攻撃者はヒープ側を狙うケースが増えています。
ヒープオーバーフローの攻撃の仕組み
概念レベルで攻撃の流れを整理します(具体的な悪用コードは掲載しません)。
1. ヒープのメタデータを理解する
glibcのptmallocやWindows NT HeapなどのアロケータはChunk(チャンク)と呼ばれる単位でメモリを管理します。各チャンクには「サイズ情報」「前後チャンクへのリンク(doubly-linked list)」などのメタデータが埋め込まれています。
攻撃者は、このメタデータの一部を上書きして「malloc / freeの操作を横取りする」ことを目指します。古典的な手法として「ヒープ・アンリンク攻撃(Heap Unlink Attack)」があります。
2. vtableポインタの汚染
C++で書かれたコードでは、オブジェクトの仮想関数を管理するvtable(仮想関数テーブル)へのポインタがオブジェクト先頭に格納されています。ヒープオーバーフローで隣接するオブジェクトのvtableポインタを攻撃者の用意したアドレスに書き換えると、仮想関数を呼び出した瞬間に任意コードが実行されます。
3. Use-After-Freeとの組み合わせ
ヒープオーバーフローはUse-After-Free(UAF)と組み合わせることで威力が増します。まずUAFで特定の位置にシェルコードや偽のオブジェクトを配置(ヒープスプレー)し、その後ヒープオーバーフローでポインタを書き換えて制御フローを乗っ取るという多段階の手法が現実の攻撃で使われています。
Use-After-FreeについてはUse-After-Free(UAF)脆弱性とは?仕組み・被害例・対策をわかりやすく解説もあわせてご覧ください。
【想定例】画像パーサーでのオーバーフロー
想定例として、「PNG画像を処理するライブラリで幅・高さの計算に整数オーバーフローが発生し、実際の画像データより小さいバッファを確保してしまうケース」を考えます。攻撃者は細工した画像ファイルを送りつけることで、バッファを意図的に溢れさせ、後続のヒープチャンクを破壊できます。このような想定例が、実際に過去のライブラリで発見された問題と類似した構造を持っています。
具体的な防御手順
1. 安全なメモリ操作の実装
コードレベルで根本的に防ぐには、長さチェックと安全な関数の使用が基本です。
# Cでの境界チェックの例(概念コード) # 危険: 長さを検証せずに memcpy を使う # memcpy(dest, src, src_len); ← dest のサイズを超える可能性 # 安全: コピー前に必ず長さを検証する # if (src_len > dest_size) { handle_error(); return; } # memcpy(dest, src, src_len); # strncpy, strlcpy, snprintf など n 付き関数で上限を指定する # 動的確保サイズを計算するときは整数オーバーフローに注意 # size_t total = width * height * 4; ← オーバーフロー可能性あり # size_t total = checked_mul(width, height, 4); ← チェック付き乗算
・strncpy / strlcpy: コピーバイト数に上限を設ける
・snprintf: sprintfの代わりにバッファサイズを指定
・整数オーバーフローチェック: サイズ計算の前にオーバーフローが起きないことを確認する
2. コンパイラ・OSの緩和策を活用する
コードの変更が難しい場合でも、プラットフォーム側の緩和策がヒープオーバーフローの悪用を困難にします。
# GCCでのコンパイル時の推奨オプション(C/C++プロジェクト) # -D_FORTIFY_SOURCE=2 : バッファ境界チェックをコンパイル時に挿入 # -fsanitize=address : AddressSanitizer(開発・テスト環境向け) # -fstack-protector-all: スタックカナリア(スタック保護)を有効化 gcc -O2 -D_FORTIFY_SOURCE=2 -fsanitize=address myapp.c -o myapp # OSレベルの確認(Linux) # ASLR が有効かどうかを確認する cat /proc/sys/kernel/randomize_va_space # 2 が望ましい(完全ランダム化) # NX bit(DEP)が有効かどうかを確認する readelf -l /usr/bin/ssh | grep -i gnu_stack # RW と出れば実行不可(NX有効)
これらの緩和策の詳細はLinuxのカーネルエクスプロイト緩和策入門|ASLR・NXビット・スタックカナリアで攻撃を失敗に追い込む設定確認ガイドで解説しています。
3. ファジングと脆弱性診断
ヒープオーバーフローは境界値テストだけでは見つかりにくく、ファジング(大量のランダム・異常入力を自動的に試すテスト手法)が有効です。
・AddressSanitizer(ASan): GCC/Clangのビルド時オプション。ヒープ・スタック・グローバルバッファのオーバーフローを実行時に即検出する
・Valgrind(Memcheck): ヒープ管理の誤りをバイトレベルで検出する動的解析ツール
・libFuzzer / AFL++: カバレッジガイドのファザー。ファイルパーサーや通信プロトコルのパーサーに有効
開発・テスト環境でこれらのツールを導入しておくことで、本番リリース前に問題を発見できます。本番環境でのASan常時動作は性能オーバーヘッドがあるため、開発・QA環境に限定するのが一般的です。
中小企業でも今日からできること
「自社でCコードを書いていない」という場合でも、依存しているライブラリやミドルウェアがヒープオーバーフローの影響を受けることがあります。
・OSとミドルウェアのパッチ適用を定期化する: 特にOpenSSL・libpng・libjpeg・FFmpegなどのメディア処理ライブラリはヒープ系の脆弱性が定期的に発見される
・ASLR・DEPが有効なOSを使用する: Windows 8以降・Linux(randomize_va_space=2)はデフォルトで有効だが、古いミドルウェアが無効化していないか確認する
・Webアプリの場合はWAFでファイルアップロード制限を設ける: 細工された画像・動画・PDFを受け付けるエンドポイントは特に注意が必要
・開発ベンダーへの要求事項に脆弱性対応を明記する: 受託開発や外注コードにもASanでのテストを要求する仕様を契約に盛り込む
よくある誤解と注意点
【注意1】「Webアプリだから関係ない」は誤り
Webアプリ本体がPython・JavaScriptで書かれていても、その下で動く言語処理系(Pythonインタープリタ)、Webサーバー(nginx / Apache)、画像処理ライブラリはC/C++で実装されています。「ユーザーがアップロードしたファイルを処理する」機能が存在する限り、間接的なリスクがあります。
【注意2】緩和策は「困難化」であって「無効化」ではない
ASLR・NXビット・スタックカナリアはどれも攻撃を「難しく」するための仕組みですが、それだけで完全には防げません。ヒープスプレーや情報漏洩(info leak)を組み合わせてASLRを回避する手法が知られています。緩和策に依存しすぎず、根本的な修正(入力バリデーション・境界チェック)を優先しましょう。
【注意3】「動いているから安全」ではない
ヒープオーバーフローが発生していても、すぐにクラッシュしないケースがあります。ヒープのレイアウト次第では長期間検知されずに悪用される可能性があるため、「クラッシュしないから問題なし」という判断は危険です。

本記事のまとめ
| 観点 | 内容 |
|---|---|
| 何が起きるか | 動的確保(ヒープ)領域を超えてデータを書き込み、隣接メモリを破壊する |
| スタックとの違い | リターンアドレスではなくヒープメタデータ・vtableを標的にする |
| 攻撃の結果 | 任意コード実行(RCE)、権限昇格に到達する可能性がある |
| コード対策 | 境界チェック・n付き関数・整数オーバーフロー防止 |
| プラットフォーム対策 | ASLR有効化・NXビット・ASanによるテスト |
| 運用対策 | 定期パッチ・ファジングテスト・WAFでのファイル入力制限 |
ヒープオーバーフローは「C/C++開発者の問題」と捉えられがちですが、依存ライブラリ・OSカーネル・ミドルウェアを通じて、すべての組織に関わるリスクです。開発者はASanとファジングを活用し、運用担当者はパッチ管理とファイル入力の制限を徹底することで、攻撃者が悪用できる窓口を着実に減らしていきましょう。
PR
サイバーセキュリティプログラミング 第2版(Justin Seitz・Tim Arnold/オライリー・ジャパン)
PythonでネットワークスキャナやFuzzerを自作しながら攻撃の仕組みを体で覚えたい方に最適。メモリ系の脆弱性を攻撃者視点で理解するうえでも実践的な一冊です。
