新增保护机制
tcache
tcache_entry结构体新增了一个指针key存放在chunk的bk处,tache_put写入,tcache_get清空
1 | tcache_put (mchunkptr chunk, size_t tc_idx) |
在free一个tcache时,新增一个判断分支:如果bk==key则会遍历tcache检查是否有相同的chunk
1 |
|
如何绕过呢??
修改bk使不进入循环分支
修改size从而修改tc_idx
house of botcake:合并chunk1 chunk2进unsortedbins,将chunk2链进tcache,从chunk1分配一个大chunk造成overlapped到chunk2修改其fd
unsortedbins
新增检查
size是否合理
next chunk的prev_size是否等于victim的size
双向链表完整性检查
next chunk的prev_inuse位是否为0
1 | if (__glibc_unlikely (size <= 2 * SIZE_SZ) |
malloc overlap
当向后合并(低地址)时,会检查上个chunk的size和当前chunk的prev_size是否相等
1 | /* consolidate backward */ |
以下假设有off by null漏洞👇
在之前的glibc版本中还存在双向链表检查,我们一般是通过将要合并的chunk放入unsortedbins中获得系统给的fd和bk来绕过以达到chunk overlapping的目的。但是2.29的新机制无法通过这个方式绕过双向链表检查。
如何绕过呢???
主要思路是利用从largebin中分配的残留指针 fd_nextsize / bk_nextsize,因为largebin中只有一个chunk时,这两个指针指向自己,先通过部分覆盖修改fb_nextsize的第一个字节,使其指向一个我们可以控制bk的chunk,例如利用smallbins或unsortedbins的机制部分覆盖bk指向fake_chunk。这时bk_nextsize仍然指向自己。利用同样的思路将其链入fastbins中部分覆盖第一个字节使其指向fake_chunk。这样双向链表就构造好了
总结一下就是利用smallbins会在bk写入堆地址,fastbins会在fd写入堆地址,然后部分覆盖构造双向链表。但是我们无法保证堆地址第二个字节是\x00,所以这种攻击方式有6.25%的概率会成功:]
top chunk
1 | if (__glibc_unlikely (size > av->system_mem)) |
GG
Hitcon-CTF2019-one_punch_man
保护全开
1 | Arch: amd64-64-little |
程序有5个功能:
add
1 | unsigned __int64 __fastcall Add(__int64 a1, __int64 a2) |
在这个函数里允许我们分配0x80-0x400大小的chunk,并把初始化chunk的内容先保存在栈上再通过strncpy传送到堆里,rebase(0x4040)会依次保存chunk地址和大小且只能同时分配三个。注意这里分配chunk使用的是calloc,即不会从tcache中取chunk。
free
1 | void __fastcall Delete(__int64 a1, __int64 a2) |
这里没有对指针清空,存在uaf
backdoor
1 | ssize_t __fastcall Magic(__int64 a1, __int64 a2) |
程序留了一个后门函数可以调用malloc,限制了利用条件 *(heap_base+0x30) > 6
edit
1 | ssize_t __fastcall rename(__int64 a1, __int64 a2) |
show
1 | ssize_t __fastcall Show(__int64 a1, __int64 a2) |
因为存在uaf,所以我们可以很容易地leak出堆地址和libc地址,同时存在edit函数可以修改tcache的fd,只要可以调用程序留给我们的后门函数中的malloc就可以实现任意地址写
那么如何将heap_base+0x30处的值修改为一个large value呢
这里就要用到glibc2.29下的一种利用手法tcache_stashing_unlink_attack
先贴主要用到的glibc源码
1 | if (in_smallbin_range (nb)) |
因为我们无法像how2heap中修改fake_chunk->fd为一个可写的地址,所以我们只在tcache中预留一个位置
那么如何使得smallbins存在两个chunk并且相应大小的tcache未满呢??
这里用到一个技巧,通过分配并释放一个较大的chunk,然后利用分割机制拿到我们想要的size
1 | add(0,"aaaa",0x210) |
此时的堆布局:
1 | pwndbg> bins |
然后修改chunk1的bk为heap_base+0x20,再分配一个相应大小的chunk触发攻击
chunk1会链入相应的tcache,heap_base+0x20处的fake_chunk会被当成bck,并会将smallbin赋值给其fd
1 | pwndbg> x/10gx 0x564cf556b000 |
此时tache已经被我们打烂了。。至于怎么烂的我也不知道。。还好我们之前已经修改了fd,这时直接调用malloc就可以劫持malloc_hook
1 | pwndbg> bins |
因为这题使用 seccomp 开启了沙箱保护,只有白名单上的系统调用可以使用。
1 |
|
我们无法执行execve系统调用,只能用过orw(open/read/write)来读取flag
因为调用add函数时,程序先将我们的输入放在了栈上,所以考虑在此处构造rop
1 | pwndbg> stack 20 |
在调用calloc里有一系列抬高栈顶的操作,将malloc_hook改为add rsp,48h;ret就可以执行我们构造的rop
完整exp
1 | from pwn_debug import* |