Format String Bug(弊研究室の某課題について考える8日目)
はじめに
この記事は弊研究室の某課題について考えるの8日目の記事です
今回ですが超優秀な記事があるのでそこを自分なりにわかりやすく説明していきます。
また攻撃方法とかはよくあるので仕組みについて重点的にやります。
お品書き
- Format String Bug(FSB)
- Format String Attack(どういうことができるかだけ)
Format String Bug
Format String(書式指定子)を扱える関数においてユーザが自由に書式指定子を配置できるバグです。書式指定子とはprintf等の関数を使うときにprintf("%d",1);
のようこの%d
が書式指定子です。書式指定子の例は以下の表に..
指定子 | 引数 | 用途 |
---|---|---|
%c | char | 1文字出力 |
%s | char * | 文字列出力 |
%d | int, short | 整数を10進で出力 |
%x | int, short, unsigned int, unsigned short | 整数を16進で出力 |
%n | 整数へのポインタ | 文字数を指定した変数に格納 |
%p | 変数 | 変数のポインタの値を16進で出力 |
さてこのバグが存在するプログラムを例に示します。
/*fsb.c*/ #include<stdio.h> int main(int argc, char *argv[]){ printf(argv[1]); return 0; }
これをコンパイルして実行すると下のようになります。
$ ./a.out AAAA AAAA$
ここで書式指定子を入れた入力をしてみます。
$ ./a.out AAAA%pBBBB AAAA0x7ffedde001a8BBBB$
なにか変な値が出力されています。これがFSBです。ちなみに出力されているのはスタックの値です。
FSBの理屈
まずは普通のprintf関数のスタック状況を見てみましょう。
printf("%d %d %d", 1, 2, 3)
まぁこうなっています。こうなっているものと思っておいて問題ないです。2日めの記事を読み込んだ人なら厳密には違うとわかるでしょうがこの節の最後に捕捉します。
さてではFSBバグのあるさっきのものだとどうなっているか。
こうなっています。なのでAAAA
とBBBB
の間に変な値が出力されていたのですね。これがFSBです。次からprintf関数の捕捉していくのでどうでもいい人は飛ばして...
printf関数の捕捉
C言語の関数ですが、2日目にも合った通り引数はレジスタ積まれます。なのでprintf("%d %d %d", 1, 2, 3)
ではrsi
やrdx
レジスタに1
,2
等の変数は格納されています。ではもしレジスタに引数が格納されていなかったら?その時は引数はレジスタに積まれているとして足りない引数をレジスタから引っ張ります。さらに足りなければスタックから引っ張ります。
引数の積まれ方はOSによって変わるのでそこらへんは注意してください
/*printf_sample.c*/ #include<stdio.h> int main(){ printf("%d %p",1); return 0; }
出力はこんな感じ
$ ./a.out 1 0x7fffffffe598
gdb-pedaで見てみます。
[----------------------------------registers-----------------------------------] RAX: 0x0 RBX: 0x0 RCX: 0x0 RDX: 0x7fffffffe598 --> 0x7fffffffe7df ("XDG_SESSION_ID=73") RSI: 0x1 RDI: 0x4005d4 --> 0x7025206425 ('%d %p') RBP: 0x7fffffffe4a0 --> 0x400550 (<__libc_csu_init>: push r15) RSP: 0x7fffffffe4a0 --> 0x400550 (<__libc_csu_init>: push r15) RIP: 0x400539 (<main+19>: call 0x400400 <printf@plt>) R8 : 0x4005c0 (<__libc_csu_fini>: repz ret) R9 : 0x7ffff7de7ab0 (<_dl_fini>: push rbp) R10: 0x846 R11: 0x7ffff7a2d740 (<__libc_start_main>: push r14) R12: 0x400430 (<_start>: xor ebp,ebp) R13: 0x7fffffffe580 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x40052a <main+4>: mov esi,0x1 0x40052f <main+9>: mov edi,0x4005d4 0x400534 <main+14>: mov eax,0x0 => 0x400539 <main+19>: call 0x400400 <printf@plt> 0x40053e <main+24>: mov eax,0x0 0x400543 <main+29>: pop rbp 0x400544 <main+30>: ret 0x400545: nop WORD PTR cs:[rax+rax*1+0x0] Guessed arguments: arg[0]: 0x4005d4 --> 0x7025206425 ('%d %p') arg[1]: 0x1 [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe4a0 --> 0x400550 (<__libc_csu_init>: push r15) 0008| 0x7fffffffe4a8 --> 0x7ffff7a2d830 (<__libc_start_main+240>: mov edi,eax) 0016| 0x7fffffffe4b0 --> 0x0 0024| 0x7fffffffe4b8 --> 0x7fffffffe588 --> 0x7fffffffe7bb ("/home/rize/Documents/adventor/a.out") 0032| 0x7fffffffe4c0 --> 0x1f7ffcca0 0040| 0x7fffffffe4c8 --> 0x400526 (<main>: push rbp) 0048| 0x7fffffffe4d0 --> 0x0 0056| 0x7fffffffe4d8 --> 0x4d2a215a3833886d [------------------------------------------------------------------------------]
RSIとRDXの値が出力されていますね。
引数を増やしていくとスタックの値が出力されるので最初の説明はあながち間違ってもいないのです。
Format String Attack
おまけです。弊研究室の某課題について考える番外編とかやるならその時にしっかり記事作ります
ではFSBを使った攻撃について説明します。書式指定子の%x
でスタックを表示することが%n
で書き込むことができます。
%s
で指定されたアドレスが指す文字列を出力することができます。
例えば
./a.out AAAA.%x.%x.%x.%x.--snip--.%x AAAA---snip--.41414141.--snip--
41414141
が出力されるのを[x]番目とした際に
AAAAの部分に読み出したいアドレスをセットし%[x]$s
を用いることでメモリ内容を出力することが可能です。
終わりに
正直この内容一日でまとめるのきついのじゃが
そして7日目の記事、画像を使いまわしたらリターンアドレス消し忘れました。ごめんね。