point
use after free
1 | __int64 __fastcall show(unsigned int a1, __int64 a2) |
double free
1 | __int64 __fastcall del(unsigned int a1, __int64 a2) |
1 | void __fastcall alloc(unsigned int a1, __int64 a2) |
再来看堆上分配chunk的结构。固定分配0x60
的chunk大小,属于fastbin里面 的chunk,很明显这题我们需要用double_free来进行fastbin attack, 至于前面的use after free 有什么用当然,我们可以用过它来泄露heap的地址。double_free配合fastbin_attack,可以进行任意堆地址写。 __malloc_hook
的内存分布附近至少需要0x70的chunk才行,在这里我们做不到覆盖。所以这题往IO方向走。
先想覆盖_IO_list_all,把它覆盖为堆上地址,第一想到用unsortedbin attack让它指向unsortedbin,但是这里都是fastbin里面的chunk,先想个办法把unsorted chunk 弄出来。通过前面的double_free实现堆上任意写,把其中一个0x60chunk变成unsorted chunk 的size,再free它,这个unsorted chunk的bk指针也是可控。再清空fastbin,malloc(0x50)就能触发unsortedbin attack。
接着递进想法,把IO_list_all放到unsortedbin上以后,下一步就是把他_chain指向我们可控的堆上,计算_chain的偏移 IO_list_all = main_arena+88 &(IO_list_all->_chain) = main+88 + 104 => unsortbin+0x68 => smallbin[0x60] 所以需要把我们伪造的fp 放到smallbins 0x60上,如何放到smallbin 上去呢?,先需要把相应的chunk的放到unsortedbin上去,所以我需要把0x60放在unsortedbin上去,但是这里有一个问题,malloc(0x50),那么这里的unsortedbin上的0x60chunk是没有办法往smallbin 0x60上放的。所以这里的IO_list_all ->_chain不可控。这里就是这道题需要学到的point 。
暂且看当0x60不可控的时候,_chain指向哪里呢? &(IO_list_all->_chain)=smallbin[0x60] => smallbin[0x60] -> unsortbin+0x68-0x18 -> smallbins[0x50] &(IO_list_all->_chain->_chain) = unsortbin+0x50 + 0x68 = smallbins[0xb0]
通过_chain遍历io单链表,又一次指向了smallbins 上的 0xb0
大小的chunk , 这里我们就可以伪造出相应的unsorted chunk。
再来看一下遍历IO_list_all刷新缓冲区的过程
1 | int |
循环遍历_IO_list_all,也不会效验是不是合法的fp,仅判断需要不需要刷新缓冲区,所以我们不用担心每一个fp是否需要特殊化,仅仅关注_chain即可,最终让它指向我们伪造的fp上。伪造的fp需要满足需要刷新缓冲区的条件之外,还需要伪造vtable虚表中的_overflow处理函数。所以这道题意图已经很明显。
- 泄露heap_base_address
- 泄露libc_base_address
- double_free 下的fastbins attack 位置长度为0xb0的chunk,chunk上设置好fp的布局
- malloc 触发unsortedbin 遍历,fake_fp放到smallbins去。
再来看一下具体的布局。 图片
接着上exploit
1 | from pwn import * |
注释的代码是第第二种绕if的构造方法即
1 | (_IO_vtable_offset (fp) == 0 |