服务器端模板注入(SSTI)漏洞详解
字数 1541 2025-11-07 22:15:37

服务器端模板注入(SSTI)漏洞详解

1. 漏洞描述
服务器端模板注入(Server-Side Template Injection, SSTI)是一种发生在Web应用服务器端的安全漏洞。当应用使用模板引擎(如Jinja2、Freemarker、Velocity等)动态生成HTML、邮件等内容时,如果未对用户输入进行严格过滤,攻击者就能将恶意模板代码注入到模板中,并在服务器端执行。这可能导致远程代码执行(RCE)、敏感信息泄露或服务器被完全控制。

2. 漏洞原理

  • 模板引擎的作用:模板引擎将静态模板文件与动态数据结合,生成最终的HTML页面。例如,模板中可能包含{{ username }},渲染时会被实际用户名替换。
  • 注入点形成:当用户输入(如URL参数、表单数据)被直接拼接到模板中,而不是作为纯数据传递时,攻击者可以插入模板语法(如{{ 7*7 }}),如果服务器计算并返回49,说明存在SSTI。
  • 危害升级:模板引擎通常支持复杂逻辑(如条件判断、循环、对象调用),攻击者可利用此特性执行系统命令或访问敏感对象。

3. 漏洞检测步骤

  • 步骤1:识别模板引擎
    尝试在用户输入点(如URL参数)提交简单模板语法:

    • Jinja2(Python):{{ 7*7 }} → 若返回"49",可能为Jinja2。
    • Twig(PHP):{{ 7*7 }}{{ 7*'7' }} → 观察返回值。
    • Freemarker(Java):${7*7} → 返回"49"则可能为Freemarker。
      注意:不同引擎语法差异较大,需多次尝试。
  • 步骤2:确认注入点
    若输入{{ 7*7 }}后看到数字49,或输入{{ 'abc' }}后看到字符串"abc",说明用户输入被当作模板代码执行。

  • 步骤3:错误信息分析
    提交无效语法(如{{ {{ }}),观察是否返回模板引擎的错误信息(如Jinja2的"TemplateSyntaxError"),进一步确认引擎类型。

4. 漏洞利用详解(以Jinja2为例)

  • 步骤1:访问Python内置类
    Jinja2模板中,所有Python对象可通过类继承关系访问基类。例如:
    {{ ''.__class__ }} → 返回字符串对象的类(<class 'str'>)。
    {{ ''.__class__.__mro__ }} → 查看类的继承链(包括object基类)。

  • 步骤2:获取危险子类
    通过基类object的子类,找到可执行命令的类(如os._wrap_close):
    {{ ''.__class__.__mro__[1].__subclasses__() }} → 列出所有子类,搜索如<class 'subprocess.Popen'>的索引号。

  • 步骤3:执行系统命令
    假设子类列表第100项为Popen
    {{ ''.__class__.__mro__[1].__subclasses__()[100]('whoami', stdout=-1).communicate() }} → 执行whoami命令并返回结果。
    注:实际索引需根据目标环境调整。

5. 防御措施

  • 输入过滤:对用户输入进行严格白名单验证,禁止输入包含模板语法字符(如{{ }},${})。
  • 沙箱环境:限制模板引擎的访问能力,禁用危险函数或类(如Jinja2的os模块)。
  • 逻辑分离:确保用户输入始终作为数据传递(如模板变量{{ data }}),而非代码(如{{ user_input }})。
  • 静态模板:避免动态拼接模板内容,优先使用预定义模板结构。
服务器端模板注入(SSTI)漏洞详解 1. 漏洞描述 服务器端模板注入(Server-Side Template Injection, SSTI)是一种发生在Web应用服务器端的安全漏洞。当应用使用模板引擎(如Jinja2、Freemarker、Velocity等)动态生成HTML、邮件等内容时,如果未对用户输入进行严格过滤,攻击者就能将恶意模板代码注入到模板中,并在服务器端执行。这可能导致远程代码执行(RCE)、敏感信息泄露或服务器被完全控制。 2. 漏洞原理 模板引擎的作用 :模板引擎将静态模板文件与动态数据结合,生成最终的HTML页面。例如,模板中可能包含 {{ username }} ,渲染时会被实际用户名替换。 注入点形成 :当用户输入(如URL参数、表单数据)被直接拼接到模板中,而不是作为纯数据传递时,攻击者可以插入模板语法(如 {{ 7*7 }} ),如果服务器计算并返回49,说明存在SSTI。 危害升级 :模板引擎通常支持复杂逻辑(如条件判断、循环、对象调用),攻击者可利用此特性执行系统命令或访问敏感对象。 3. 漏洞检测步骤 步骤1:识别模板引擎 尝试在用户输入点(如URL参数)提交简单模板语法: Jinja2(Python): {{ 7*7 }} → 若返回"49",可能为Jinja2。 Twig(PHP): {{ 7*7 }} 或 {{ 7*'7' }} → 观察返回值。 Freemarker(Java): ${7*7} → 返回"49"则可能为Freemarker。 注意:不同引擎语法差异较大,需多次尝试。 步骤2:确认注入点 若输入 {{ 7*7 }} 后看到数字49,或输入 {{ 'abc' }} 后看到字符串"abc",说明用户输入被当作模板代码执行。 步骤3:错误信息分析 提交无效语法(如 {{ {{ }} ),观察是否返回模板引擎的错误信息(如Jinja2的"TemplateSyntaxError"),进一步确认引擎类型。 4. 漏洞利用详解(以Jinja2为例) 步骤1:访问Python内置类 Jinja2模板中,所有Python对象可通过类继承关系访问基类。例如: {{ ''.__class__ }} → 返回字符串对象的类( <class 'str'> )。 {{ ''.__class__.__mro__ }} → 查看类的继承链(包括 object 基类)。 步骤2:获取危险子类 通过基类 object 的子类,找到可执行命令的类(如 os._wrap_close ): {{ ''.__class__.__mro__[1].__subclasses__() }} → 列出所有子类,搜索如 <class 'subprocess.Popen'> 的索引号。 步骤3:执行系统命令 假设子类列表第100项为 Popen : {{ ''.__class__.__mro__[1].__subclasses__()[100]('whoami', stdout=-1).communicate() }} → 执行 whoami 命令并返回结果。 注:实际索引需根据目标环境调整。 5. 防御措施 输入过滤 :对用户输入进行严格白名单验证,禁止输入包含模板语法字符(如 {{ }} , ${} )。 沙箱环境 :限制模板引擎的访问能力,禁用危险函数或类(如Jinja2的 os 模块)。 逻辑分离 :确保用户输入始终作为数据传递(如模板变量 {{ data }} ),而非代码(如 {{ user_input }} )。 静态模板 :避免动态拼接模板内容,优先使用预定义模板结构。