Command Injection: обход blacklist-фильтра WAF
Админ-панель содержит функцию ping с blacklist-фильтром (пробел, ;, cat, flag). Фильтр обходится через ${IFS}, $(), backticks и регистр имени переменной окружения.
mediumphpPro
Задача
# OS Command Injection: обход naïve WAF на blacklist
## Сценарий
Ты — пентестер, проверяющий безопасность интернет-магазина **HackShop** на PHP/Apache. В предыдущем релизе разработчики уже узнали о существовании инъекции команд в форме диагностики сети админ-панели и попытались закрыть проблему «быстрым» способом — прикрутили на вход WAF-подобный фильтр на основе чёрного списка: блокируют пробел и несколько «опасных» подстрок (`cat`, `flag`, `;`, `tail`, `more`, ...). В коммите-фиксе сказано: «теперь команды нельзя инжектить, payload отбрасывается».
Твоя задача — показать команде разработки, что чёрный список не решает проблему архитектурно: shell-язык слишком богат, чтобы перечислить в чёрном списке все варианты записи одной и той же команды. Если ввод по-прежнему попадает в shell, будут найдены способы обхода.
## Цель
1. Войти в админ-панель и открыть инструмент **Диагностика сети (WAF Protected)**.
2. Определить, какие именно символы и слова блокируются (через эксперименты в форме).
3. Подобрать payload, обходящий фильтр одной из техник: `${IFS}` вместо пробелов, brace expansion (`{cmd1,cmd2}`), backticks/`$()` без блокированных слов, перенос строки `%0a` вместо `;`, заглавный регистр имени переменной окружения.
4. Прочитать значение переменной окружения `LAB_FLAG` и забрать флаг.
## Теория
**WAF на чёрных списках для shell-инъекций** обречён по тем же причинам, что и blacklist для SQLi: пространство «опасных» комбинаций бесконечно, а нормализация на стороне приложения не совпадает с нормализацией оболочки. Распространённые техники обхода:
- **`${IFS}` вместо пробела** — `IFS` (Internal Field Separator) по умолчанию содержит пробел/таб/перевод строки; конструкция `cmd1${IFS}cmd2` парсится оболочкой как два аргумента.
- **Brace expansion** — `{ping,-c,3,127.0.0.1}` развернётся в `ping -c 3 127.0.0.1` без явных пробелов.
- **Подстановка команд через backticks/`$()`** — `` `printenv LAB_FLAG` `` или `$(printenv LAB_FLAG)` исполняет команду и подставляет её вывод.
- **Перенос строки `%0a`** — URL-кодированный `\n` shell трактует как разделитель команд.
- **Конкатенация кавычками** — `c''at` разрывает ключевое слово до выполнения, фильтр по байтам пропускает.
- **Регистр имени переменной** — если в blacklist строчное `flag`, имя `LAB_FLAG` (заглавные) проходит без проблем.
- **Кодирование/обфускация** — `bash -c "$(echo cHJpbnRlbnYgTEFCX0ZMQUc=|base64 -d)"` или ANSI-C quoting `$'\x70rintenv\x20LAB_FLAG'`.
Корректная защита **не пытается перечислить опасное** — она либо строит allowlist (только разрешённые хосты), либо вообще убирает оболочку из цепочки исполнения, передавая аргументы процессу через массив.
**Уязвимый паттерн (двойная ошибка):**
Обработчик ping-формы устроен так: перед запуском команды значение поля `host` пропускается через blacklist из конкретных запрещённых подстрок (`;`, пробел, несколько утилит вроде `cat`, слово `flag`). Если ни одна подстрока не найдена, ввод **по-прежнему конкатенируется в shell-команду** и отправляется в `shell_exec`. Таким образом две ошибки накладываются друг на друга: (1) попытка защититься чёрным списком — наивно по построению, потому что shell-язык богат; (2) даже без blacklist пользовательский ввод по-прежнему попадает в shell-интерпретатор, который запускает `/bin/sh -c "..."`. Любой обход чёрного списка автоматически приводит к выполнению произвольной команды.
## Точка входа атаки
- **Эндпоинт:** `POST /admin/ping`, поле формы `host`
- **Учётные данные администратора:** `admin` / `lab-secret`
- **Где искать флаг:** значение `LAB_FLAG` в переменных окружения процесса лабы — попадает в HTTP-ответ после успешного обхода фильтра
## Данные для входа
| Роль | Логин | Пароль |
|------|-------|--------|
| Администратор | `admin` | `lab-secret` |
| Обычный пользователь | `demo` | `demo` |
> Совет: начни со стандартных техник инъекции — что отвечает приложение? По форме сообщения об ошибке восстанови набор запрещённых символов и подстрок, затем подбери эквивалентную конструкцию shell, в которой ни один из них не встречается.