xo.01 ret2syscall原理
ret2syscall,即通过ROP控制程序执行系统调用,获取 shell。
xo.02 ROP原理:
随着 NX 保护的开启,以往直接向栈或者堆上直接注入代码的方式难以继续发挥效果。攻击者们也提出来相应的方法来绕过保护,目前主要的是 ROP(Return Oriented Programming),其主要思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。
xo.03 gadgets:
所谓 gadgets 就是以 ret 结尾的指令序列。
例如:pop eax ; ret
这段代码的作用就是将栈顶的数据弹出给eax,然后再将栈顶的数据作为返回地址返回。
假如我们通过栈溢出将eip覆盖为pop eax的地址,当程序返回的时候就会执行pop eax,将’aaaa’放到eax中,然后执行ret指令,将’bbbb’放入eip寄存器中,然后会执行地址为’bbbb’处的指令,可以看到,bbbb处的指令为xxxx,当执行完xxxx后,继续执行ret指令,然后会返回到cccc指令处,执行xxxx。system函数的本质也是一条条汇编指令的组合,如果我们能找到多个xxxx,ret结构的指令,将其连接在一起,就可以达到执行system函数的效果。
之所以称之为 ROP,是因为核心在于利用了指令集中的 ret 指令,改变了指令流的执行顺序,我们可以将多个gadget组合到一起,进而可以执行多条汇编指令,从而达到目的。ROP 攻击一般得满足如下条件
程序存在溢出,并且可以控制返回地址。
可以找到满足条件的 gadgets 以及相应 gadgets 的地址。
xo.04 获取gadgets的方式:
指令:
ROPgadget --binary 文件名 --only 'pop|ret' |grep '寄存器名'
ROPgadget --binary 文件名 --only 'int'
ROPgadget --binary 文件名 --string '/bin/sh'
xo.05 步骤
漏洞分析
使用checksec
指令查看程序保护措施
checksec rop
可见该程序为32位,仅开启了NX堆栈不可执行
,故不可以直接向堆栈中注入shellcode获取权限。
使用IDA32位进行调试
反汇编后可看到main函数如下
可以看到主函数中存在gets危险函数,可以利用gets修改main函数的返回值返回到我们所需的gadgets,从而进行想要执行的系统调用,在这里我们可以进行execve("/bin/sh",NULL,NULL)系统调用
通过在IDA中shift + F12查看字符串,我们可以看到存在/bin/sh字符串,地址为0x080BE408
我们可以直接利用这个字符串作为execve的第一个参数进行调用。
由上述原理可知,这里我们需要eax寄存器来存储系统调用号,利用其它寄存器来存储参数,再通过0x80触发中断
此处我们系统调用函数为execve,对应的系统调用号为0xb,故应给eax赋值0xb。
execve函数的三个参数则分别对应着ebx、ecx、edx三个寄存器
ebx——0x080BE408(/bin/sh字符串地址)
ecx——0
edx——0
如何给这些寄存器赋值呢?
汇编语言中有一条指令是pop xxx,可将栈顶数据弹出并存入xxx中,这个xxx可以为寄存器
我们就可以使用gets函数修改栈中数据,然后触发pop指令将这些数据存入栈中
那么如何指定pop指令的操作对象呢?
这时我们需要利用一个小工具:ropgadgets,这个工具可以帮助我们获取需要的gadgets
比如这道题我们需要将0xb存入eax中,我们就可以输入以下指令
这条指令可以帮助我们找到rop
程序中eax
寄存器的pop|ret
指令的地址
可以看到,包含pop
、eax
、ret
指令的所有gadgets
都被列出,这里我们只需要第二个即可
然后我们再找’ebx’的相关gadgets
发现这么多相关的gadgets
均被列出,这里我们可以发现倒数第六行将ebx
、ecx
、edx
都用到了,我们就可以直接利用这一条gadgets
来给这些寄存器赋值
接下来再利用gdb
获取return
偏移量
随机产生200个字母
可以看到在0x62616164
报错,说明这里就是return
在栈中的位置,被这四个字母覆盖
通过cyclic -l
命令查找刚刚随机产生的字符串中这四个连续字母的位置可以确定偏移量为112