Python中从服务端模板注入到沙盒逃逸的源码探索 (一)
字数 1245 2025-08-27 12:33:37
Python服务端模板注入(SSTI)与沙盒逃逸技术深度解析
一、服务端模板注入(SSTI)基础概念
服务端模板注入(SSTI)是一种通过在服务端的模板文件或模板字符串中注入恶意代码导致代码执行的漏洞攻击方式。不同模板引擎根据其解析方式存在不同的利用方法。
关键特征:
- 模板引擎通常具有沙盒和命名空间机制
- 代码解析执行发生在有限的沙盒环境中
- 沙盒逃逸是SSTI攻击的关键环节
二、Python主流模板引擎分析
1. Tornado模板引擎
核心渲染函数:
render(): 调用render_string生成内容后输出给客户端render_string(): 加载模板并更新命名空间后生成HTML
关键代码路径:
# tornado/web.py
class RequestHandler(object):
def render(self, template_name, **kwargs):
html = self.render_string(template_name, **kwargs)
return self.finish(html)
def render_string(self, template_name, **kwargs):
t = loader.load(template_name)
namespace = self.get_template_namespace()
namespace.update(kwargs)
return t.generate(**namespace)
命名空间关键对象:
handler: 可访问整个Tornado应用request: 当前请求对象current_user: 当前用户locale: 本地化对象static_url: 静态URL生成器xsrf_form_html: XSRF保护表单
沙盒逃逸路径:
通过handler对象可以访问整个应用:
{{handler.application.settings}} # 获取settings配置
{{handler.settings}} # 获取settings配置(简写)
实际案例:护网杯2018 WEB (1) easy_tornado
2. Flask/Jinja2模板引擎
核心渲染函数:
render_template(): 通过模板文件加载渲染render_template_string(): 直接渲染模板字符串
关键代码路径:
# flask/templating.py
def render_template_string(source, **context):
ctx = _app_ctx_stack.top
ctx.app.update_template_context(context)
return _render(ctx.app.jinja_env.from_string(source), context, ctx.app)
全局变量注入:
Flask向Jinja2环境注入了6个全局对象:
url_for
get_flashed_messages
config
request
session
g
实际案例:TokyoWesterns CTF 4th 2018 shrine
三、Jinja2模板引擎深度分析
1. 核心编译流程
# jinja2/environment.py
class Environment(object):
def _compile(self, source, filename):
return compile(source, filename, 'exec')
def compile(self, source, name=None, filename=None, raw=False, defer_init=False):
source = self._generate(source, name, filename, defer_init=defer_init)
return self._compile(source, filename)
2. 特殊语法特性
-
支持的控制语句关键字:
_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print', 'macro', 'include', 'from', 'import', 'set', 'with', 'autoescape']) -
当
{{}}被禁用时,可使用{% print somedata %}输出内容
实际案例:网鼎杯CTF 2018第三场Web mmmmy
3. 默认命名空间
DEFAULT_NAMESPACE = {
'range': range_type,
'dict': dict,
'lipsum': generate_lorem_ipsum,
'cycler': Cycler,
'joiner': Joiner,
'namespace': Namespace
}
4. 未定义对象处理
Jinja2对不存在的对象使用Undefined类:
<class 'jinja2.runtime.Undefined'>
可通过{{ vvv.__class__.__init__.__globals__ }}访问全局变量
5. 内置过滤器
Jinja2提供了丰富的内置过滤器(部分):
FILTERS = {
'abs': abs,
'attr': do_attr,
'capitalize': do_capitalize,
'dictsort': do_dictsort,
'escape': escape,
'float': do_float,
'int': do_int,
'join': do_join,
'length': len,
'list': do_list,
'lower': do_lower,
'upper': do_upper,
'urlencode': do_urlencode,
'tojson': do_tojson,
}
使用方式:{{ somedata | filter }}
四、高级利用技巧
1. 通过过滤器绕过限制
{{url_for.__globals__.current_app.config|safe}}
2. 特殊场景下的输出方式
当常规输出方式被限制时:
{% print config %} # 使用print语句输出
{% if config %}1{% endif %} # 使用条件判断进行盲注
3. 对象链式访问
通过对象属性链访问敏感数据:
{{ ''.__class__.__mro__[1].__subclasses__() }}
五、防御建议
- 避免直接渲染用户输入的模板内容
- 严格限制模板中可访问的对象和方法
- 对模板引擎进行安全配置,禁用危险功能
- 使用沙盒环境执行模板代码
- 对用户输入进行严格的过滤和验证
六、总结
本文深入分析了Python中两大主流模板引擎(Tornado和Jinja2)的SSTI漏洞原理和利用方式,揭示了从模板注入到沙盒逃逸的完整攻击链。理解这些底层机制对于安全开发和漏洞挖掘都具有重要意义。