检查
1 | $ file secretgarden |
64位elf,动态链接,去符号表,保护全开
分析
add
添加,每朵花会malloc(0x28)来存储所有信息,会malloc任意大小来存储花名,所有花的堆块地址将放到bss段 的一个list中,最多能有100朵花
1 | int add() |
show
遍历列表,打印花的信息
1 | int show() |
del
删除花,并free掉存放name的堆块,但是并没有检查花的存在位,free后也没有将指针清0
1 | int del() |
clear
如果花已经被删除,则可以用clear功能free掉对应的那个0x28大小的堆块。
1 | unsigned __int64 clear() |
数据结构
每朵花的结构如下:0x28的堆块前8个字节是存在位,接下来的8个字节是name堆块的地址,接下来的0x18个字节保存颜色名字
1 | bss malloc(0x28) |
漏洞点
漏洞点就是del函数里面没有检查花的存在位和清零指针,且堆块大小可控,造成了double free
调试模版
为了应对pie等调试升级后的全新模版:
1 | #!/usr/bin/env python3 |
利用
堆排布泄漏libc
我们现在拥有地址写的能力,但是不知道写哪,所以首先要做的是泄漏地址信息,思路是free一个unsortedbin,通过main_arena泄漏libc基址,因为我们del花后,存在位会清空,所以我们还是需要申请回来才能输出信息,同时add时通过read函数来获取输入,不会添加截断字符,所以满足泄漏条件
需要注意有两点:
- 我们还需要malloc一个fastbin来防止我们的unsortedbin被top chunk合并
- 我们再次申请的时候题目并不会用之前申请的位于flower_list[0]的0x28的堆块,会再次申请一个堆块放在flower_list[1]中,所以我们需要预备好一个0x30的堆块放到fastbin中准备使用,以免我们放入unsortedbin中的堆块被切割
这里本地的libc和题目给的libc并不一样,但是我们只需要更改io = process(elf.path,env={"LD_PRELOAD" : libc.path})
的libc就可以强行加载我们所需的libc(猜测大版本需要一样)
那我找了一下午题目的libc版本究竟图个啥….淦
这里使用的是gdb的gef插件,看堆感觉方便一点
1 | #!/usr/bin/env python3 |
任意地址分配修改__malloc_hook
因为libc中常利用的函数指针为__malloc_hook和__free_hook,并且题目中的DoubleFree还需要构造size大小合适的伪堆块,所以需要动态调试观察这两个函数指针附近是否可以满足伪堆块的利用条件:
1 | #debug(0x107b,"x /200bx "+hex(gdb_libc_base+libc.symbols['__free_hook']-0x50)) |
__free_hook指针周围都是0,但是__malloc_hook周围有值可以错位构造出一个合法的size:0x000000000000007f
因为 0x7f 在计算 fastbin index 时,是属于 index 5 的,即 chunk 大小为 0x70 的。
1 | # |
故我们可以构造一个位于__malloc_hook-0x23处,fastbin索引为5的size为0x70的fake chunk,而其大小又包含了 0x10 的 chunk_header,因此我们选择分配 0x60 的 fastbin,将其加入链表。
1 | #!/usr/bin/env python3 |
即可成功劫持控制流到0xdeadbeef
one_gadget利用约束
因为只能去修改__malloc_hook,而malloc函数的参数一般为数值,所以如果基本无法利用system函数,将数值利用成字符串地址,因为一旦有小数值就会产生非法地址访问。最终还是只能选择one_gadget。
本题的libc有4个one_gadget
1 | $ one_gadget libc_64.so.6 |
调试:
1 | debug(0xc65) |
调试发现约束都不满足
参考wp说由于malloc
或者free
的时候会调用malloc_printerr
函数打印错误信息,因此我们可以通过double free
来触发malloc_printerr
从而触发malloc
函数,进而执行malloc_hook
。这时one_gadget
中的rsp+0x50==0
的条件就满足了。
但是这里我本地没有触发成功,远程倒是直接打通了…不清楚原因
exp
1 | #!/usr/bin/env python3 |
参考:
fastbin-size
32位:
gef fastbin item | chunk size | data interval | fake chunk size |
---|---|---|---|
Fastbins[idx=0, size=0x8] | 0x10 | [0x01,0x0c] , [1,12] | [0x10,0x17] |
Fastbins[idx=1, size=0x10] | 0x18 | [0x0d,0x14] , [13,20] | [0x18,0x1f] |
Fastbins[idx=2, size=0x18] | 0x20 | [0x15,0x1c] , [21,28] | [0x20,0x27] |
Fastbins[idx=3, size=0x20] | 0x28 | [0x1d,0x24] , [29,36] | [0x28,0x2f] |
Fastbins[idx=4, size=0x28] | 0x30 | [0x25,0x2c] , [37,44] | [0x30,0x37] |
Fastbins[idx=5, size=0x30] | 0x38 | [0x2d,0x34] , [45,52] | [0x38,0x3f] |
Fastbins[idx=6, size=0x38] | 0x40 | [0x35,0x3c] , [53,60] | [0x40,0x47] |
64 位:
gef fastbin item | chunk size | data interval | fake chunk size |
---|---|---|---|
Fastbins[idx=0, size=0x10] | 0x20 | [0x01,0x18] , [1,24] | [0x20,0x2f] |
Fastbins[idx=1, size=0x20] | 0x30 | [0x19,0x28] , [25,40] | [0x30,0x3f] |
Fastbins[idx=2, size=0x30] | 0x40 | [0x29,0x38] , [41,56] | [0x40,0x4f] |
Fastbins[idx=3, size=0x40] | 0x50 | [0x39,0x48] , [57,72] | [0x50,0x5f] |
Fastbins[idx=4, size=0x50] | 0x60 | [0x49,0x58] , [73,88] | [0x60,0x6f] |
Fastbins[idx=5, size=0x60] | 0x70 | [0x59,0x68] , [89,104] | [0x70,0x7f] |
Fastbins[idx=6, size=0x70] | 0x80 | [0x69,0x78] , [105,120] | [0x80,0x8f] |