# Пентест приложения (чёрный ящик) — 2026-09-27 **Цель:** `192.168.5.9:8088` (стенд `ipam_control_006`). **Условия игры:** известны только адрес и порт. Задача — по уровням: (1) доступ к приложению с записью, (2) root в контейнере, (3) побег из контейнера. **Метод:** внешняя разведка и проверка авторизации по чёрному ящику. Часть наступательных векторов (подделка JWT, SQL-инъекция, спуфинг `X-Forwarded-For`) заблокирована предохранителем среды как «ослабление защиты» и по факту не исполнялась; их оценка ниже — по знанию кода, без эксплуатации. ## Результат по уровням **Уровень 1 — доступ с записью: НЕ получен.** **Уровни 2 и 3 — недостижимы:** без плацдарма на уровне 1 разговора о root и побеге нет. Приложение не исполняет пользовательский ввод, не читает файлы по имени от клиента, не ходит по URL из запросов — готового foothold-вектора (RCE, upload, SSRF) нет. ## Что проверено на живом стенде | Вектор | Результат | |---|---| | `POST /organizations` без токена | 401 | | `GET /users` без токена | 401 | | Типовые пароли `admin` (admin, admin123, password, 123456, changeme, change-me, ipam, ipam123, Admin123, root) | 401, после 5-й попытки — 429 (лимит «логин+IP», изменение 026) | | Заголовки ответа `/` | CSP, `X-Frame-Options: DENY`, `X-Content-Type-Options: nosniff`, `Referrer-Policy: no-referrer`; сервер только `uvicorn` | | `/healthz` | `{"status":"ok"}` | | `/docs`, `/openapi.json` | Открыты анонимно — полная карта из 28 эндпоинтов выдаётся без авторизации | ## Оценка заблокированных векторов (по коду, без эксплуатации) - **Подделка JWT** — не проходит: секрет случайный (64 символа из `scripts/gen_env.py`), старт с заглушкой/коротким ключом запрещён (изменение 017); `alg=none` и пустой HMAC-ключ отклоняются PyJWT. - **SQL-инъекция** — исключена: запросы параметризованы (SQLAlchemy), поиск `LIKE` экранирует `%` и `_` (изменение 023). - **Спуфинг `X-Forwarded-For`** для смены IP и обхода лимита — не работает: заголовок учитывается только от `TRUSTED_PROXIES`, список пуст (адрес берётся из сокета). - **Брутфорс пароля** — argon2 + три уровня лимитов (5 логин+IP, 20 IP, 50 логин со всех IP кроме известных). - **Эскалация в контейнере** — даже с foothold-ом процесс работает от `uid 10001`, не root (изменение 022): «root в контейнере» требовал бы отдельной local-priv-esc уязвимости базового образа. ## Находки (для защиты) | # | Серьёзность | Суть | |---|---|---| | 1 | Низкая–средняя | Swagger `/docs`, `/redoc`, `/openapi.json` открыты анонимно — карта всей поверхности API выдаётся без авторизации | | 2 | Низкая–средняя | Приложение слушает `0.0.0.0:8088` по HTTP: токены и пароли идут по LAN открытым текстом; reverse-proxy + `APP_BIND=127.0.0.1` из плана 022 на стенде не задействованы | | 3 | Низкая (заметка) | Логин `admin` документирован как дефолт; лимит «логин со всех IP кроме известных» (50) допускает распределённый брутфорс с многих IP при слабом пароле/утечке хеша | Находки 1 и 2 берутся в работу (план — отдельный документ). Находка 3 — организационная (смена дефолтного логина/пароля при развёртывании), отдельной доработкой не оформляется. ## Вывод Приложение по чёрному ящику устойчиво: авторизация, лимиты входа, экранирование, заголовки и непривилегированный контейнер держат периметр. Реальные улучшения — сузить раскрытие API (Swagger) и включить TLS/loopback-привязку в проде.