
Лаборатория на основе Docker для воспроизведения CVE-2026-46645, обхода авторизации в конечной точке ajax_lookup SQLAdmin. Включает уязвимые и исправленные цели, PoC-скрипт и шаги ручного воспроизведения с помощью curl для исследований в области безопасности и обучения.
Данный репозиторий содержит локальную лабораторию Docker для воспроизведения CVE-2026-46645 — уязвимости обхода авторизации, затрагивающей конечную точку ajax_lookup SQLAdmin.
SQLAdmin — это административный интерфейс для моделей SQLAlchemy в приложениях Starlette и FastAPI. Уязвимость возникает, когда приложение ограничивает ModelView с помощью is_accessible(request), но маршрут ajax_lookup SQLAdmin не применяет то же самое решение контроля доступа перед возвратом результатов поиска.
Данная лаборатория сравнивает две версии SQLAdmin:
| Сервис | Версия SQLAdmin | Назначение | URL |
|---|---|---|---|
vuln | 0.25.0 | Уязвимый целевой сервер | http://127.0.01:8001 |
patched | 0.25.1 | Исправленный целевой сервер для сравнения | http://127.0.0.2:8002 |
Цепочка демонстрируемой уязвимости:```text Authenticated low-privileged user → restricted SQLAdmin ModelView → ModelView.is_accessible(request) returns False → user directly requests the ajax_lookup endpoint → SQLAdmin 0.25.0 returns relationship lookup data → SQLAdmin 0.25.1 blocks the same request with HTTP 403
Лабораторная работа намеренно использует простую модель данных `Report` / `SecretProject`, чтобы обход авторизации было легко понять. Эти имена моделей не являются первопричиной уязвимости. Они используются только для создания контролируемых условий воспроизведения.
Данная лабораторная работа предназначена исключительно для контролируемого локального исследования, понимания на уровне исходного кода и демонстрации в портфолио.
## Подтверждённые факты
| Утверждение | Доказательство | Как проверить в этой лабораторной работе |
| ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Конечная точка `ajax_lookup` в SQLAdmin является уязвимым компонентом. | В публичном уведомлении описана затрагиваемая конечная точка как `GET /{identity}/ajax/lookup?name=<field>&term=<query>`. | Запустите PoC и наблюдайте запросы к `/admin/report/ajax/lookup?name=project&term=Secret`. |
| SQLAdmin версии `0.25.0` используется в качестве уязвимой цели для сравнения. | Лабораторная работа устанавливает пакет `sqladmin==0.25.0` в контейнере `vuln`. | Выполните `docker compose exec -T vuln python -m pip show sqladmin`. |
| SQLAdmin версии `0.25.1` используется в качестве исправленной цели для сравнения. | В публичном уведомлении и примечаниях к выпуску указана версия `0.25.1` как исправленная. | Выполните `docker compose exec -T patched python -m pip show sqladmin`. |
| Первопричина находится в маршруте `Admin.ajax_lookup()` вышестоящего компонента SQLAdmin. | Исправление добавляет отсутствующую аутентификацию и проверку `is_accessible(request)` в метод `ajax_lookup()`. | Изучите метод `Admin.ajax_lookup()` внутри обоих контейнеров с помощью команд из этого README. |
| Лабораторная работа создаёт ограниченное представление `ModelView`. | Метод `ReportAdmin.is_accessible(request)` намеренно возвращает `False`. | Изучите файл `app/main.py`. |
| PoC использует аутентифицированную сессию. | PoC сначала выполняет вход в `/admin/login`, сохраняет cookie сессии, а затем отправляет запрос к `ajax_lookup`. | Выполните `python3 poc/poc.py --base-url http://127.0.0.1:8001`. |
| Сигналом уязвимости является раскрытие данных. | SQLAdmin версии `0.25.0` возвращает HTTP 200 и результаты JSON-поиска из ограниченного представления. | Уязвимая цель должна вернуть `Secret Project Alpha` и `Secret Project Beta`. |
| Сигналом исправления является отказ в доступе. | SQLAdmin версии `0.25.1` возвращает HTTP 403 для того же аутентифицированного запроса. | Исправленная цель должна вернуть `403 Forbidden`. |
## Допущения и неизвестные
В этой лабораторной работе в качестве уязвимой версии используется `sqladmin==0.25.0`, а в качестве исправленной — `sqladmin==0.25.1`.
Лабораторная работа посвящена условию обхода авторизации, при котором:```text
A user is authenticated,
the target ModelView is not accessible,
but ajax_lookup is requested directly.
Лабораторная работа не пытается воспроизвести каждый возможный шаблон развертывания SQLAdmin. Она намеренно создает небольшое приложение Starlette с одним ограниченным представлением администратора, чтобы разница в поведении между уязвимой и исправленной версиями была легко проверяема.
Модели Report и SecretProject — это объекты, используемые только в лабораторной работе. Они не являются частью самого SQLAdmin.
PoC не пытается реализовать повышение привилегий, изменение данных, кражу сессий, внешние обратные вызовы, сохранение состояния или атаки на системы, не являющиеся частью лабораторной работы.
Коренная причина находится в маршруте Admin.ajax_lookup() в вышестоящем компоненте SQLAdmin, а не в коде приложения этой лабораторной работы.
SQLAdmin позволяет разработчикам ограничивать доступ к представлениям администратора, переопределяя:```python ModelView.is_accessible(request)
Ожидается, что другие маршруты администратора будут применять это решение контроля доступа перед тем, как разрешить запросу продолжиться. Например, такие маршруты, как list, create, details, delete, edit и export, проверяют, разрешен ли текущему запросу доступ к целевому `ModelView`.
Уязвимый маршрут `ajax_lookup` не применял то же самое решение контроля доступа.
Конечная точка `ajax_lookup` используется функцией `form_ajax_refs` SQLAdmin для динамической загрузки значений отношений. Ее формат конечной точки:```text
/admin/<identity>/ajax/lookup?name=<field>&term=<query>
В уязвимой версии ajax_lookup() определяет целевой ModelView, считывает имя поля поиска и поисковый запрос из строки запроса, затем вызывает загрузчик AJAX и возвращает результаты в формате JSON. Отсутствующим шагом безопасности является то, что он не проверяет, разрешён ли текущий запрос на доступ к этому ModelView.
Последствие для безопасности заключается в том, что аутентифицированный пользователь может быть заблокирован в доступе к ограниченному административному представлению через обычные маршруты пользовательского интерфейса, но всё ещё может напрямую запросить конечную точку AJAX lookup для этого представления и получить данные о связях.
SQLAdmin 0.25.1 исправляет это, обеспечивая контроль доступа внутри ajax_lookup(). Исправленный маршрут проверяет model_view.is_accessible(request) и возвращает HTTP 403, если целевое представление недоступно.