堆漏洞(一)
文章目录
前言
堆的题基础的攻击方法就是覆盖got表里面的地址为system,然后执行那个函数之后就 getshell。
RELRO是Partial可以写入,有的时候遇到 Full RELRO的话,就对 malloc_hook 或者 free_hook进行覆盖。
一、off by one
1.什么是off by one
off-by-one漏洞是一种特殊的溢出漏洞,off-by-one指程序向缓冲区中写入时,写入的字节数超过了这个缓冲区本身所申请的字节数并且只越界了一个字节。
2.形成原理
边界验证不严谨:
1.循环边界不严谨:使用循环语句向堆块中写入数据时,循环的次数设置错误,导致多写了一个字节
int my_gets(char *ptr,int size)
{
int i;
for(i = 0; i <= size; i++)
{
ptr[i] = getchar();
}
return i;
}
int main()
{
//
char *chunk1,*chunk2;
chunk1 = (char *)malloc(16);
chunk2 = (char *)malloc(16);
puts("Get Input:");
my_gets(chunk1, 16);
return 0;
}
2.对字符串长度判断有误:
int main(void)
{
char buffer[40]="";
void *chunk1;
chunk1=malloc(24);
puts("Get Input");
gets(buffer);
if(strlen(buffer)==24)
{
strcpy(chunk1,buffer);
}
return 0;
}
首先创建了一个40字节的字符串buffer,然后又创建了一个24字节的堆chunk1。接着从外部接收字符串并存放在字符串buffer中,然后判断buffer中的字符串的长度是否为24个字节,如果是将这段字符串放在堆中。其实乍一看来并没有什么问题,但是有一个隐形的东西,就是结束符\x00。strcpy函数在拷贝的时候也会将结束符\x00存入堆块中,也就是说我们想chunk1中一共写了25个字节,这就导致了chunk1溢出了一个字节
二、chunk extend-> chunk overlapping
1.介绍
chunk extend 是堆漏洞的一种常见利用手法
chunk overlapping(块重叠):是一种安全隐患,实现overlapping,就可以控制某个chunk。就是使两个chunk中的一个size增大,包含了另一个chunk,free第一个chunk就把第二个也free了;多出来的空间就是你可以再次申请的chunk。
所以这两个都不是堆中的漏洞。
因此chunk extend 的使用条件:
- 程序中存在基于堆的漏洞
- 漏洞可以控制 chunk header 中的数据
2.extend使用
对 inuse 的 fastbin 进行 extend
1 //gcc -g test1.c -o test
2 #include<stdio.h>
3 int main(void)
4 {
5 void *hollk, *hollkr1;
6 hollk = malloc(0x10);//分配第一个0x10的chunk
7 malloc(0x10);//分配第二个0x10的chunk
8 *(long long *)((long long)hollk - 0x8) = 0x41;// 修改第一个块的size域
9 free(hollk);
10 hollk1 = malloc(0x30);// 实现extend,控制了第二个块的内容
11 return 0;
12 }
在 malloc(0x50) 对 extend 区域重新占位后,其中 0x10 的 fastbin 块依然可以正常的分配和释放,此时已经构成 overlapping,通过对 overlapping 的进行操作可以实现 fastbin attack
fastbin attack
利用fastbin分配原理的漏洞,利用要求是我们能够修改“已释放”堆块。 通常情况下与double free、use after free和chunk extend连用。 fastbins是管理在malloc_state结构体重的一串单向链表,分为0x20-0x807个链表(默认情况下)。
对 inuse 的 smallbin 进行 extend
1 //gcc -g test2.c -o test2
2 #include<stdio.h>
3 int main()
4 {
5 void *hollk, *hollk1;
6 hollk = malloc(0x80);//分配第一个 0x80 的chunk1
7 malloc(0x10); //分配第二个 0x10 的chunk2
8 malloc(0x10); //防止与top chunk合并
9 *(long *)((long)hollk-0x8) = 0xb1;
10 free(hollk);
11 hollk1 = malloc(0xa0);
12}
*(int *)((int)hollk-0x8) = 0xb1;这段代码也是将chunk1的size部分进行了更改,将原有的0x90扩展到了0xb0。这就导致了chunk2被chunk1所包含。
chunk1进的是unsortbin,有两种情况下进unsortbin:
- 当一个较大的 chunk 被分割成两半后,如果剩下的部分大于 MINSIZE,就会被放到 unsorted bin 中
- 释放一个不属于 fast bin 的 chunk,并且该 chunk 不和 top chunk 紧邻时,该 chunk 会被首先放到 unsorted bin 中
那么这个例子就满足第二种情况,不属于fastbin中的空闲块,并且不和top chunk相邻。其实这个例子和第一个例子差不多,因为chunk1和chunk2合并之后的chunk的大小超过了fast bin的最大接收值,所以不进fast bin,并且chunk3的size标志位变成了0,证明前一个块chunk2是一个释放的状态。接下来的过程也是一样的,再次申请一个0xa0大小的chunk时,会从unsort bin中提取。连带着chunk2中的内容也会被提取出来,这样一来再次对chunk1进行操作,从而达到操作chunk2的目的
对 free 的 smallbin 进行 extend
1 //gcc -g test3 -o test3
2 #include<stdio.h>
3 int main()
4 {
5 void *hollk, *hollk1;
6 hollk = malloc(0x80);//分配第一个0x80的chunk1
7 malloc(0x10);//分配第二个0x10的chunk2
8 free(hollk);//首先进行释放,使得chunk1进入unsorted bin
9 *(long *)((long)hollk - 0x8) = 0xb1;
10 hollk1 = malloc(0xa0);
11}
先进行释放,然后重新修改chunk1的size大小,释放之后的chunk1依然进入了unsort bin中,在修改完size之后重新申请0xa0的时候会从unsort bin中申请,这个时候大家需要总结一下,其实各个bin中存放的只有chunk的首地址,真正判断多大还得是去看这个chunk的size大小,所以再次申请的时候依然还可以对chunk2进行控制
通过 extend 后向 overlapping
//gcc -g test4.c -o test4
#include<stdio.h>
int main()
{
void *hollk, *hollk1;
hollk = malloc(0x10);//分配第1个 0x10 的chunk1
malloc(0x10); //分配第2个 0x10 的chunk2
malloc(0x10); //分配第3个 0x10 的chunk3
malloc(0x10); //分配第4个 0x10 的chunk4
*(long *)((long)hollk - 0x8) = 0x61;
free(hollk);
hollk1 = malloc(0x50);
}
通过 extend 进行后向 overlapping,这也是在 CTF 中最常出现的情况
在 malloc(0x50) 对 extend 区域重新占位后,其中 0x10 的 fastbin 块依然可以正常的分配和释放,此时已经构成 overlapping,通过对 overlapping 的进行操作可以实现 fastbin attack
通过 extend 前向 overlapping
//gcc -g test5.c -o test
#include<stdio.h>
int main(void)
{
void *hollk1, *hollk2, *hollk3, *hollk4;
hollk1 = malloc(128);//smallbin1
hollk2 = malloc(0x10);//fastbin1
hollk3 = malloc(0x10);//fastbin2
hollk4 = malloc(128);//smallbin2
malloc(0x10);//防止与top合并
free(hollk1);
*(int *)((long long)hollk4 - 0x8) = 0x90;//修改pre_inuse域
*(int *)((long long)hollk4 - 0x10) = 0xd0;//修改pre_size域
free(hollk4);//unlink进行前向extend
malloc(0x150);//占位块
}
前向 extend 利用了 smallbin 的 unlink 机制,通过修改 pre_size 域可以跨越多个 chunk 进行合并实现 overlapping
smallbin 的 unlink 机制:
unlink是在smallbin被释放的时候的一种操作,是将当前物理内存相邻的free chunk进行合并,简单的讲就是我们在free一个smallchunk的时候,如果它前面或者后面的chunk有空闲的,即in_use位为0时,就将前面或后面的chunk连在一起合成一个chunk;
smallbin的数据结构:prev_size,size,fd,bk;
因为smallbin被释放后是用双链串在一起的,这就使目前unlink操作时,有一定的检查机制,主要检查我们的双链是否是合法的;
主要检查fd,bk等指针:
三、unlink
1.Unlink是什么
unlink其实是libc中定义的一个宏,在malloc.c中,在执行free()函数时执行了 _int_free()函数,在_int_free()函数中调用了unlink宏,大概的意思如下(注意_int_free()是函数不是宏)。
2.unlink的过程
FD = P -> fd = target addr -12
BK = P -> bk = expect value
FD -> bk = BK,即 *(target addr - 12 + 12) = BK = expect value
BK -> fd = FD,即 *(expect value + 8) = FD = target addr - 1
堆释放的例子,依次释放了first_chunk、second_chunk、third_chunk,也就是说首先释放的是first,然后释放的是second,最后释放的是third。
由于chunk是双向链表连接的,所以让first_chunk的bk指向third_chunk的prev_size,让third_chunk的fd指向first_chunk的prev_size,就可以把second_chunk摘出来,就可以控制second_chunk进行攻击。
总结
off by one是溢出漏洞
chunk overlapping和unlink是使得有一个chunk能不被系统控制。