前言
通过一道PWN题目学习ROP。
随着 NX 保护 (No-eXecute 不可执行) 的开启,以往直接向栈或者堆上直接注入代码的方式难以继续发挥效果。攻击者们也提出来相应的方法来绕过保护,目前主要的是 ROP (Return Oriented Programming),其主要思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。所谓 gadgets 就是以 ret 结尾的指令序列,通过这些指令序列,我们可以修改某些地址的内容,方便控制程序的执行流程。
ROPgadget工具可以帮助你寻找合适的gadgets,在编写你的ROP exp的时候有很大作用。ROPgadget支持x86, x64, ARM, ARM64, PowerPC, SPARC和MIPS架构下的ELF/PE/Mach-O文件格式。
这里我们需要构造的gadget如下
1 | mov eax,0xb |
0x00
checksec查看开启了哪些保护机制
1 | % checksec ret2syscall |
通过查看架构得之主要的寄存器有eax ebp ebx ecx..
程序执行如下,输出前两句后接受输入,然后退出
1 | [root@VM-16-14-centos rop]# ./ret2syscall |
反编译查看,从gets函数得之存在溢出漏洞

pwndbg查看寄存器位置,发现eax寄存器位置为0xffffd29c,ebp寄存器的位置为0xffffd308,如果我们想要覆盖掉ebp寄存器的位置,则需要填充的字节为0xffffd308-0xffffd29c字节再加上ebp所占的4个字节,因为架构是32位的。即需要填充112字节。

0x01
接着我们通过ROPgadget工具寻找gadget
首先寻找eax的pop|ret地址,选择0x080bb196地址最合适
1 | % ROPgadget --binary ret2syscall --only "pop|ret" | grep eax |
接着寻找ebx的pop|ret地址,有很多,哪一个最适合呢,发现0x0806eb90最合适,因为它也有ecx和edx寄存器。
1 | % ROPgadget --binary ret2syscall --only "pop|ret" | grep ebx |
最后我们寻找int,地址为0x08049421
1 | % ROPgadget --binary ret2syscall --only "int" |
因为ebp寄存器中需要写入/bin/sh,因此我们还需要在程序中找到/bin/sh的地址
在ghidra中全局搜索发现地址为0x80be408

或者通过python编写脚本获取
1 | from pwn import * |
0x02
编写exp
1 | from pwn import * |
0x03
本地执行exp获取shell
1 | [root@VM-16-14-centos rop]# python3 exp.py |