
Docker-basiertes Labor zur Reproduktion von CVE-2026-46645, einer Autorisierungsumgehung im ajax_lookup-Endpunkt von SQLAdmin. Enthält anfällige und gepatchte Ziele, PoC-Skript und manuelle curl-Reproduktionsschritte für Sicherheitsforschung und -bildung.
Dieses Repository enthält ein lokales Docker-Labor zur Reproduktion von CVE-2026-46645, einer Berechtigungsumgehung-Schwachstelle, die den ajax_lookup-Endpunkt von SQLAdmin betrifft.
SQLAdmin ist eine Administrationsoberfläche für SQLAlchemy-Modelle in Starlette- und FastAPI-Anwendungen. Das anfällige Verhalten tritt auf, wenn eine Anwendung einen ModelView mit is_accessible(request) einschränkt, aber die ajax_lookup-Route von SQLAdmin dieselbe Zugriffskontrollentscheidung nicht durchsetzt, bevor sie Lookup-Ergebnisse zurückgibt.
Dieses Labor vergleicht zwei SQLAdmin-Versionen:
| Dienst | SQLAdmin-Version | Zweck | URL |
|---|---|---|---|
vuln | 0.25.0 | Angreifbares Ziel | http://127.0.0.1:8001 |
patched | 0.25.1 | Vergleichsziel (gepatcht) | http://127.0.0.1:8002 |
Die demonstrierte Schwachstellenkette ist:```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
Das Labor verwendet absichtlich ein einfaches `Report` / `SecretProject` Datenmodell, um die Umgehung der Autorisierung leicht verständlich zu machen. Diese Modellnamen sind nicht die Ursache der Schwachstelle. Sie dienen lediglich dazu, eine kontrollierte Reproduktionsbedingung zu schaffen.
Dieses Labor ist ausschließlich für kontrollierte lokale Forschung, Quellcode-Verständnis und Portfolio-Demonstration konzipiert.
## Bestätigte Fakten
| Behauptung | Beweis | Wie im Labor zu überprüfen |
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Der `ajax_lookup`-Endpunkt von SQLAdmin ist die betroffene Komponente. | Der öffentliche Hinweis beschreibt das betroffene Endpunktformat als `GET /{identity}/ajax/lookup?name=<field>&term=<query>`. | Führen Sie den PoC aus und beobachten Sie die Anfragen an `/admin/report/ajax/lookup?name=project&term=Secret`. |
| SQLAdmin `0.25.0` wird als verwundbares Vergleichsziel verwendet. | Das Labor installiert `sqladmin==0.25.0` im `vuln`-Container. | Führen Sie `docker compose exec -T vuln python -m pip show sqladmin` aus. |
| SQLAdmin `0.25.1` wird als gepatchtes Vergleichsziel verwendet. | Der öffentliche Hinweis und die Versionshinweise identifizieren `0.25.1` als die behobene Version. | Führen Sie `docker compose exec -T patched python -m pip show sqladmin` aus. |
| Die Ursache liegt in der upstream `Admin.ajax_lookup()`-Route von SQLAdmin. | Der Patch fügt fehlende Authentifizierung und Durchsetzung von `is_accessible(request)` zu `ajax_lookup()` hinzu. | Untersuchen Sie `Admin.ajax_lookup()` in beiden Containern mit den Befehlen in dieser README. |
| Das Labor erstellt eine eingeschränkte `ModelView`. | `ReportAdmin.is_accessible(request)` gibt absichtlich `False` zurück. | Untersuchen Sie `app/main.py`. |
| Der PoC verwendet eine authentifizierte Sitzung. | Der PoC meldet sich zuerst bei `/admin/login` an, behält das Sitzungscookie und fordert dann `ajax_lookup` an. | Führen Sie `python3 poc/poc.py --base-url http://127.0.0.1:8001` aus. |
| Das verwundbare Signal ist die Datenoffenlegung. | SQLAdmin `0.25.0` gibt HTTP 200 und JSON-Suchergebnisse aus einer eingeschränkten Ansicht zurück. | Das verwundbare Ziel sollte `Secret Project Alpha` und `Secret Project Beta` zurückgeben. |
| Das gepatchte Signal ist die Zugriffsverweigerung. | SQLAdmin `0.25.1` gibt HTTP 403 für die gleiche authentifizierte Anfrage zurück. | Das gepatchte Ziel sollte `403 Forbidden` zurückgeben. |
## Annahmen und Unbekanntes
Dieses Labor verwendet `sqladmin==0.25.0` als verwundbare Basis und `sqladmin==0.25.1` als gepatchte Basis.
Das Labor konzentriert sich auf die Bedingung zur Umgehung der Autorisierung, bei der:```text
A user is authenticated,
the target ModelView is not accessible,
but ajax_lookup is requested directly.
Das Labor versucht nicht, jedes mögliche SQLAdmin-Bereitstellungsmuster zu reproduzieren. Es erstellt absichtlich eine kleine Starlette-Anwendung mit einer eingeschränkten Admin-Ansicht, damit der Verhaltensunterschied zwischen anfälligen und gepatchten Versionen leicht zu überprüfen ist.
Die Modelle Report und SecretProject sind Labor-exklusive Objekte. Sie sind nicht Teil von SQLAdmin selbst.
Der PoC versucht keine Privilegienausweitung, Datenänderung, Sitzungsdiebstahl, externe Rückrufe, Persistenz oder Angriffe auf Nicht-Laborsysteme.
Die Grundursache liegt in SQLAdmins vorgelagerter Admin.ajax_lookup()-Route, nicht im Anwendungscode dieses Labors.
SQLAdmin ermöglicht Entwicklern, den Zugriff auf Admin-Ansichten durch Überschreiben einzuschränken:```python ModelView.is_accessible(request)
Andere Admin-Routen müssen diese Zugriffskontrollentscheidung erzwingen, bevor die Anfrage fortgesetzt werden kann. Beispielsweise überprüfen Routen wie list, create, details, delete, edit und export, ob die aktuelle Anfrage auf die Ziel-`ModelView` zugreifen darf. Die anfällige `ajax_lookup`-Route erzwingt nicht dieselbe Zugriffskontrollentscheidung. Der `ajax_lookup`-Endpunkt wird von der `form_ajax_refs`-Funktion von SQLAdmin verwendet, um Beziehungswerte dynamisch zu laden. Das Endpunktformat lautet:```text
/admin/<identity>/ajax/lookup?name=<field>&term=<query>
In der verletzlichen Version löst ajax_lookup() das Ziel-ModelView auf, liest den Lookup-Feldnamen und den Suchbegriff aus der Abfragezeichenfolge, ruft dann den AJAX-Loader auf und gibt JSON-Ergebnisse zurück. Der fehlende Sicherheitsschritt besteht darin, dass nicht zuerst überprüft wird, ob die aktuelle Anfrage auf dieses ModelView zugreifen darf.
Die Sicherheitsauswirkung ist, dass ein authentifizierter Benutzer daran gehindert werden kann, über normale UI-Routen auf eine eingeschränkte Admin-Ansicht zuzugreifen, aber dennoch direkt den AJAX-Lookup-Endpunkt für diese Ansicht anfordern und Beziehungs-Lookup-Daten erhalten kann.
SQLAdmin 0.25.1 behebt dies, indem es die Zugriffskontrolle innerhalb von ajax_lookup() erzwingt. Die gepatchte Route prüft model_view.is_accessible(request) und gibt HTTP 403 zurück, wenn die Zielansicht nicht zugänglich ist.
Dieses Labor definiert ReportAdmin.is_accessible(request), um nur zur Reproduktion der verletzlichen Bedingung False zurückzugeben. Der Laborcode ist nicht die Ursache. Es ist ein kontrolliertes Test-Setup, das beweist, ob die übergeordnete ajax_lookup()-Route von SQLAdmin die Zugriffsentscheidung respektiert.
Erwartetes Verhalten Unterschied:```text sqladmin 0.25.0 -> HTTP 200 with JSON lookup results sqladmin 0.25.1 -> HTTP 403 Forbidden
## Zusammenfassung des Quell-Patches
Der bedeutende Upstream-Patch ist die Hinzufügung von Authentifizierungs- und Autorisierungsdurchsetzung zu `Admin.ajax_lookup()`.