「Ghidra」×「Claude Desktop」でスタックオーバーフローの脆弱性を見つけてみよう

はじめに
前回は、「Ghidra」と「Claude Desktop」をMCPで接続し、リバースエンジニアリングを効率化する方法を紹介しました。
今回は「Ghidra」×「Claude Desktop」で、スタックオーバーフローの脆弱性を発見できるかチャレンジしてみます。
脆弱性のあるC言語のプログラムを用意する
まず、脆弱性のあるC言語のプログラムを用意します。次のC言語のプログラムはgets関数を使用しているため、スタックオーバーフローの脆弱性があります。
#include <stdio.h> int main(int argc, char *argv[]) { char buf[100]; setvbuf(stdout, NULL, _IOLBF, 0); gets(buf); puts(buf); return 0; }
スタックは関数の引数やローカル変数を格納するメモリ領域です。スタックオーバーフローはスタックにデータを溢れさせる脆弱性で、この脆弱性を悪用するとリターンアドレスを上書きし、任意のアドレスへと処理を移すことができます。
詳細な解説は「単純なスタックバッファオーバーフロー攻撃をやってみる - ももいろテクノロジー」を参照してください。
用意したC言語のプログラムを、x64のLinux上でコンパイラにGCCを使用してビルドします。「スタックカナリー」というスタックにランダム生成された値を格納しておき、改ざんを検知することでスタックオーバーフローを防ぐ機構がデフォルトでは有効になっています。
この機構を無効にするために-fno-stack-protector
オプションを指定してビルドします。このプログラムを実行すると、次のように入力した文字列がそのまま出力されます。
$ gcc test.c -fno-stack-protector -o test # スタックカナリーを無効にしてビルド $ ./test Ghidra+MCP!!! # 入力 Ghidra+MCP!!! # 出力
「Ghidra」×「Claude Desktop」で
バイナリの脆弱性を見つける
MCPを用いてClaude Desktopを接続したGhidraで、実行ファイルからこのプログラムの脆弱性を見つけます。Ghidraのインストール方法は「公式ドキュメント」を、Claude Desktopを接続する方法は第4回を参照してください。
まず、Ghidraで作成したプログラムを読み込みます。Ghidraでmain関数をデコンパイルすると、結果は次のようになります。
undefined8 main(void) { char local_78 [112]; setvbuf(stdout,(char *)0x0,1,0); gets(local_78); puts(local_78); return 0; }
デコンパイル結果を見た時点で、C言語に詳しい方ならスタックオーバーフローの脆弱性があると気づくと思います。ここでは、検証のためにClaude Desktopに脆弱性を見つけるように指示してみます。
Q. main関数に脆弱性はないでしょうか?
A. このファイルのmain関数を詳しく分析するために、まずmain関数の内容を確認してみましょう。
(省略)
main関数を確認したところ、明確な脆弱性が確認できました。具体的には:
1. バッファオーバーフロー脆弱性:
gets(local_78) 関数の使用が非常に危険です。この関数は入力のサイズをチェックせず、バッファサイズを超える入力が可能です。
local_78 は112バイトのバッファサイズですが、getsはバッファサイズを超える入力を制限しません。
2. 攻撃のリスク:
スタックオーバーフローによりリターンアドレスを上書きでき、任意コード実行の可能性があります。
これにより攻撃者がシステム上で権限を奪取できる可能性があります。
3. より安全な代替案:
getsの代わりにfgetsを使用し、サイズ制限を設けるべきです。
例: fgets(local_78, sizeof(local_78), stdin);
4. その他の問題点:
エラー処理が不足しています
入力値の検証がありません
この脆弱性は非常に基本的ですが重大であり、実際の環境で悪用される可能性が高いものです。
Claude Desktopはmain関数に存在するスタックオーバーフローの脆弱性を見つけることができました。続いて、この脆弱性を検証するための方法も生成してもらいます。
Q. 本当にスタックオーバーフローの脆弱性があるのか、検証したいと考えています。
A.スタックオーバーフロー脆弱性の検証は重要な確認作業です。main関数を詳しく分析し、検証方法について考えていきましょう。
まず、現在の関数の状態を確認します:
local_78は112バイトのバッファです
gets()関数は入力サイズの制限なしに読み込みを行います
バッファを超える入力があった場合、スタック上の他の領域(リターンアドレスを含む)を上書きする可能性があります
検証するには以下のようなアプローチが考えられます:
1. スタックレイアウトの確認
- スタック上での変数配置と保存されたリターンアドレスの位置関係を把握する必要があります
- バッファからリターンアドレスまでの距離を特定します
2. 検証用の入力パターン生成
- バッファサイズを超える特定パターンの入力を作成します
- 例:112バイト以上の異なるパターンを持つ文字列
3. 実行時の挙動確認
- 過剰な入力でプログラムがクラッシュするか確認
- デバッガを使用して、リターンアドレスが上書きされるかを確認
具体的な検証手順:
# 検証用Pythonスクリプト例
import struct
# バッファサイズ (112バイト) + 追加のスタック領域
payload = b"A" * 112 # バッファを埋める
payload += b"B" * 8 # 保存されたbase pointerを上書き
payload += b"C" * 8 # リターンアドレスを上書き
# ファイルに書き出す場合
with open("input.txt", "wb") as f:
f.write(payload)
# もしくはコマンドラインで直接
# (echo -ne "payload") | ./program
この入力でプログラムが通常終了せずにSEGFAULTやシグナル11で終了する場合、スタックオーバーフローの存在が確認できます。さらに詳細な分析には、GDBなどのデバッガを用いてスタックの状態を確認することをお勧めします。
検証の結果、もし脆弱性が確認された場合は、gets()の代わりにfgets()などのバッファサイズを制限できる関数への置き換えを検討すべきです。
より詳細な検証や悪用のデモンストレーションが必要であれば、お知らせください。
Claude Desktopに生成してもらったPythonスクリプトを実行し、スタックオーバーフローが発生するか確認します。このPythonスクリプトはバッファサイズを超える入力を生成し、その入力を「input.txt」に書き出します。input.txtをtestプログラムに与え、スタックオーバーフローが発生するか確認した結果は次の通りです。
$ python3 exploit.py $ cat input.txt | ./test AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC Segmentation fault (core dumped)
「Segmentation fault (core dumped)」というメッセージが出力され、スタックオーバーフローが発生していることが確認できました。Segmentation faultは、メモリの不整合が原因で発生するエラーです。
おわりに
「Ghidra」×「Claude Desktop」で簡単なスタックオーバーフローの脆弱性を見つけることができました。より効率的なプロンプトやGhidra Scriptなどを用いて脆弱性を見つける処理を自動化できれば、ファームウェアなど現実世界のバイナリから効率的に脆弱性を見つけることができるでしょう。
GhidraやIDAなどのリバースエンジニアリングツールとAIツールを組み合わせて自動化することで、バイナリの脆弱性を大量に見つけるような発表が今年のセキュリティカンファレンスでは出てくるのではないでしょうか。
今回は、スタックオーバーフローの脆弱性が存在することを確認するに留めましたが、スタックオーバーフローを悪用するとシェルコードをメモリ上に書き込み、実行することで任意のコードを実行できます。
興味がある方は、シェルコードを実行するところまでを検証してみてください。
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- リバースエンジニアリング新時代へ! 「Ghidra」と「Claude Desktop」をMCPで繋ぐ
- 32ビット/64ビットの両方のARM環境で有効なディスアセンブルを妨害するテクニック
- JBossクラスタのチューニング
- Netgearが提供する複数のネットワーク製品にバッファオーバーフローの脆弱性が存在すると発表
- Akka Streamsで実装するリアクティブストリーム(その2)
- Dnsmasqに複数の脆弱性
- 32ビットのARM環境のみで有効なディスアセンブルを妨害するテクニック
- 「Ace」を使って「TAURI」で「テキストエディタ」アプリを作ろう
- 稼動情報からボトルネックを探し出す
- 「TAURI」にも必要な「Rust」の「クレート」を使う