--- 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 ![](https://www.researchgate.net/publication/292997018/figure/fig1/AS:694192334450690@1542519758561/SQL-Injection-Categories-and-Types-proached-through-various-techniques-which-are-broadly.jpg) ### Как найти SQL - Ввод символа одиночной кавычки ' и поиск ошибок или других аномалий. - Ввод некоторого синтаксиса, специфичного для SQL и поиск систематических различий в результирующих ответах приложения. - Ввод булевых условий, таких как OR 1=1 и OR 1=2, и поиск различий в ответах приложения. - Отправка полезных нагрузок (прим. Временная задержка) ##### Union-based Классический вариант внедрения SQL-кода, когда в уязвимый параметр (или несколько параметров) передаётся выражение (фактически новый запрос), начинающееся с "UNION SELECT" или "UNION ALL SELECT". Этот способ работает тогда, когда веб-приложения напрямую возвращают результат вывода команды SELECT на страницу. Важно определить количество столбцов ##### Error-based Внедрение в уязвимый параметр синтаксически неправильного выражения, после чего веб-сайт раскрывает ошибки DBMS (обычно это делается в целях отладки). ![](https://miro.medium.com/v2/resize:fit:4800/format:webp/1*C02Rfg_EwWHfio7VnJtcFQ.png) ##### 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) (Сложность: низкая)