1. 什么是PWN与cgpwn
PWN这个词在安全圈里已经流行了十几年,最早来源于打字错误(原意是"own"),后来演变成了二进制漏洞利用的代名词。简单来说,PWN就是通过分析程序漏洞,精心构造输入数据,最终获得程序控制权的过程。这就像开锁匠研究锁具结构,找到薄弱点后制作专用工具打开它。
cgpwn是CTF(Capture The Flag)比赛中常见的一类PWN题目,全称是"classic game pwn"。这类题目通常会模拟经典游戏场景(比如贪吃蛇、俄罗斯方块),但背后隐藏着精心设计的漏洞。与常规PWN题不同,cgpwn往往需要选手先理解游戏逻辑,再结合逆向分析找出漏洞点。我2016年第一次遇到cgpwn题时,花了3小时才明白原来游戏计分系统存在整数溢出漏洞。
2. 搭建PWN实验环境
2.1 基础工具链配置
工欲善其事必先利其器,推荐使用Ubuntu 20.04 LTS作为基础系统。安装核心工具:
sudo apt install gcc gdb python3 pip3 git pip install pwntools ropper keystone-engine特别提醒:gdb一定要搭配peda插件使用(git clone https://github.com/longld/peda.git ~/peda)。我在2018年打一场线下赛时,因为没装peda导致堆分析慢人一步,错失奖金。配置好后,在~/.gdbinit添加:
source ~/peda/peda.py set follow-fork-mode child2.2 32位与64位环境差异处理
64位系统运行32位程序需要安装兼容库:
sudo dpkg --add-architecture i386 sudo apt update sudo apt install libc6:i386 libstdc++6:i386关键区别在于函数调用约定:
- 32位:参数全部通过栈传递
- 64位:前六个参数依次用RDI、RSI、RDX、RCX、R8、R9传递
用实际代码演示差异:
// 32位调用示例 void func(int a, int b) {...} // 汇编:push b; push a; call func // 64位调用示例 void func(long a, long b) {...} // 汇编:mov rdi, a; mov rsi, b; call func3. cgpwn题目实战分析
3.1 典型题目结构解剖
以2019年XCTF决赛题"snake_pwn"为例:
- 游戏界面是贪吃蛇,吃特定食物会触发计分函数
- IDA分析发现计分函数存在栈缓冲区溢出
- 逆向发现游戏内置"作弊码"验证函数地址固定
- 通过溢出覆盖返回地址跳转到作弊函数
关键payload构造:
from pwn import * payload = b'A'*72 + p64(0x4008A5) # 覆盖到返回地址3.2 常见漏洞模式
cgpwn题目有几种经典漏洞组合:
- 游戏存档/读档功能 - 往往存在堆溢出或格式化字符串
- 高分榜功能 - 常见数组越界写入
- 道具系统 - 通常伴随UAF(Use After Free)
- 游戏设置项 - 容易出现整数溢出
我总结的检查清单:
- [ ] 所有用户输入是否有长度校验
- [ ] 分数计算是否可能溢出
- [ ] 内存分配/释放逻辑是否匹配
- [ ] 函数指针是否可被覆盖
4. 从入门到精通的训练路径
4.1 新手阶段(0-3个月)
建议刷题顺序:
- pwnable.kr的fd、collision、bof
- CTFwiki的ret2text/ret2shellcode
- XCTF高校战题的32位基础pwn
每天保持2小时逆向练习,重点培养:
- 基本汇编阅读能力(mov/call/ret)
- 栈帧结构理解
- GDB基础使用(break/x/i)
4.2 进阶阶段(3-6个月)
重点突破:
- ROP链构造(ret2libc)
- 堆利用基础(fastbin attack)
- 64位调用约定实战
推荐资源:
- how2heap全部示例
- 看雪论坛的漏洞分析文章
- 复现CVE-2017-7269这类真实漏洞
4.3 高手阶段(6个月+)
需要掌握的硬核技能:
- 无libc情况下的内存泄露
- 多线程环境下的竞争条件利用
- 自定义堆分配器漏洞利用
- 内核pwn基础
我个人的突破点是2018年花了整整两周研究House of Orange技术,期间写了17个版本的exp才最终成功。建议准备专门的实验笔记记录每个失败的尝试。
5. 调试技巧与避坑指南
5.1 必知的GDB技巧
- 检查内存映射:
info proc mappings- 快速定位崩溃点:
pattern create 200 pattern search $rsp- 可视化堆块状态:
heap bins heap chunks5.2 常见问题解决
- 遇到"no such file or directory"错误:
- 检查程序是否动态链接了特殊库
- 使用ldd查看依赖
- 用patchelf修改解释器路径
- 本地能打远程不通:
- 检查libc版本差异(使用libc-database)
- 网络字节序问题(特别是结构体打包)
- 环境变量影响(unset LINES; unset COLUMNS)
- 栈地址随机化处理:
- 先泄露地址再计算偏移
- 使用ROP降低依赖
- 暴力破解部分地址(概率约1/16)
6. 资源推荐与社区参与
6.1 持续学习资源
- 在线实验平台:
- pwnable.tw(台湾高质量题目)
- pwnable.xyz(国际新锐平台)
- 网络安全实验室(国内合规平台)
- 必读书籍:
- 《漏洞战争》豆瓣8.7分
- 《CTF竞赛权威指南》系列
- 《二进制分析实战》
- 视频课程:
- 看雪学院PWN专题
- 极客时间安全课程
- Offensive Security官方培训
6.2 社区参与建议
- 从WriteUp开始:
- GitHub搜索"ctf writeup"
- 复现后补充自己的注释
- 尝试改进exp效率
- 参加线下活动:
- 高校CTF联赛(如XCTF)
- 安全沙龙分享
- 企业举办的hackathon
- 建立知识体系:
- 用Markdown记录每个知识点
- 制作漏洞利用流程图
- 维护自己的工具脚本库
最后分享一个真实案例:去年某次比赛中,我通过分析游戏存档压缩算法,发现其使用自定义LZ77实现存在堆溢出,最终用精心构造的贪吃蛇走位触发了漏洞。这提醒我们,cgpwn的精髓往往在于把游戏机制转化为漏洞触发条件。