問題概要
picoCTFの「heap 0」という問題の解説記事です。
- カテゴリ: Binary Exploitation
- 難易度: Easy
問題文
解説
この問題では、与えられたバイナリファイル
chall とソースコード chall.c を解析して、隠されたフラグを見つけることが求められます。ステップ1: picoCTFのサーバーに接続
まずは picoCTFの実行環境(問題サーバー)に接続します。picoCTFの問題ページに記載されている情報をもとに、
nc コマンドで接続します。$ nc tethys.picoctf.net xxxxxx
(
xxxxxx は問題ページに記載されているポート番号に置き換えてください。)接続に成功すると以下のように表示されます。
Welcome to heap0!
I put my data on the heap so it should be safe from any tampering.
Since my data isn't on the stack I'll even let you write whatever info you want to the heap, I already took care of using malloc for you.
Heap State:
+-------------+----------------+
[*] Address -> Heap Data
+-------------+----------------+
[*] 0x6363f5d672b0 -> pico
+-------------+----------------+
[*] 0x6363f5d672d0 -> bico
+-------------+----------------+
1. Print Heap: (print the current state of the heap)
2. Write to buffer: (write to your own personal block of data on the heap)
3. Print safe_var: (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag: (Try to print the flag, good luck)
5. Exit
Enter your choice:
上記の出力から、以下が読み取れます。
Heap Stateには、ヒープ上に確保された領域の アドレス と、その領域に入っている 文字列 が表示されているpicoとbicoの2つのデータがあり、互いに近いアドレスに配置されている- メニューの
2. Write to bufferで、ユーザーがヒープ上のデータ(入力用バッファ)を書き換えられる - メニューの
3. Print safe_varで、ヒープ上の変数safe_varの内容を確認できる - メニューの
4. Print Flagが勝利判定(フラグ表示)に繋がっていそう
ステップ2: ソースコード(chall.c)を読む
次に
chall.c を確認して、
「フラグを出す条件」と「脆弱性」を探します。まずは初期化部分です。
// 重要部分だけ抜粋
input_data = malloc(INPUT_DATA_SIZE); // INPUT_DATA_SIZE = 5
strncpy(input_data, "pico", INPUT_DATA_SIZE);
safe_var = malloc(SAFE_VAR_SIZE); // SAFE_VAR_SIZE = 5
strncpy(safe_var, "bico", SAFE_VAR_SIZE);
ここで、ヒープ上に以下の2つが確保されます。接続時に表示されていたアドレスと対応しています。
input_data(5バイト)…初期値は"pico"safe_var(5バイト)…初期値は"bico"
次に書き込み処理です。
void write_buffer() {
printf("Data for buffer: ");
scanf("%s", input_data);
}
ポイントは
scanf("%s", input_data) です。
%s は 空白までの文字列を無制限に読み込む ため、
5バイトしかない input_data に長い文字列を入れると ヒープ上でオーバーフローします。最後に、flagが出る条件を確認します。
void check_win() {
if (strcmp(safe_var, "bico") != 0) {
printf("\nYOU WIN\n");
// flag.txt を表示
} else {
printf("Looks like everything is still secure!\n");
}
}
つまり、
safe_var が "bico" 以外になれば勝ちです。ステップ3: 攻撃方針(何をどう壊す?)
方針はシンプルです。
- メニューの「Write to buffer(2)」で
input_dataに長い文字列を書き込む input_dataの領域をはみ出して、隣に確保されているsafe_varを上書きする- メニューの「Print Flag(4)」で
check_win()を呼ぶ
safe_var は最初 bico なので、1文字でも変われば条件を満たします。ステップ4: 実際に攻撃してフラグを取得(nc接続のまま操作)
ステップ1で
nc 接続してメニューが表示されている状態のまま進めます。まずはアドレス差を確認
ステップ1の表示には
input_data と safe_var のアドレスが出ています。例:
input_data = 0x...72b0safe_var = 0x...72d0
この場合、差分は
0x20(10進数で 32)です。
つまり、input_data の先頭から見て 33文字目あたり(オフセット32の次) で safe_var の領域に到達し、先頭を上書きできる可能性が高い…という当たりが付きます(あくまで目安)。safe_var を上書きする
メニューで 2(Write to buffer)を選び、十分に長い文字列を入れます。
まずは例として、
A を 32 個 + 末尾に Z を付けます(safe_var の先頭が Z になれば勝ちです)。Enter your choice: 2
Data for buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ
次に 3(Print safe_var)で
safe_var が書き換わったか確認します。Enter your choice: 3
Take a look at my variable: safe_var = Z...
ここで
safe_var が bico 以外になっていればOKです。フラグを出す
最後に 4(Print Flag)を選びます。
Enter your choice: 4
YOU WIN
picoCTF{xxxxx}
※フラグはマスクしています。
これでフラグを取得できました。
まとめ
heap 0 は、
「ヒープだから安全」という油断を突いて、
ヒープ上の隣接領域をオーバーフローで上書きするタイプの入門問題でした。▼ポイントは以下の通りです。
scanf("%s", ...)のような 長さ制限のない入力は危険malloc(5)のような小さいバッファに長い入力を入れると ヒープオーバーフローsafe_var != "bico"にできれば勝ち、という勝利条件が分かれば解法は一直線
閲覧ありがとうございました!
NEXT
次におすすめ
読み終わったら、そのまま次へ
【picoCTF】format string 0 - フォーマット文字列脆弱性で任意メモリを読み出す
カテゴリ: Binary Exploitation難易度: Easy#picoCTF
次の記事へ →
同じカテゴリ/難易度/picoCTFでの表示順が近い記事を優先しておすすめしています。