前言

学习自CTF Wiki Fastbin Attack, 根据学习情况略有补充和修改

介绍

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 进行攻击

原理

fastbin attack 存在的原因在于 fastbin 是使用单链表来维护释放的堆块的,并且由 fastbin 管理的 chunk 即使被释放,其 next_chunk 的 prev_inuse 位也不会被清空。 我们来看一下 fastbin 是怎样管理空闲 chunk 的。

一个例子

#include <stdlib.h>

int main(void)
{
void *chunk1,*chunk2,*chunk3;
chunk1=malloc(0x30);
chunk2=malloc(0x30);
chunk3=malloc(0x30);
//进行释放
free(chunk1);
free(chunk2);
free(chunk3);
return 0;
}

释放前

0x602000:   0x0000000000000000  0x0000000000000041 <=== chunk1
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000000
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000041 <=== chunk2
0x602050: 0x0000000000000000 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000000000
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000041 <=== chunk3
0x602090: 0x0000000000000000 0x0000000000000000
0x6020a0: 0x0000000000000000 0x0000000000000000
0x6020b0: 0x0000000000000000 0x0000000000000000
0x6020c0: 0x0000000000000000 0x0000000000020f41 <=== top chunk

执行三次 free 进行释放后

0x602000:   0x0000000000000000  0x0000000000000041 <=== chunk1
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000000
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000041 <=== chunk2
0x602050: 0x0000000000602000 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000000000
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000041 <=== chunk3
0x602090: 0x0000000000602040 0x0000000000000000
0x6020a0: 0x0000000000000000 0x0000000000000000
0x6020b0: 0x0000000000000000 0x0000000000000000
0x6020c0: 0x0000000000000000 0x0000000000020f41 <=== top chunk

此时位于 main_arena 中的 fastbin 链表中已经储存了指向 chunk3 的指针,并且 chunk 3、2、1构成了一个单链表

pwndbg> fastbin
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x602080 —▸ 0x602040 —▸ 0x602000 ◂— 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0

Fastbin Double Free

Fastbin Double Free

House Of Spirit

House of Spirit

Alloc to Stack

介绍

如果你已经理解了前文所讲的 Fastbin Double Free 与 house of spirit 技术,那么理解该技术就已经不成问题了,它们的本质都在于 fastbin 链表的特性:当前 chunk 的 fd 指针指向下一个 chunk。

该技术的核心点在于劫持 fastbin 链表中 chunk 的 fd 指针,把 fd 指针指向我们想要分配的栈上,从而实现控制栈中的一些关键数据,比如返回地址等。

例子

这次我们把 fake_chunk 置于栈中称为 stack_chunk,同时劫持了 fastbin 链表中 chunk 的 fd 值,通过把这个 fd 值指向 stack_chunk 就可以实现在栈中分配 fastbin chunk。

#include <stdio.h>
#include <stdlib.h>

typedef struct _chunk {
long long pre_size;
long long size;
long long fd;
long long bk;
} CHUNK, *PCHUNK;

int main(void) {
CHUNK stack_chunk;
void *chunk1;
void *chunk_a;

stack_chunk.size = 0x21;
chunk1 = malloc(0x10);

printf("stack_chunk address: %p\n", &stack_chunk);
printf("chunk1 address: %p\n", chunk1);

free(chunk1);

*(long long*)chunk1 = &stack_chunk;
malloc(0x10);
chunk_a = malloc(0x10);

printf("chunk_a address: %p\n", chunk_a);
return 0;
}

编译运行结果如下:

➜  work ./alloc_to_stack
stack_chunk address: 0x7fffffffdc60
chunk1 address: 0x555555756260
chunk_a address: 0x7fffffffdc60

小结

通过该技术我们可以把 fastbin chunk 分配到栈中,从而控制返回地址等关键数据。要实现这一点我们需要劫持fastbin 中 chunk 的 fd 域,把它指到栈上,当然同时需要栈上存在有满足条件的size值

Arbitrary Alloc

介绍

Arbitrary Alloc 其实与 Alloc to stack 是完全相同的,唯一的区别是分配的目标不再是栈中。 事实上只要满足目标地址存在合法的 size 域(这个 size 域是构造的,还是自然存在的都无妨),我们可以把 chunk 分配到任意的可写内存中,比如bss、heap、data、stack等等。

例子

在这个例子,我们使用字节错位来实现直接分配 fastbin 到**_malloc_hook的位置,相当于覆盖_malloc_hook来控制程序流程**。

#include <stdlib.h>
#include <stdio.h>

int main(void) {
void *chunk1;
void *chunk_a;

chunk1 = malloc(0x60);

free(chunk1);

*(long long *)chunk1 = 0x7ffff7dcfc30 - 0x3;
chunk1 = malloc(0x60);
chunk_a = malloc(0x60);

printf("chunk_a address: %p", chunk_a);
return 0;
}

其中 0x7ffff7dcfc30 根据本机调试得, 这个值的获得首先需要我们观察欲写入地址附近是否存在可以字节错位的情况。

pwndbg> x/16gx &__malloc_hook - 0x8
0x7ffff7dcfbf0 <_IO_wide_data_0+272>: 0x0000000000000000 0x0000000000000000
0x7ffff7dcfc00 <_IO_wide_data_0+288>: 0x0000000000000000 0x0000000000000000
0x7ffff7dcfc10 <_IO_wide_data_0+304>: 0x00007ffff7dcbd60 0x0000000000000000
0x7ffff7dcfc20 <__memalign_hook>: 0x00007ffff7a7b480 0x00007ffff7a7c800
0x7ffff7dcfc30 <__malloc_hook>: 0x0000000000000000 0x0000000000000000

0x7ffff7dcfc30 是我们想要控制的 __malloc_hook 的地址,于是我们向上寻找是否可以错位出一个合法的 size 域。因为这个程序是 64 位的,因此 fastbin 的范围为 32 字节到 128 字节(0x20-0x80),如下:

//这里的size指用户区域,因此要小2倍SIZE_SZ
Fastbins[idx=0, size=0x10]
Fastbins[idx=1, size=0x20]
Fastbins[idx=2, size=0x30]
Fastbins[idx=3, size=0x40]
Fastbins[idx=4, size=0x50]
Fastbins[idx=5, size=0x60]
Fastbins[idx=6, size=0x70]

通过观察发现 0x7ffff7dcfc2d 处可以实现错位构造一个 0x000000000000007f

pwndbg> x/16b 0x7ffff7dcfc30 - 0x3
0x7ffff7dcfc2d <__realloc_hook+5>: 0x7f 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7dcfc35 <__malloc_hook+5>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

因为 0x7f 在计算 fastbin index 时,是属于 index 5 的,即 chunk 大小为 0x70 的。

#define fastbin_index(sz)                                                      \
((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)

(注意sz的大小是unsigned int,因此只占4个字节)

而其大小又包含了 0x10 的 chunk_header,因此我们选择分配 0x60 的 fastbin,将其加入链表。

最后经过两次分配可以观察到 chunk 被分配到 0x7ffff7dcfc2d,因此我们就可以直接控制 malloc_hook 的内容(在我的libc中 realloc_hook 与__malloc_hook 是在连在一起的)。

小结

Arbitrary Alloc 在 CTF 中用地更加频繁。我们可以利用字节错位等方法来绕过 size 域的检验,实现任意地址分配 chunk,最后的效果也就相当于任意地址写任意值