
Proof-of-Concept für CVE-2026-40864: JupyterHub-XSRF-Bypass über einen Cross-Origin-Formular-POST, der Sec-Fetch-Mode: no-cors ausnutzt. Enthält PoC-HTML, Ursachenanalyse und Offenlegungs-Zeitplan.
Sec-Fetch-Mode: no-cors)Schweregrad: Mittel (CVSS 5.4)
CWE: CWE-352 — Cross-Site Request Forgery (XSRF)
Betroffen: jupyterhub 4.1.0 ≤ Version < 5.4.5 (behoben in 5.4.5)
Sicherheitshinweis: GHSA-m68r-v472-jgq9
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-40864
Danksagung: Romain Deperne
Der XSRF-Schutz von JupyterHub (in 4.1.0 überarbeitet) nutzte den Request-Header Sec-Fetch-Mode, um zu entscheiden,
ob eine Anfrage same-origin war. Er behandelte Sec-Fetch-Mode: no-cors als same-origin — aber
no-cors ist genau das, was ein Browser bei einer Cross-Origin-Übermittlung eines „einfachen" Formulars sendet. Infolgedessen
umgingen Cross-Origin-HTML-Formular-POSTs an die Formular-Endpunkte des Hubs (/hub/spawn, /hub/accept-share)
die XSRF-Prüfung vollständig.
Die JSON-API ist nicht betroffen (sie erfordert einen nicht-einfachen Content-Type, was einen CORS-Preflight erzwingt). Nur die HTML-Formular-Endpunkte sind auf diesem Weg erreichbar.
Die XSRF-Logik springt kurzschlussartig auf „vertrauenswürdig" für eine Reihe von Sec-Fetch-*-Zuständen, die eigentlich
Same-Origin-Navigationen erfassen sollen. Sec-Fetch-Mode: no-cors war in dieser vertrauenswürdigen Menge enthalten. Aber no-cors
ist der Modus, den der Browser einem einfachen <form method=POST> zuweist, das an eine andere Origin sendet —
genau der klassische CSRF-Vektor, den das Token eigentlich verhindern soll. Jeder zustandsändernde Endpunkt, der
einen einfachen Formular-Body akzeptiert und sich ausschließlich auf dieses XSRF-Gate verlässt, ist also cross-origin fälschbar.
Übertragen auf reale Endpunkte: /hub/spawn (den Server des Opfers starten) und /hub/accept-share
(das Opfer eine Freigabe des Servers des Angreifers annehmen lassen) sind beide Formular-POSTs hinter diesem Gate.
/hub/spawn — eine Angreiferseite kann den Single-User-Server des Opfers ohne Einwilligung starten
(Ressourcenverbrauch / unerwarteter Zustand; der Angreifer erhält keinen Zugriff auf diesen Server)./hub/accept-share — wenn der Angreifer ein JupyterHub-Benutzer ist, der seinen eigenen
Server freigeben darf, kann er ein Opfer dazu zwingen, eine Freigabe anzunehmen, wodurch das Opfer Zugriff auf den Server des Angreifers
erhält (ein Vorbereitungsschritt für weitere Social-Engineering- / Data-Drop-Szenarien).Die Verwendung von Sec-Fetch-Mode als Origin-Orakel ist unzuverlässig: no-cors impliziert nicht same-origin. Der Fix
in 5.4.5 vertraut no-cors nicht mehr als same-origin. Betreiber, die nicht sofort aktualisieren können, können
Anfragen mit Sec-Fetch-Mode: no-cors am Reverse-Proxy verwerfen.
poc/csrf_spawn.html — hosten Sie es auf einer beliebigen Angreifer-Origin und lassen Sie einen angemeldeten JupyterHub-Benutzer es öffnen.
Das automatisch übermittelnde Formular sendet einen Cross-Origin-POST an /hub/spawn (der Browser sendet
Sec-Fetch-Mode: no-cors); anfällige Builds akzeptieren ihn ohne gültiges _xsrf-Token und starten den
Server des Opfers. Richten Sie action auf /hub/accept-share für die Variante der Freigabeannahme.
1. Edit TARGET-JUPYTERHUB in poc/csrf_spawn.html
2. Serve the file from an attacker origin (any static host)
3. Open it in a browser already authenticated to the target Hub
4. Observe the victim's server spawn with no XSRF token supplied
Verantwortungsvoll offengelegt. PoC nach Auslieferung des Fixes veröffentlicht.