PicoCTF 2025 - PWN & RE 方向全解
字数 2384 2025-08-29 08:30:06
PicoCTF 2025 PWN & RE方向全解教学文档
PIE TIME - 75 pts
题目分析
- 题目给出源码,允许用户输入一个地址,然后执行该地址的代码
- 程序存在PIE保护,但泄露了main函数地址
- 可以通过泄露的main地址计算出PIE基址,进而计算出win函数地址
解题思路
- 接收程序泄露的main函数地址
- 计算PIE基址:
pie_base = leaked_main_addr - main_offset - 计算win函数地址:
win_addr = pie_base + win_offset - 输入计算出的win函数地址
关键点
- PIE保护下函数地址是随机的,但相对偏移固定
- 通过泄露的地址可以计算出基址
hash-only-1 - 100 pts
题目分析
- 无源码,提供SSH访问
- 反编译发现调用system执行
md5sum /root/flag.txt - 程序有SUID权限
解题思路
- 利用环境变量PATH修改命令查找优先级
- 创建恶意md5sum脚本:
#!/bin/sh cat /root/flag.txt - 添加当前目录到PATH最前面:
export PATH=/tmp:$PATH - 将恶意脚本放在/tmp下并赋予执行权限
关键点
- SUID程序继承用户环境变量
- PATH环境变量控制命令查找顺序
hash-only-2 - 200 pts
题目分析
- flaghasher位于/usr/local/bin
- 系统路径优先级:sbin > bin > usr/local/bin
- /sbin和/bin目录下没有md5sum
解题思路
- 检查系统路径:
which -a md5sum - 在可写目录(如/tmp)创建符号链接:
ln -s /bin/cat /tmp/md5sum - 修改PATH:
export PATH=/tmp:$PATH
关键点
- 利用系统路径优先级
- 符号链接欺骗程序执行其他命令
PIE TIME 2 - 200 pts
题目分析
- 需要通过格式化字符串漏洞泄露地址
- 泄露PIE基址后计算win函数地址
解题步骤
- 利用格式化字符串泄露栈上PIE地址
- 计算PIE基址
- 计算win函数地址
- 输入win函数地址
关键点
- 格式化字符串漏洞读取栈数据
- 计算相对偏移
Echo Valley - 300 pts
题目分析
- 提供打印flag的函数
- 存在无限次触发的格式化字符串漏洞
- 保护全开(ASLR, NX, Stack Canary等)
解题思路
- 利用格式化字符串泄露PIE地址
- 修改返回地址指向print_flag函数
详细步骤
- 发送格式化字符串泄露栈数据:
payload = b"%p."*20 - 从泄露数据中识别PIE地址
- 计算print_flag函数地址
- 构造ROP链覆盖返回地址
关键点
- 格式化字符串漏洞利用
- 无直接溢出情况下的控制流劫持
handoff - 400 pts
题目分析
- 选项2输入idx时使用int接收
- 检查
idx > v1[0]时存在整数溢出漏洞 - 输入负数可覆盖fgets返回地址
漏洞利用
- 输入负数idx(如-1)绕过检查
- 覆盖fgets返回地址:
- 前40字节填充NOP和shellcode
- 返回地址设置为jmp rax gadget地址
关键点
- 整数溢出导致缓冲区越界
- 利用现有gadget(jmp rax)执行shellcode
Flag Hunters - 75 pts
题目分析
- Python代码分析题
- 程序逐行读取文本,遇到REFRAIN跳转到指定行
- 默认refrain=8
解题思路
- 在CROWD输入行添加
;RETURN 0 - 程序split(';')时会先遇到RETURN
- 从第0行开始输出而非默认的第8行
关键点
- 利用字符串分割改变程序流程
- 注入RETURN指令控制输出顺序
Binary Instrumentation 1 - 200 pts
题目分析
- 程序调用Sleep API暂停执行
- Hint建议使用Frida工具
解题步骤
- 编写Frida脚本拦截Sleep调用:
Interceptor.attach(Module.findExportByName("kernel32.dll", "Sleep"), { onEnter: function(args) { args[0] = ptr(0); } }); - 运行程序并注入脚本
关键点
- Frida动态二进制插桩
- API函数拦截和参数修改
Tap into Hash - 200 pts
题目分析
- 提供源码和加密文件
- 加密流程:
- 将flag拼接到区块链字符串中间
- 对已知key进行SHA256哈希
- 取前16字节作为密钥进行异或加密
解密步骤
- 读取加密文件内容
- 使用相同key生成SHA256哈希
- 取前16字节作为密钥
- 异或解密数据
关键代码
def decrypt(encrypted, key):
sha = hashlib.sha256(key.encode()).digest()
xor_key = sha[:16]
return bytes([a ^ b for a, b in zip(encrypted, xor_key)])
ChronoHack - 200 pts
题目分析
- 基于时间种子的随机数问题
- 有50次猜测机会
- 需要预测服务器生成的随机数
解题思路
- 利用低延迟连接减少时间差
- 同步本地时间与服务器时间
- 生成相同种子预测随机数
关键点
- 伪随机数生成的可预测性
- 时间同步的重要性
Quantum Scrambler - 200 pts
题目分析
- "量子纠缠"编码的flag
- 每个列表的第一个和最后一个元素拼接即为flag内容
解题步骤
- 分析编码脚本逻辑
- 提取每个列表的首尾元素
- 拼接得到原始flag
关键代码
flag = ""
for lst in encoded_data:
flag += lst[0] + lst[-1]
Binary Instrumentation 2 - 300 pts
题目分析
- 程序应创建文件并写入flag
- 使用Windows API函数
解题思路
- 使用Frida hook文件操作API:
Interceptor.attach(Module.findExportByName("kernel32.dll", "WriteFile"), { onEnter: function(args) { console.log("Writing:", Memory.readUtf16String(args[1])); } }); - 拦截WriteFile调用获取flag
关键点
- Windows API函数拦截
- 内存数据读取
perplexed - 400 pts
题目分析
- 输入要求27字符(216位)
- 校验流程:
- 前23字节(184位)与v3循环右移1位比较
- 全部匹配且长度正确则成功
解题方法
- 使用angr符号执行:
import angr proj = angr.Project("./perplexed") state = proj.factory.entry_state() simgr = proj.factory.simulation_manager(state) simgr.explore(find=lambda s: b"Success" in s.posix.dumps(1)) - 自动求解满足条件的输入
关键点
- 符号执行自动化逆向
- 复杂校验条件的自动化求解
总结
本系列题目涵盖了多种PWN和RE技术:
- 地址泄露与PIE绕过
- 环境变量利用
- 格式化字符串漏洞
- 整数溢出漏洞
- 二进制插桩技术
- 加密算法逆向
- 符号执行自动化
每种技术都有其独特的应用场景和利用方法,掌握这些技术对于CTF竞赛和二进制安全研究至关重要。