Bypass Filters: Строгий WAF
Обход фильтрации пробелов и черного списка команд для RCE.
mediumgolangPro
Задача
# OS Command Injection: Обход naïve WAF на blacklist
## Сценарий
Вы продолжаете аудит **SecureShop**. После прошлого инцидента с Network Diagnostics команда разработки добавила «WAF»: список запрещённых подстрок (`;`, `&`, `|`, `printenv`, `cat`, `env`...) и блокировка любых пробелов. Архитектор уверен, что проблема решена. Однако blacklist-подход для command injection — известный антипаттерн: оболочка предоставляет десятки способов выразить одну и ту же команду без запрещённых символов и слов.
## Цель
1. Войдите в админ-панель и откройте инструмент **Network Diagnostics (WAF Protected)**.
2. Определите, какие именно символы и слова блокируются (через эксперименты в форме).
3. Подберите payload, обходящий фильтр одной из техник: `${IFS}`/`$'\t'` вместо пробелов, brace expansion (`{cmd1,cmd2}`), backticks/`$()` без блокированных слов, кодирование через `bash -c "$(echo ZWNobw==|base64 -d)"`.
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)` исполняет команду и подставляет её вывод.
* **Кодирование/обфускация** — `bash<<<<(echo cHJpbnRlbnYgTEFCX0ZMQUc=|base64 -d)` или ANSI-C quoting `$'\x70rintenv\x20LAB_FLAG'`.
* **Глобирование** — если запрещено слово `printenv`, помогает `/usr/bin/p?intenv` или `/usr/bin/print*` через wildcards.
Корректная защита **не пытается перечислить опасное** — она либо строит allowlist (только цифры/точки/буквы для hostname), либо вообще убирает оболочку из цепочки исполнения, либо заменяет внешнюю команду на эквивалент в стандартной библиотеке Go (например, `net.LookupHost` + `golang.org/x/net/icmp` вместо `ping`).
**Уязвимый паттерн (двойная ошибка):**
```go
var bannedSubstrings = []string{";", "&", "|", "$(", "`", "printenv", "cat", "env"}
func (h *Handler) AdminPing(w http.ResponseWriter, r *http.Request) {
host := r.FormValue("host")
// (1) Blacklist по подстрокам — десятки способов обхода:
for _, banned := range bannedSubstrings {
if strings.Contains(host, banned) {
http.Error(w, "WAF blocked", 400)
return
}
}
if strings.Contains(host, " ") { // (1b) запрет пробелов
http.Error(w, "WAF blocked", 400)
return
}
// (2) Команда всё равно идёт через sh -c:
cmd := exec.Command("sh", "-c", "ping -c 3 "+host)
out, _ := cmd.CombinedOutput()
w.Write(out)
}
```
## Точка входа атаки
* **Эндпоинт:** `POST /admin/ping` (поле `host`)
* **Учётные данные администратора:** `admin` / `LabAdmin2024!`
* **Где искать флаг:** значение `LAB_FLAG` в переменных окружения процесса лабы