前言
这道题是glibc2.23环境的堆题,包含多种知识与技巧。例如:沙箱函数、overlapping、利用_IO_2_1_stdout_泄漏libc、unsorted bin attack、fastbin attack、setcontext、mprotect、srop等等。
分析
检查保护机制,保护全开
1 | ➜ unctf_orwheap checksec pwn |
ida静态分析
可以发现在add函数处存在off by one。但是限制了size大小(size > 0x67 && size <= 0x3F0),这样我们依然可以进行overlapping。
1 | add(0x68,'0'*0x60) |
此时bin的内容如下
1 | pwndbg> bins |
查看0x55e33cd130f0的chunk
1 | pwndbg> heap 0x55e33cd130f0 |
此时如果有show的功能的话,就可以直接add(0x68,’2’*0x60)从而泄露<main_arena+88>的地址再计算得到libc基址。但这里没有libc基址,于是只好利用_IO_2_1_stdout_
泄漏libc。具体操作如下:
1、修改fd的低字节为’\xdd\x25’,让它指向_IO_2_1_stdout_
。这里的2
需要爆破,概率为1/16
2、利用fastbin attack可以修改_IO_2_1_stdout_
的flag字段从而泄露libc
1 | pwndbg> p stdout |
利用脚本如下
1 | delete(0) |
有了libc基址,我们本可以再次利用fastbin attack修改掉malloc_hook为one_gadget直接get shell。但是,ida分析的时候发现存在沙箱函数prctl
。可以使用secconp-tools查看
1 | ➜ unctf_orwheap seccomp-tools dump ./pwn |
禁掉execve函数,因此无法直接get shell。这时可以通过orw得到flag。但是没有可用的栈空间,于是我们可以利用setcontext函数调用srop来进行栈迁移,最后call mprotect -> shellcode。
这时候可以利用堆空间来部署SigreturnFrame()
,然后将利用fastbin attack将__free_hook
的地址改成setcontext
。在改写__free_hook之前我们需要利用unsorted bin attack来伪造chunk size。(不得不说,unsorted bin attack确实是打辅助的好手)
1 | #unsorted bin attack |
攻击效果bck->fd = unsorted_chunks(av)
1 | pwndbg> x/10gx 0x7f9fe23c37a8-0x20 |
利用fastbin attack将__free_hook
的地址改成setcontext
,并布置好SigreturnFrame()
1 | #fastbin attack |
这里要说一下setcontext
函数;
1 | int setcontext(const ucontext_t *ucp); |
这个函数的作用主要是用户上下文的获取和设置,可以利用这个函数直接控制大部分寄存器和执行流:
1 | pwndbg> x/80i 0x7ffff7a7bb50 |
这里需要说明的是:
- 一般是从
setcontext+53
开始用的,不然程序容易崩溃,主要是为了避开fldenv [rcx]
这个指令。 - 64位中第一个参数刚好在rdi中,因此这里的rdi即frame的地址。
mov rcx,QWORD PTR [rdi+0xa8]; push rcx
即mov rip,QWORD PTR [rdi+0xa8]
利用push是保证指向的内存可访问,否则就会crash。
这是我们已经把栈迁移到(free_hook) & 0xfffffffffffff000
,并可以在改地址处写入0x2000字节的数据,下面构造好ROP链即可
完整exp如下:
1 | #coding=utf-8 |