打得最刺激的一次,拿了个二血,还剩十分钟的时候做出来了 msgparser。做出来的题目都是比较常规的,一个是菜单题中利用 glibc 中的 CVE,一个是(虚假的)http server 中的人为漏洞。赛后研究一下 mimic-game,似乎是一个多线程的条件竞争题。还有堆风水总是要弄半天。。。
打完后累得不行,还是得多锻炼身体。
GHOST
猜测
用 ida 反编译,发现是一个常见的菜单题。
粗略地看过后没有发现常见的漏洞。而程序中使用了在堆题中不常见的 gethostbyname_r,再加上题目给了一个不常见的低 libc 版本,据此推测题目考察的是 glibc 中的 CVE。
Poc
经过简单的搜索后发现名为 GHOST 的 glibc 中的漏洞,与题目名称对应。
然后找到了一个 PoC:https://gist.github.com/dweinstein/66e6a088191ac0e8105c,编译、使用 patchelf 改变 ld 和 libc、运行,发现漏洞存在。
利用
利用这个漏洞,我们可以在堆上溢出 8 个字节,然而经过测试,溢出的字节只能为数字的 ascii 码值,不然程序会崩溃。
于是我选择将 chunk size 修改为 0x3031,free 从而构造 overlap,之后就是修改 fastbin 的 fd,申请 chunk 到 _malloc_hook 上方,改 _malloc_hook 为 one_gadget,最终 get shell。
堆风水搞了半天,直接猪脑过载。(主要原因是每次分配和释放都会操作三个 chunk,还有忘记了 malloc large chunk 的时候会触发 malloc_consolidate)
exp
1 | from pwn import * |
msgparser
分析
程序实现了一个 http 服务器。服务器解析输入后,会将 content 拷贝到一个 output 数组中:
而 output 数组位于栈上:
漏洞点
程序没有对 Content-Length 字段的值做检查,所以可以泄漏栈上的任意长的信息,以及进行任意长的栈溢出。
利用
因为输入拷贝到 msg 时有 ‘\x00’ 截断,所以我们不能一次性覆盖 canary 和返回地址(因为 canary 的最低字节总为 ‘\x00’)。
而 parse 成功时,程序会再次接收输入。据此我们可以先覆盖返回地址,再覆盖 canary。最后输入错误的信息,parse 失败,main 函数返回时就会执行流就会跳转到 one_gadget。
对 parse_msg 函数的逆向会花费一些时间。
exp
1 | from pwn import * |