Files
ayurishchevandClaude Opus 5.5 744a025960 Задачи 032-033: ролевая модель с привязкой к организации, исправления по ревью
Пентест (docs/reviews/2026-09-27-pentest.md) и план 031 (Swagger, TLS) — план, не реализован.
032 Роль superadmin (без организации) и привязка admin/viewer к одной организации:
    users.organization_id + CHECK, audit_log.organization_id (миграции 0010-0012);
    require_org/scope_org во всех чтениях и записях, журнал и «Обзор» в границах
    организации; пользователи, организации, типы устройств, настройки журнала — только superadmin.
033 Исправление находок ревью 032 (docs/reviews/2026-09-27-changes-032-review.md,
    docs/reviews/2026-09-27-codebase-review.md):
    - FK audit_log.organization_id ON DELETE SET NULL (миграция 0013) — удаление организаций;
    - проверка организации в предпросмотре подсети;
    - инвариант «роль — организация» по итоговому состоянию (повышение снимает организацию,
      понижение требует её), 422/404 вместо обезличенных 409;
    - одинаковый 404 для чужих и несуществующих объектов (VRF, устройство, parent_id, оператор);
    - отказы удаления в журнале организации, счётчики типов в пределах организации;
    - UI: живое поле «Организация» в диалоге пользователя, бейдж superadmin; род в текстах 404.
README актуализирован под ролевую модель.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 11:26:57 +03:00

42 lines
5.2 KiB
Markdown

# Пентест приложения (чёрный ящик) — 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-привязку в проде.