第2回:初学者のためのバイナリ解析
カテゴリ:バイナリ解析、gdb-peda、pwntools、ASLR
➊ 概要/目的
第1回ではスタックバッファオーバーフロー(以下BoFと呼ぶ)を学ぶため、前提としてバイナリ解析の基礎について説明した
今回はBoFを利用してプログラム内にある特定の関数を呼び出してみたいと思う
■ ブレイクスルー観点
1⃣ 簡単なプログラムを実行して任意の関数を実行できることを理解する
2⃣ pwntoolsを使用したシェルコードを作成できる
3⃣ セキュリティ機能(ASLR、スタック保護)を意識する
➋ 事前準備
・gdb-peda(gdbに拡張機能を追加しバイナリ解析の支援を行うためのツール)をインストールする
git clone https://github.com/longld/peda.git ~/peda echo "source ~/peda/peda.py" >> ~/.gdbinit
・pwntools(バイナリ解析を支援するためのPythonライブラリ)をインストールする
apt-get update apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential python3 -m pip install --upgrade pip python3 -m pip install --upgrade pwntools
・ASLRというセキュリティ機能を無効化しておく必要がある(バイナリを実行する度に変化するメモリアドレスを固定させる)
$ sudo sysctl -w kernel.randomize_va_space=0
今回実行するプログラムは $ cat bof_zeizyaku.c
#include <stdio.h>
void zeizyaku(){
printf("hacked\n");
}
void vuln(){
char hackme[12];
scanf("%[^\n]",hackme);
}
int main(){
vuln();
printf("failed\n");
return 0;
}
以下はスタックのイメージ図となる
今回はコンパイル時にセキュリティ機能を無効化させる必要がある
$ gcc -m32 -g -fno-stack-protector bof_zeizyaku.c -o bof_zeizyaku
・スタック保護(Stack Protector)とは?
バッファオーバーフローの脆弱性を防ぐために導入されたセキュリティ機能、関数内で大きなローカル変数(配列や文字列)を扱う場合、バッファオーバーフローが発生するとスタック上の制御構造(リターンアドレスや関数ポインタなど)が上書きされ、これを悪用した攻撃(リターンアドレスの書き換えによる不正コードの実行)が行われる可能性がある
・「-fno-stack-protector」 の意味このスタック保護機能が無効にしている
つまりコンパイルされたコードにはカナリア値が挿入されず、スタック保護が行われない
これによりスタックオーバーフローが発生した場合、攻撃を防止する仕組みがなくなるためプログラムが脆弱になる可能性がある
$ gdb –q bof_zeizyaku
(gdb) となればOK
ちなみに上記のプログラムを実行して「aaaaa」と入力すると、以下の結果が出力される
今回はBoFを利用してfailedではなく、zeizyaku関数を呼び出すことでhackedを表示させたい
❸ 方針
① hackmeからリターンアドレスまでの距離を調べる
② zeizyaku関数のアドレスを調べる
③ リターンアドレスをzeizyaku関数へジャンプするように書き換える
まずはプログラム全体を確認してみる
❹ hackmeからリターンアドレスまでの距離を調べる
abcd..みたいに順番に入れていく方法でも調べることは可能である
しかし入力数が多いときpattcコマンドを利用する、これは4byteを切り取ったときに距離が分かるようになっている
gdb-peda$ pattc 70 AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3
上記を30個くらいをコピーしてプログラムに入力する
これを入れるとEIPが'(AAD'になっていることが分かる(赤枠)、ここからhackmeから戻りアドレスまでのバッファを調べる
gdb-pedaのpattoコマンドを使えば、距離が24byteあることが分かる
ここから24byteの適当な文字列'A' + 任意の関数のアドレスを指定すれば良いことが分かる
❺ pwntoolsを利用してシェルコードを実行する
まずはzeizyaku関数のアドレスを調べる必要がある
次に「step2_exploit.py」を作成する
実行するとhackedが表示されたことが確認できる
❻ まとめ
本内容ではスタックバッファオーバーフローの脆弱性をついたが、これは現実的ではない
(コード内のzeizyaku関数を事前に知っている必要があったり等)
しかし攻撃者はヘッダファイルを利用することでシェルを起動させることが可能なため、コード内容を理解する必要がなかったりする
(ヘッダファイルにはbashを呼び出せるライブラリ等が含まれている)
次回はシェルコードを実行して/bin/bashを呼び出してみたいと思う
[番外] pattcを使わずにhackmeとリターンアドレスまでの距離を調べる方法
本編ではpwntoolsを利用していたが自力で距離を求める方法も紹介する
まずはbreakpointを13行目に作って、vuln後のリターンアドレスを調べる
stepiで一つだけアセンブリ処理を進める
9行目で「AAAAAAAAA」入力する
hackmeとリターンアドレスまでの差が24バイトになっていることが確認できる