---
title: OWASP TOP 10 Day 3
tags: OWASP TOP 10
slideOptions:
transition: fade
center: true
parallaxBackgroundImage: 'https://hackmd.io/_uploads/SJhktzri2.png'
---
# OWASP TOP 10 DAY 3
# A03: 2021 - A01: 2017 Injection
## OS injection
### Что такое инъекции команд?
Инъекция команд ОС (также известная как shell инъекция) является уязвимостью веб-приложений, которая позволяет злоумышленнику выполнять произвольные команды операционной системы (ОС) на сервере, на котором запущено приложение, и, как правило, дает возможность полностью скомпрометировать приложение и все его данные. Очень часто злоумышленник может использовать уязвимость внедрения команд ОС для компрометации других частей инфраструктуры, используя доверительные отношения для перенаправления атаки на другие системы в организации.
### Как возникают инъекции команд?
Пример уязвимости инъекции команд:
```
$command = 'convert -pointsize 72 label:Hello ' . $_FILES['userfile']['name'];
system($command);
```
Пример эксплуатации для уязвимости выше:
```
------WebKitFormBoundaryePkpFF7tjBAqx29L
Content-Disposition: form-data; name="userfile"; filename="check | ls -la;"
Content-Type: application/x-object
... contents of file goes here ...
```
### Code examples
[VulnPlanet - a03 injection code example](https://github.com/yevh/VulnPlanet/blob/main/web2/owasp/A03-Injection.md)
[VulnPlanet - command injection](https://github.com/yevh/VulnPlanet/blob/main/web2/type/Command_Injection.md)
#### PHP CVE-2019-16663
[Two unpatched RCE flaws in rConfig software expose servers to hack](https://securityaffairs.com/93391/hacking/rconfig-rce-flaws.html)
```php!
<?php
$rootUname = $_GET['rootUname'];
$array = array();
/* check PHP Safe_Mode is off */
if (ini_get('safe_mode')) {
$array['phpSafeMode'] = '<strong><font class="bad">Fail - php safe mode is on - turn it off before you proceed with the installation</strong></font>br/>';
} else {
$array['phpSafeMode'] = '<strong><font class="Good">Pass - php safe mode is off</strong></font><br/>';
}
/* Test root account details */
$rootTestCmd1 = 'sudo -S -u ' . $rootUname . ' chmod 0777 /home 2>&1';
exec($rootTestCmd1, $cmdOutput, $err);
$homeDirPerms = substr(sprintf('%o', fileperms('/home')), -4);
if ($homeDirPerms == '0777') {
$array['rootDetails'] = '<strong><font class="Good">Pass - root account details are good </strong></font><br/>';
} else {
$array['rootDetails'] = '<strong><font class="bad">The root details provided have not passed: ' . $cmdOutput[0] . '</strong></font><br/>';
}
// reset /home dir permissions
$rootTestCmd2 = 'sudo -S -u ' . $rootUname . ' chmod 0755 /home 2>&1';
exec($rootTestCmd2, $cmdOutput, $err);
echo json_encode($array);
```
#### C#
```c#
using Microsoft.AspNetCore.Mvc;
using System;
using System.Diagnostics;
namespace WebFox.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class OsInjection : ControllerBase
{
[HttpGet("{binFile}")]
public string os(string binFile)
{
Process p = new Process();
p.StartInfo.FileName = binFile; // Noncompliant
p.StartInfo.RedirectStandardOutput = true;
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.Dispose();
return output;
}
}
}
```
#### nodeJS
```javascript!
const express = require('express');
const router = express.Router()
const { exec, spawn } = require('child_process');
router.post('/ping', (req,res) => {
exec(`${req.body.url}`, (error) => {
if (error) {
return res.send('error');
}
res.send('pong')
})
})
router.post('/gzip', (req,res) => {
exec(
'gzip ' + req.query.file_path,
function (err, data) {
console.log('err: ', err)
console.log('data: ', data);
res.send('done');
});
})
router.get('/run', (req,res) => {
let cmd = req.params.cmd;
runMe(cmd,res)
});
function runMe(cmd,res){
// return spawn(cmd);
const cmdRunning = spawn(cmd, []);
cmdRunning.on('close', (code) => {
res.send(`child process exited with code ${code}`);
});
}
module.exports = router
# curl -X POST -H "Content-Type: application/json" -d '{"url": "touch /tmp/test"}' http://server/ping
# curl -X POST 'http://server/gzip?file_path=touch%20/tmp/test'
# curl -X GET 'http://server/run?cmd=touch%20/tmp/test'
```
#### Java
```java!
import testcasesupport.*;
import javax.servlet.http.*;
public class CWE78_OS_Command_Injection__getParameter_Servlet_10 extends AbstractTestCaseServlet
{
/* uses badsource and badsink */
public void bad(HttpServletRequest request, HttpServletResponse response) throws Throwable
{
String data;
if (IO.staticTrue)
{
/* POTENTIAL FLAW: Read data from a querystring using getParameter */
data = request.getParameter("name");
}
else
{
/* INCIDENTAL: CWE 561 Dead Code, the code below will never run
* but ensure data is inititialized before the Sink to avoid compiler errors */
data = null;
}
String osCommand;
if(System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)
{
/* running on Windows */
osCommand = "c:\\WINDOWS\\SYSTEM32\\cmd.exe /c dir ";
}
else
{
/* running on non-Windows */
osCommand = "/bin/ls ";
}
/* POTENTIAL FLAW: command injection */
Process process = Runtime.getRuntime().exec(osCommand + data);
process.waitFor();
}
/* goodG2B1() - use goodsource and badsink by changing IO.staticTrue to IO.staticFalse */
private void goodG2B1(HttpServletRequest request, HttpServletResponse response) throws Throwable
{
String data;
if (IO.staticFalse)
{
/* INCIDENTAL: CWE 561 Dead Code, the code below will never run
* but ensure data is inititialized before the Sink to avoid compiler errors */
data = null;
}
else
{
/* FIX: Use a hardcoded string */
data = "foo";
}
String osCommand;
if(System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)
{
/* running on Windows */
osCommand = "c:\\WINDOWS\\SYSTEM32\\cmd.exe /c dir ";
}
else
{
/* running on non-Windows */
osCommand = "/bin/ls ";
}
/* POTENTIAL FLAW: command injection */
Process process = Runtime.getRuntime().exec(osCommand + data);
process.waitFor();
}
/* goodG2B2() - use goodsource and badsink by reversing statements in if */
private void goodG2B2(HttpServletRequest request, HttpServletResponse response) throws Throwable
{
String data;
if (IO.staticTrue)
{
/* FIX: Use a hardcoded string */
data = "foo";
}
else
{
/* INCIDENTAL: CWE 561 Dead Code, the code below will never run
* but ensure data is inititialized before the Sink to avoid compiler errors */
data = null;
}
String osCommand;
if(System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)
{
/* running on Windows */
osCommand = "c:\\WINDOWS\\SYSTEM32\\cmd.exe /c dir ";
}
else
{
/* running on non-Windows */
osCommand = "/bin/ls ";
}
/* POTENTIAL FLAW: command injection */
Process process = Runtime.getRuntime().exec(osCommand + data);
process.waitFor();
}
public void good(HttpServletRequest request, HttpServletResponse response) throws Throwable
{
goodG2B1(request, response);
goodG2B2(request, response);
}
/* Below is the main(). It is only used when building this testcase on
* its own for testing or for building a binary to use in testing binary
* analysis tools. It is not used when compiling all the testcases as one
* application, which is how source code analysis tools are tested.
*/
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException, IllegalAccessException
{
mainFromParent(args);
}
}
```
[Code Security Advent Calendar 2022 | Can you Spot the Vulnerability? | Sonar](https://www.sonarsource.com/knowledge/code-challenges/advent-calendar-2022/) - day 10 - java
[Real attack - PHP Supply Chain Attack on Composer](https://www.sonarsource.com/blog/php-supply-chain-attack-on-composer/)
```java!
@WebServlet(name = "MercurialImporterServlet", urlPatterns = {"/check"})
public class MercurialImporterServlet extends HttpServlet {
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse res) throws IOException {
res.setContentType("text/plain");
var out = res.getOutputStream();
if (req.getParameter("repository") == null
|| req.getParameter("repository").indexOf("$(") != -1
|| req.getParameter("repository").indexOf("`") != -1) {
res.setStatus(405);
return;
}
var cmd = new String[] {
"hg",
"identify",
req.getParameter("repository")
};
var p = Runtime.getRuntime().exec(cmd);
var br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String l;
while ((l = br.readLine()) != null) {
out.write(l.getBytes("ascii"));
}
br.close();
}
}
```
`repository=--config=alias.identify=!id`
### Мета - символы
| Injection Operator | Injection Character | URL-Encoded Character | Executed Command |
|-|-|-|-|
|Semicolon| ;|%3b|Both|
|New Line| \n|%0a|Both|
|Background| &|%26|Both (second output generally shown first)|
|Pipe| \||%7c|Both (only second output is shown)|
|AND| &&|%26%26|Both (only if first succeeds)|
|OR| \|\||%7c%7c|Second (only if first fails)|
|Sub-Shell| `` |%60%60|Both (Linux-only)|
|Sub-Shell| $()|%24%28%29|Both (Linux-only)|
### Как предотвратить инъекции команд?
На сегодняшний день наиболее эффективным способом предотвращения уязвимостей инъекции команд ОС является никогда не вызывать команды ОС из кода прикладного уровня. Практически в каждом случае существуют альтернативные способы реализации требуемой функциональности с использованием более безопасных платформенных API.
1. **Избегать прямого выполнения команд ОС**. Для огромного количества различных функций реализованы библиотеки.
2. Использовать функции языка для **вызова других программ без обращения к оболочке ОС** (proc_open в PHP)
3. **Не передавать пользовательский ввод** в командную оболочку.
4. В крайнем случае **используйте специальные функции для экранирования** (escapeshellargs в PHP)
5. [https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview](https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview) (для .Net)
Если считается неизбежным вызывать команды ОС с пользовательским вводом, то должна быть выполнена серьезная проверка вводимых данных:
1. Соответствие типу данных
2. Корректность формата
3. Регулярные выражения
4. Разрешенный набор символов
Валидация Laravel
```php!
public function createUser(Request $request)
{
$this->validate($request, [
'login' => 'required|max:255', (required - обязятельные данные)
'age' => 'required|integer', (integer целое число )
'email' => 'required|email',
'body' => 'required',
]);
...
}
```
[Security Code Review 101](https://owasp.org/SecureCodingDojo/codereview101/ "title")
### Еще примеры
https://hackerone.com/reports/212696
https://hackerone.com/reports/1360208
https://hackerone.com/reports/389561
https://hackerone.com/reports/303061
https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41293
https://www.wallarm.com/what/command-injection-definition
### Упражнения
- BWAPP OS Command injection
- BWAPP OS Command injection (Blind)
- [https://portswigger.net/web-security/os-command-injection/lab-simple](https://portswigger.net/web-security/os-command-injection/lab-simple)
### Отстуки
https://requestinspector.com/
## Code injection
Контролируемые атакующим данные подставляются в программный код, после чего он исполняется веб-приложением.
### Упражнения
- bWAPP: PHP Code Injection
- root-me PHP Command Injection
## SQL Injection (Инъекции управляющих конструкций SQL)
**SQL** - Structured Query Language Структурированный язык запросов
https://sqliteonline.com/
### Что такое SQL инъекция?
SQL-инъекция - Уязвимость, при которой контролируемые атакующим данные внедряются в формируемый SQL-запрос. В результате внедрения атакующий может нарушить логику работы запроса и/или заставить СУБД выполнить полезные для атакующего действия.
```php
$query = "SELECT username, email, phone FROM users
WHERE username = '" . $_REQUEST['username'] . "'";
$result = mysql_query($conn, $query);
```
### Какой ущерб несут уязвимости к инъекции SQL?
Успешная атака SQL-инъекции может привести к несанкционированному доступу к конфиденциальным данным, таким как пароли, данные кредитной карты или личная информация пользователя. Многие громкие утечки данных в последние годы стали результатом атак SQL-инъекции, что привело к репутационному ущербу и штрафам со стороны регулирующих органов. В некоторых случаях злоумышленник может получить постоянный черный ход в системы организации, что приводит к долгосрочному закреплению злоумышленника в инфраструктуре, которое может оставаться незамеченным длительный период времени.
В частности, уязвимости SQL-инъекций могут приводить к:
- Чтение информации из доступных баз данных
- Получение информации о сервере, текущем пользователе, настройках
- Получение информации из файловой системы
- Возможность модификации информации в базе данных
- Возможность записи в файлы
- Возможность выполнения произвольного кода
### Примеры SQL инъекций
**Сценарий №1** Приложение использует недоверенные данные при создании следующего уязвимого SQL-вызова:
```php!
String query = "SELECT * FROM accounts WHERE custID='" + request.getParameter("id") + "'";
```
**Сценарий №2** Безоговорочное доверие приложений к фреймворкам может привести к появлению уязвимых запросов (например, в языке запросов HQL):
```php!
Query HQLQuery = session.createQuery("FROM accounts WHERE custID='" + request.getParameter("id") + "'");
```
В обоих случаях злоумышленник изменяет в своем браузере значение параметра “id” для отправки `' or '1'='1`.
Например:
`http://example.com/app/accountView?id='+or+'1'='1`
Изменение обоих запросов позволяет получить все записи из таблицы учетных данных. Более серьезные атаки позволяют изменить или удалить.
**Сценарий №3** Использование фреймворков, кажущееся безопасным, может быть обманчивым (Пример использования фреймворка Ruby On Rails и Active Records):
```
User.order("#{params[:sortby]} ASC")
```
Кажущийся безопасным вызов `.order()` на самом деле не экранирует управляющих конструкций получаемых от приложения, что приводит к возможности проведения атак на базу данных, через такой вектор как:
```
(CASE SUBSTR(password, 1, 1) WHEN 's' THEN 0 else 1 END)
```
[SQL Injection ruby on rails](https://www.stackhawk.com/blog/sql-injection-prevention-rails/)
### О техниках атак
Существует широкий спектр уязвимостей, атак и методов SQL инъекции, которые возникают в различных ситуациях. К числу наиболее распространенных примеров SQL-инъекций относятся:
- Stacked queries
- Union-based
- Error-based
- Boolean blind
- Time-base
- Out of band

### Как найти SQL
- Ввод символа одиночной кавычки ' и поиск ошибок или других аномалий.
- Ввод некоторого синтаксиса, специфичного для SQL и поиск систематических различий в результирующих ответах приложения.
- Ввод булевых условий, таких как OR 1=1 и OR 1=2, и поиск различий в ответах приложения.
- Отправка полезных нагрузок (прим. Временная задержка)
##### Union-based
Классический вариант внедрения SQL-кода, когда в уязвимый параметр (или несколько параметров) передаётся выражение (фактически новый запрос), начинающееся с "UNION SELECT" или "UNION ALL SELECT". Этот способ работает тогда, когда веб-приложения напрямую возвращают результат вывода команды SELECT на страницу. Важно определить количество столбцов
##### Error-based
Внедрение в уязвимый параметр синтаксически неправильного выражения, после чего веб-сайт раскрывает ошибки DBMS (обычно это делается в целях отладки).

##### Boolean blind
"Слепая" инъекции: данные из базы данных в чистом виде никогда не возвращаются. Пользователь может предоставить строку или регулярное выражение для определения "true"-страниц (отсюда и название атаки), и используя бинарный поиск выявлять за 7 запросов любой ASCII символ
##### Time-base
Полностью слепая инъекция, но в отличии от предыдущей - true/false можно определить по времени ответа сервера
##### Out of band
Слепая с выводом на подконтрольный злоумышленнику сервер
##### Stacked Quieries
Иногда бывает можно в одном запросе от приложения к базе данных передать несколько SQL-запросов через `;`. В этом случае SQL injection позволяет сделать уже вообще любой SQL-запрос. Доступность stacked queries зависит от БД и используемого драйвера, к примеру c базами sqlite3 и MySQL обычно нельзя, с базой PostgreSQL и стандартным питоновским драйвером psycopg2 - можно.
### Как предотвращать SQL инъекции?
Большинство случаев SQL-инъекции можно предотвратить, используя параметризованные запросы (также известные как “prepared statements”) вместо конкатенации строк внутри запроса.
Также, от SQL-инъекций спасает концепция ORM (Object-Relational Mapping), но не следует ей слепо доверять.
[Security Code Review 101](https://owasp.org/SecureCodingDojo/codereview101/ "title")
### Дополнительные материалы
- [PayloadsAllTheThings - SQL injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
- [PayloadsAllTheThings - NoSQL injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection)
- [OWASP NoSQL injection slides](https://owasp.org/www-pdf-archive/GOD16-NOSQL.pdf)
- [PortSwigger - SQL injection cheat sheet](https://portswigger.net/web-security/sql-injection/cheat-sheet)
- [SQLMap - Инструмент автоматизации](https://xakep.ru/2011/12/06/57950/)
- [https://en.wikipedia.org/wiki/List_of_object–relational_mapping_software](https://en.wikipedia.org/wiki/List_of_object%E2%80%93relational_mapping_software)
### Примеры из Тинькофф Банка
- [https://jira.tcsbank.ru/browse/IBUL-5866](https://jira.tcsbank.ru/browse/IBUL-5866)
- [https://jira.tcsbank.ru/browse/INFDEV-3156](https://jira.tcsbank.ru/browse/INFDEV-3156)
- [https://jira.tcsbank.ru/browse/IBUL-6509](https://jira.tcsbank.ru/browse/IBUL-6509)
- [https://jira.tcsbank.ru/browse/FOCRM-164](https://jira.tcsbank.ru/browse/FOCRM-164)
### Упражнения
#### Упражнение 1
UNION-based атака SQL инъекции, на извлечение данных из других таблиц - [https://portswigger.net/web-security/sql-injection/union-attacks/lab-retrieve-data-from-other-tables](https://portswigger.net/web-security/sql-injection/union-attacks/lab-retrieve-data-from-other-tables) (Сложность: Низкая)
#### Упражнение 2
https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-String
#### Упражнение 3
https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-File-reading
#### Упражнение 4
https://portswigger.net/web-security/sql-injection/blind/lab-sql-injection-visible-error-based
## Инъекции шаблонов (Server-side template injection)
### Что такое внедрение шаблона на стороне сервера?
Инъекция шаблонов на стороне сервера - это атака, при которой злоумышленник может использовать собственный синтаксис шаблона для введения вредоносной полезной нагрузки в шаблон, который затем выполняется на стороне сервера.
Движки шаблонов предназначены для генерации веб-страниц путем объединения фиксированных шаблонов с изменчивыми данными. Атаки с инъекцией шаблонов на стороне сервера могут происходить, когда вводимые пользователем данные не передаются в виде данных, а непосредственно вставляются в шаблон. Это позволяет злоумышленникам вводить произвольные директивы шаблонов для манипулирования движком шаблонов, часто позволяя им получить полный контроль над сервером.
**Twig example**
https://github.com/marvin/php-twig-example/blob/master/views/index.html
https://github.com/marvin/php-twig-example/blob/master/index.php
### Какой ущерб несут уязвимости к инъекции шаблонов?
Уязвимости инъекции шаблонов на стороне сервера могут подвергать веб-сайты различным атакам в зависимости от движка шаблонов и того, как именно приложение его использует. В некоторых редких случаях эти уязвимости не представляют реального риска для безопасности. Однако, в большинстве случаев, воздействие инъекции шаблона на стороне сервера может быть катастрофическим.
В самом крайнем случае злоумышленник потенциально может добиться удаленного выполнения кода, получив полный контроль над внутренним сервером и используя его для других атак на внутреннюю инфраструктуру.
Даже в тех случаях, когда полное удаленное выполнение кода невозможно, злоумышленник часто все равно может использовать внедрение шаблонов на стороне сервера в качестве основы для множества других атак, потенциально получая доступ на чтение к конфиденциальным данным и произвольным файлам на сервере.
### Как появляются уязвимости инъекции шаблонов?
Рассмотрим несколько примеров кода:
Пример НЕ уязвимый к SSTI:
```
<?php
require_once('lib/Twig/Autoloader.php');
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem('views');
$twig = new Twig_Environment($loader, array(
'cache' => 'cache',
));
echo $twig->render('index.html', array('name' => '', 'title' => 'Startpage'));
?>
```
Пример уязвимый к SSTI:
```
<?php
require_once('lib/Twig/Autoloader.php');
Twig_Autoloader::register();
$twig = new \Twig_Environment(new \Twig_Loader_String());
echo $twig->render("Error message: ".$_GET['error']);
?>
```
### Проведение атаки инъекции шаблонов
#### Сценарий №1 Пример для кода шаблонизатора PHP Twig
Код:
```
echo $twig->render("Error message: ".$_GET['error']);
```
Эксплоит:
```
{{['cat\x20/etc/passwd']|filter('system')}}
```
#### Сценарий №2 Пример для кода шаблонизатора [ASP.NET](http://ASP.NET) Razor
Код:
```
[HttpPost]
[ValidateInput(false)]
public ActionResult Index(string razorTpl)
{
ViewBag.RenderedTemplate = Razor.Parse(razorTpl);
ViewBag.Template = razorTpl;
return View();
}
```
Эксплоит:
```
@{
// C# code
}
```
#### Сценарий №3 Пример для кода шаблонизатора Java Velocity
Код:
```
// Set up the context data
VelocityContext context = new VelocityContext();
context.put( "name", user.name );
// Load the template
String template = getUserTemplateFromRequestBody(request);
RuntimeServices runtimeServices = RuntimeSingleton.getRuntimeServices();
StringReader reader = new StringReader(template);
SimpleNode node = runtimeServices.parse(reader, "myTemplate");
template = new Template();
template.setRuntimeServices(runtimeServices);
template.setData(node);
template.initDocument();
// Render the template with the context data
StringWriter sw = new StringWriter();
template.merge( context, sw );
```
Эксплоит:
```
$name.getClass().forName("java.lang.Runtime").getRuntime().exec(<COMMAND>)
```
### Как защищаться от инъекции шаблонов?
Лучший способ предотвратить инъекцию шаблонов на стороне сервера - **не позволять никаким пользователям изменять или отправлять новые шаблоны**. Однако иногда это неизбежно из-за требований бизнеса.
Один из простейших способов избежать внедрения уязвимостей инъектирования шаблонов на стороне сервера - **всегда использовать “logic-less” шаблонный движок**, такой как Mustache, если в этом нет абсолютной необходимости. Максимально возможное отделение логики от представления может значительно уменьшить вашу подверженность наиболее опасным атакам на основе шаблонов.
Другой мерой является **выполнение пользовательских шаблонов только в песочнице**, где потенциально опасные модули и функции были полностью удалены. К сожалению, недоверенный код в песочнице по своей природе сложен и склонен к обходу.
Наконец, еще одним дополнительным подходом является признание того, что произвольное выполнение кода практически неизбежно, и **применение собственной “песочницы” путем развертывания среды шаблонов**, например, в изолированном контейнере Docker.
### Дополнительные материалы
- [PayloadsAllTheThings - Templates Injections](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection)
- [TPLMap: инструмент автоматизации](https://github.com/epinna/tplmap)
### Упражнения
#### Упражнение 1
Основы инъеции шаблонов на стороне сервера - [https://portswigger.net/web-security/server-side-template-injection/exploiting/lab-server-side-template-injection-basic](https://portswigger.net/web-security/server-side-template-injection/exploiting/lab-server-side-template-injection-basic) (Сложность: низкая)
#### Упражнение 2
Основы инъекции шаблонов на стороне сервера (контекст кода) - [https://portswigger.net/web-security/server-side-template-injection/exploiting/lab-server-side-template-injection-basic-code-context](https://portswigger.net/web-security/server-side-template-injection/exploiting/lab-server-side-template-injection-basic-code-context) (Сложность: низкая)