KCTF jediescape
前言
多线程的题,做题的时候漏洞没找到,记录一下
程序逻辑
程序有三个功能,分配,释放和输出。
分配部分可以自己选择bss存储数组的index,size不能为0,之后输入数据
Free部分读取的是一个范围start-end,释放的是start-end-1的chunk,这里start小于end且end不大于255,之后选择线程数量,通过start_routine函数进行释放
start_routine函数里是一个循环,_InterlockedExchangeAdd8这个函数的功能是arg1指向的位置值+arg2,即(end_index+8)+1。返回值为(end_index+8)的初始值。看Free函数可以看到end_index2+8位置的v12是char类型,这意味着v12为255的时候,+1即变为0。
start_routine里每次执行这个函数,之后比较end_index和start_index,相等即退出,否则释放(bss数据不为空)
Puts函数在数组index处值不为空时输出
漏洞利用
这里利用的是start_routine的逻辑问题,我们取start为254,end为255,线程数为2。在第一个线程里,chunk_254被释放后变为255,执行函数,255+1变成了0,函数返回值为255,比较之后符合条件break。此时在第二个线程里*(end_index+8)的值为0,+1变为1,返回值为0,成功绕过了检查,相当于从0-254又释放了一遍,254的块double free。
在0位置处malloc一个0x90的堆,根据刚才的原理,0会被释放,之后放入unsorted bin,但是因为数据没有清空,可以Puts泄露Libc
调试发现每个线程都有自己的thread_arena,即线程分配区。每次子线程结束的时候都会把tcache中的chunk放到对应大小的fastbin或者unsorted bin。这也导致我们不能用刚才那种最简单的方式(tcache加入fastbin的时候有重复检查,start可以选择253)
还有一个发现是malloc一个fastbin范围的内存时,如果tcache没有满,fastbin就会放入tcache中(晓晨师姐说之前0CTF还是LCTF有道类似的题,todo),我们可以利用fast bin的double free修改其fd,再malloc的时候即可从tcache里分配到指定内存地址。
exp.py
1 | #coding=utf-8 |