在了解幽灵漏洞的时候看到了这个词, 查了一下原来就是恶意控制程序执行流.
正好有个朋友也在研究这些东西,如果我不知道岂不是连人家的博客都看不懂
本文仅用作个人大概了解,可能有误.参考资料主要来源于wiki,gpt和下文中的链接.
词典
- gadgets: 以ret结尾的指令序列.
- payload: 攻击者在溢出缓冲区时注入的恶意代码或数据.
分类
- ret2text
- ret2shellcode
- ret2syscall
- ret2libc
- ret2dl_resolve
- ret2VDSO
- SROP
- BROP
如何绕过canary
^参考
ret是在canary之后的. 如果绕不过canary, 修改返回地址就免谈了.
- 泄漏canary:
正常是读不到canary的.
并且每次运行程序 canary都不一样.
有时canary低字节部分为0, 我们可以稍微溢出一点缓冲区, 正好覆盖掉canary低字节为0的部分而不覆盖其全部, 这样就能读取到canary了. - 爆破canary
令覆盖的值随机(或者遍历),一步步覆盖canary,如果正确就进一步覆盖,不正确就重开. (不过因为重开的是fork程序, 不管fork多少次 canary都是和原程序一致的. 所以不用担心canary变动的问题)
(原文给出的示例是仅限于fork时,canary不变才能得到的,不知道有没有其他适用条件) - ssp leak(Stack Smashing Protect Leak)
glibc2.27以上已经修复了这个方法,这里简单了解一下吧.
“通过输入足够长的字符串覆盖掉argv[0],这样就能让canary保护输出我们想要地址上的值”
更详细一点的过程是,当覆盖了canary引发stack_chk_fail时, 会执行fortify_fail(), 这个函数会打印启动参数argv[0].
而argv[0]是在canary后面的. 如果我们覆盖的足够长, 覆盖掉argv[0], 也就可以打印我们修改过的argv[0]的值.
再深入一点涉及到smashes和exp什么的??这里不再细说了. ^原文链接 - 劫持__stack_chk_fail
通过修改got表实现. 这里涉及到格式化字符串漏洞.关于这个漏洞,我觉得暂时没必要再往深看了,不然好像没有尽头的样子
大概过程是, 修改__stack_chk_fail在got表中指向的函数, 从而在触发的时候 执行其他函数(比如一个空函数.)
好了,现在终于可以看看怎么ret2xxx了!
ret2text:
也就是返回到程序原有的代码段.
不需要关nx, 毕竟这是程序本身的代码段.(感觉没什么用啊 哪个攻击者能直接获取二进制程序并且知道ret到哪里可以有效攻击啊)后来想想 好像ctf比赛是不是会直接给二进制程序?
^示例
ret2shellcode
返回到自己覆盖的缓冲区上的代码.
需要关NX保护.
覆盖返回地址到栈上, 具体是返回到我们写入的缓冲区中的shellcode.(仍然感觉没什么用, 除了打比赛.)
ret2syscall
返回到系统调用. 和ret2text差不多,只不过要指定寄存器.
amd64调用系统调用需要这几个寄存器:eax, ebx, ecx, edx和syscall指令. riscv是用的x10, x11, x12和x17, 这里先看看amd64的吧.
ret2libc
这里复习一下got表和plt的知识.
动态链接库中的函数, 在我们的程序的函数中调用时, 访问的是全局符号. got表记录了这些符号到 动态链接库中实际函数地址 的映射.
当第一次调用动态链接库中的函数时, 会先访问plt. plt负责调用动态链接器, 找到相应的真实的链接库中的函数地址, 然后放到got表中.
可以将plt看成一种懒加载的实现.
返回到libc或者其他的外部链接库(不过一般是返回到libc).
ret2csu
就是调用任意函数.
相应的, ret2csu的额外工作是拟造目标函数需要的参数.
需要的参数很多时怎么办?
当传入多个参数时, 程序将先前的参数压到栈中, 然后再读剩下的参数.
等需要某个参数时, 再将栈中的参数放到寄存器中.
程序中,一般有一段万能的控制参数的gatget, 可以控制用于传参的寄存器(比如__libc_csu__init), 这样就可以进行传参调用了.
more
还有其他的rop方法,不细看了,放个链接在这里吧.
https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/medium-rop/
总结
栈溢出真的能引发很多问题.
但貌似也是很低级的问题.
关于逆向方面的知识先看到这里. 下一次学习可能会了解^这个东西.
但是现在我该ret2幽灵漏洞了.