一道题,盯一天,描述的就是我当时的状态。比赛结束后看了这篇 writeup 才做出来。这道题对我来说比较新颖的点有四个:一是高版本 libc (主要是有 tcache)下的堆利用,二是 rop 可以在堆上,三是泄漏地址的一些姿势,四是 libc 中的一些奇妙的 gadget。
基本情况
保护
开满了,要控制程序执行流程大概只能在各种 hook 上做手脚了。
只允许 orw。
程序分析
看上去是一道朴实无华的菜单题。
buy
分配函数。其使用的各种输入函数都不会导致溢出。值得注意的是 off_4010 变量,它的值为0x1000,限制了分配 chunk 的用户空间的总大小。
load
设置标志位。并将堆块用单链表连在一起,使用头插法。
shoot
从链表头开始依次输出堆块中的值,并将其 free 。存在 UAF 漏洞。
结构
结合上面的三个函数我们可以得到 “bullet” 的结构图:
chunk 域指向分配的 chunk,status 域的值为0、1、2,分别对应释放/未分配、已分配和 Loaded (经过 load 函数处理后)状态,next 域指向下一个 “bullet”。
小结
堆块的分配和放置与常规做法相同,有输出功能,但删除的方法比较复杂,构造时需注意。
漏洞利用
我看了前面提到的 writeup 才搞明白这题的漏洞利用方法,但它提到的 free unsortedbin 中的 fake chunk 的方法我一直搞不明白,所以我将其换成了 fastbin attack,流程如下:
利用 fastbin attack 修改__free_hook
由于 libc-2.31 中对 tcache 的 double free 检测比较完善(只能通过修改 key 域绕过),我选择在 fastbin 中 进行 double free。
利用残余指针,泄漏 libc 基地址和堆中的某个地址、
利用 unsortedbin 中与链表头相邻的 chunk 泄漏 libc 地址,利用 tcache chunk 的 fd 泄漏堆地址。
利用各种奇妙的 libc 中的 gadget 在堆上进行 rop
在堆上 rop 的一个重要问题是:如何正确设置 rsp ?下面介绍两个比较重要的 gadget(exp 中有它们的汇编代码),这里描述一下它们的神奇功能:
set_call_rdx
rdx <- [rdi + 8], …, call [rdx + 0x20]
与 free 函数配合时,要在堆上存放 rdx 的值和返回地址。
rsp_rdx_0xA0
rsp <- [rdx + A0h], …, ret
设置 rsp后, rop 和栈上的没两样了。
一些细节问题记录在 exp 中。构造方法千万种,漏洞点和利用方法才是关键。
exp
1 | from pwn import * |
当时的记录
特点:
- libc-2.31的题
- 保护全开
- 分配的chunk总大小有限制:0x1000
- 利用prctl开了seccomp和禁止提升权限(具体是怎样的不清楚),只能orw
- 把符号表都去掉了,但又跟常规的去符号表不一样,连libc函数的名字也不能显示
且跳转到libc多了一层(好像是plt.sec?)。最后我调试后才搞清哪个got表对应哪个函数 - 用了比较复杂的结构管理分配的空间(其实就是链表),花了我很长时间搞清
漏洞:只发现了UAF(只能double free)
尝试:
早上大概了解了程序的流程和漏洞
下午编译gcc(编译libc-2.31需要,时间真长。。。),libc-2.31,分析漏洞利用手段(一筹莫展)
晚上最后看了看,搞了个fastbin double free(多了一步填满tcache bin),但不知道怎么泄漏地址。。。
最后因为限制太多:free不方便、chunk总大小限制、高版本libc保护多,再加上不了解除rop以外的orw的方法(就算泄露了libc基地址也只能改改各种hook)
放弃。。。
有用的资料:
- libc-2.31攻击方法(有源代码):https://github.com/StarCross-Tech/heap_exploit_2.31
- 上面资料更详细的解说:https://zhuanlan.zhihu.com/p/136983333
总结
- 思路还是太局限了(刷的题太少吧)。
- 调试->发现问题->再调试->解决问题也许是最好的学习方法。