Tcache LCTF2018-PWN-easy_heap
链接:https://pan.baidu.com/s/1UB8-3Iy-UpGObkW6B9YHCQ
提取码:cc8v
十个堆块一直在变位置,有点绕啊
grxer@grxer /m/h/S/c/p/heap> checksec easy_heap |
大致情况
最多十个堆块
malloc时存在一个null byte overflow,申请固定大小0xF8堆块,a1[a2] = 0;正好可以覆盖下一个堆块的inuse位为0
管理结构在0x202050处
struct |
没有其他漏洞了
大致思路是修改掉一个堆块的prevsize和inuse位,放入unsortedbin,利用unlink放入把一个中间的隔块污染放到unsorted链里来利用,泄露libc的同时构成double free,其实术语应该叫tcache dup,但是哥们感觉double free更形象一点
overlapping heap chunk隔块攻击泄露libc
先申请十个堆块0-9
for i in range(10): |
把下面地址当成我们的chunk0-9基地址
再把0-5释放掉,
for i in range(6): |
再释放9(释放9是为了防止直接释放789,会和topchunk合并,9进入tcachebin inuse位不置空)
tacache满了再释放6 7 8
for i in range(6, 9): |
为什么只有chunk6呢?678相邻释放完6再释放7,8前面的堆块为free状态,会触发unlinke
十个chunk都释放掉了
再把tcache chunk的7个chunk申请出来,我们才可以接触到unsort bin的chunk,
for i in range(7): |
把三个都申请出来作为chunk 7 8 9
这个时候chunk8((0x555555757b00))的inuse位为1,presize为0x200,可以把他前面的chunk7(0x555555757a00)申请出来,就可以覆盖chunk8(0x555555757b00)的inuse位
需要把前面8个chunk申请出来,才可以拿到chunk7(0x555555757a00)
for i in range(6): |
delete(8)
delete(7)
这里先释放8(0x555555757a00)有几点原因
- 可以填满填满tcache,7(0x555555757900)进入unsortbin,会有fd和bk指针写入unsortedbin地址
- 下一次会先申请到8(0x555555757a00),来覆盖chunk8(0x555555757b00)的inuse位
new(0xf8,b'null-byte-overflow')
目前申请到的0为0x555555757a00,0x555555757b00 previnuse位为0,也就是说0被污染为未使用实际却是使用状态,prevsize是0x200大小,0x555555757900也是free,
这个时候释放9(0x555555757b00)会触发unlinke,0x555555757900,0x555555757a00,0x555555757b00合并为一个大堆块,这样我们就可以强制把0x555555757a00加入unsortedbin里
delete(6)
delete(9)
这个时候再把0x555555757900申请出来,0x555555757a00会挂上链,fd和bk会有bin值,这时候0x555555757a00还在0处使用就可以show泄露unsortedbin了
for i in range(7):#拿走tcachebin里的 |
通过__malloc_hook和main_arean和unsortedbin的偏移找到libc基址
double free && __free_hook
目前state是这样的
再去申请一个堆不就可以double free了吗
**你可能会问我们之前不是在 https://grxer.gitee.io/2023/03/27/Tcache_poisoning_dup/ 说过unbutu18被patch掉了吗,怎么还能double free **
我们注意到在程序delete时
memset(*(void **)(16LL * index + ar), 0, *(unsigned int *)(16LL * index + ar + 8));
这一句就帮助我们绕过double free
if (__glibc_unlikely (e->key == tcache))
{
tcache_entry *tmp;
LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
for (tmp = tcache->entries[tc_idx];
tmp;
tmp = tmp->next)
if (tmp == e)
malloc_printerr ("free(): double free detected in tcache 2");
/* If we get here, it was a coincidence. We've wasted a
few cycles, but don't abort. */
}在检测doublefree时最简单的就是每一次释放都去遍历一下单链表,但是这样效率太低了,毕竟tcache的出现就是为了速度,ptmalloc2为了效率,在e->key == tcache时才会进行检测double free,因为在使用状态的chunk,e->key是数据区,基本上是不会满足->key == tcache的,64位程序只有2^48-1分之一的概率,使用状态的也没必要检测double free
memset(*(void **)(16LL * index + ar), 0, *(unsigned int *)(16LL * index + ar + 8)这一句会帮我们把原本要释放的key抹除掉,e->key != tcache自然不会检测doublefree
free掉0和9
new(0x10, p64(libc.dump(‘__free_hook’)+base))写入hook到单链表
new(0x10, ‘fuck’)
把a10拿出来
由于在满员下释放了两个堆,又申请了两个,满员了,free掉0和9之前需要再释放几个堆才可以申请下一个
new(0x10, p64(one_gadget))就可以申请到__free_hook-0x10去,hook掉了__free_hook为onegadget
exp
from pwn import * |