FrontPage > Armadillo-9 > exploit libpng_arm
libpng(ARM)†
ARM版の攻撃方法
- CPU: ARM9
- OS: Debian Sarge(2.4.31-a9-2)
- アットマークテクノのページで配布されているものを使った
- target: imagemagick-6.0.4(Debian Sarge 版)
- target: libpng-1.2.5(オリジナル版)を少し改造する。
- コンパイラに渡すオプション -O3 を外す。
- imagemagick 側の対策を回避するために Warning のメッセージを少し変更する。(後述する)
- imagemagick に脆弱性のある libpng を LD_PRELOAD で差し込んで、脆弱性を作ってから攻撃する。
- LD_PRELOAD で複数のライブラリを指定するときはスペース区切りである。
攻撃対象†
- バッファオーバーフロー脆弱性のある箇所は x86 版と同じで、pngrutil.c 1240行目付近だが、攻撃方法が異なる
- x86
- リターンアドレスを書き換えて、攻撃コードにリターンさせる。
- ARM版の攻撃も可能だと思われる(未確認)
- ARM
- リターンアドレスを書き換える方法は使えない。
- libpng が用いる構造体を偽者のデータとすり替えて、攻撃コードにジャンプさせる。
リターンアドレス書き換えが使えない理由†
- まず両者ではスタックの積み方が異なる。
- x86: [readbuf] [...] [ret_addr] [arg2, 1, 0 逆かも?]
- ARM: [readbuf] [arg2] [arg1] [arg0] [20bytes] [ret_addr]
- readbuf: オーバーフローが起こるバッファ
- arg_n: n 番目の引数
- arg0: png_ptr
- arg1: info_ptr
- arg2: length
- ret_addr: リターンアドレス
バッファオーバーフローを起こす部分は次のコードの [!] の行である。
その後、png_ptr(arg0) を利用しているため、下手にpng_ptr を壊すと関数の return までたどり着けず、リターンアドレスに指定したアドレスに制御を飛ばすことができない。
png_crc_read(png_ptr, readbuf, (png_size_t)length); //[!]
png_ptr->num_trans = (png_uint_16)length;
- x86 は、引数がリターンアドレスの後ろに居るので、リターンアドレスだけ書き換えて、引数は書き換えない、という手法が取れる。
- ARM: [readbuf](overflow!!)[(x)...] [ret_addr -> addr_attack_code] [args]
- ARM は、引数がリターンアドレスより前に居るため、リターンアドレスを書き換えると必ず引数を壊してしまう問題がある。
- ARM: [readbuf](overflow!!)[(x)arg2] [(x)arg1] [(!!)arg0] [(x)20bytes] [ret_addr -> addr_attack_code]
ARMでの攻撃手法†
- png_crc_read でバッファオーバーフローを起こした後、以下のような関数が呼ばれる。
- png_crc_finish(pngrutil.c 94行目から。pngrutil.c lines 1300行目付近で呼び出している)
- png_crc_error(pngrutil.c 130行目から)
- png_read_data(pngrio.c 27行目から)
- png_read_data では第一引数の png_ptr 内の関数ポインタを呼び出す部分がある。
void /* PRIVATE */
png_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
{
png_debug1(4,"reading %d bytes\n", (int)length);
if (png_ptr->read_data_fn != NULL)
(*(png_ptr->read_data_fn))(png_ptr, data, length); //[!]
else
png_error(png_ptr, "Call to NULL read function");
}
- png_ptr のデータをすりかえて、関数ポインタに攻撃コードを指すアドレスを渡してやることで、攻撃コードを呼び出す。
攻撃の詳細†
- リターンアドレスは使わない。
- 書き換えるのは png_ptr(arg0) である。
- 関数ポインタが攻撃コードのアドレスを指すようにするため、攻撃コードのアドレスで埋め尽くした領域を指すようにする。
- png_ptr(0x00112233) -> 0x00112233 [本来のデータ領域]
これを書き換えて
png_ptr(0xdeadbeaf) -> 0xdeadbeaf [攻撃コードの開始アドレスで埋め尽くした領域]
とする。これによって関数ポインタが攻撃コードの開始アドレスを指すようになり、プログラムを騙して、攻撃コードを call させることができる。
計算すべきアドレス†
- 必要なもの
- readbuf(オーバーフローさせるバッファ)の開始地点
- 計算するもの
- 攻撃コードの開始地点
- png_ptr が指す領域(攻撃コードの開始アドレスで埋め尽くした領域)のアドレス
攻撃作成の際の注意点†
- スタックの開始位置が多少ずれても平気なように、png_struct のサイズ(684バイト)の 1.5〜2倍くらいは欲しいところである。
- 同様に攻撃データの開始地点も余裕を持って、nop(ARM だと mov r0, r0)で埋めた領域内を指すようにすべき。
攻撃データの詳細†
データの配置イメージ†
0x021: [tRNS チャンクのサイズ(注:ビッグエンディアンで指定する)]
0x025: [tRNS]
0x029: [ゴミデータ]
0x131: [arg0 の書き換え用データ: ptr_fraud_png]
0x149: [リターンアドレス(今回は使わない)]
0x14d: [攻撃コードの先頭アドレス ptr_attack で埋め尽くしている領域]
0x3c1: [同上] <- ptr_fraud_png の指す位置
[同上]
0x671: [nop で埋め尽くしている領域]
0x875: [同上] <- ptr_attack の指す位置
[同上]
0xab5: [mov r5, pc]
0xab9: [sub r5, r5, #8]
0xabd: [攻撃コード]
0xb11: [swi 0x0090000b]
0xb19: [攻撃用データ]
- スタックの開始位置がずれても割と平気な攻撃コードになっている。
- x86 のときはかなりシビアにアドレスを指定しなければならなかったが、今回はスタックの開始位置がずれても割と平気な攻撃コードになっている
攻撃用データの配置イメージ†
offset
+0 ["/bin"]["//sh"][NULL]
+12 ["-ppi"][NULL]
+20 ["touc"]["h${I"]["FS}/"]["tmp/"]["itwo"]["rked"][NULL]
+48 [argv[0]][argv[1]][argv[2]][argv[3]]
+64 [env]
- アクセス方法は、[r5 + 100(攻撃コードのサイズ) + offset] である。攻撃コードが長いなら、+100 の部分を増やして対応する。
- r5 自体は mov r5, pc の命令があるアドレスを指している。
実際に使用した値†
- 攻撃データ全体のサイズ: 2888バイト
- ベースとなるアドレス(readbuf の開始地点)はいくつかの場合を見て、0xbfffa400 とした
- /tmp/ で実行すると、0xbfffa4a0
- /home/username/ で実行すると、0xbfff490
- /home/username/apps/libpng_exploit/ で実行すると、0xbfff460
- 攻撃コードの先頭アドレスで埋めた領域、の真ん中あたりを指すポインタ(arg0 に上書きする値)
- nop〜攻撃コード〜攻撃用データ、の先頭部分 nop の領域を指すポインタ(領域を埋めるときに使う値)