
Citrix NetScaler SAML-Authentifizierungs-Bypass CVE-2026-19490 sicher erkennen
Ein sicherer, unauthentifizierter Schwachstellen-Check für CVE-2026-19490, die Pre-Authentication-
Authentication-Bypass in dem SAML-Service-Provider-Pfad von Citrix NetScaler ADC / NetScaler Gateway
(CTX696939,
veröffentlicht am 2026-08-19). CWE-288, CVSS v4.0 9.3
(AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:L/SI:L/SA:L). Gemeldet von Samarth Vashisht vom
Penetrationstest-Team von JPMorgan Chase.
Die Appliance base64-dekodiert den RelayState-Parameter einer SAML-Antwort und übergibt, wenn der
Klartext mit ctx= beginnt, den Rest an den nFactor-Context-Deserializer. Bei einem ungepatchten Build
propagiert ein Deserialisierungsfehler die dekodierte RelayState-Länge als internen
Disposition-Code der Anfrage statt eines Fehlers, sodass ein unauthentifizierter Angreifer allein
dadurch, dass er die Länge des RelayState wählt, bestimmt, welchen internen Zweig die Appliance als
Nächstes nimmt. Einige Zweige erzeugen eine echte Gateway-Session; andere bringen die Packet Engine zum
Absturz und starten die Appliance neu. Dieses Skript tut weder das eine noch das andere — es sendet die
eine validierte Länge, die nachweislich keine Session erzeugt und die Packet Engine unberührt lässt, und
beantwortet pro Ziel eine einzige Frage: ist diese Appliance verwundbar? Ein anderes Ergebnis als
VULNERABLE ist für sich genommen kein Unbedenklichkeitsbescheinigung.
# single target
./cve_2026_19490_check.py https://gateway.example.com
# a specific Gateway or AAA virtual server
./cve_2026_19490_check.py https://gateway.example.com:9443
# several targets; the scheme defaults to https://
./cve_2026_19490_check.py gw-a.example.com gw-b.example.com:9443
# scan a list, one target per line ('#' comments allowed), compact output
./cve_2026_19490_check.py -f targets.txt --brief
# machine-readable output for pipelines
./cve_2026_19490_check.py -f targets.txt --json > results.json
Python 3.8+, nur Standardbibliothek — keine Drittanbieter-Pakete.
Richten Sie das Tool auf den Gateway- oder AAA-Virtual-Server, nicht auf die Management-Schnittstelle.
Die Exposition gilt pro Virtual Server, daher muss bei einer Appliance mit mehreren VIPs jeder einzelne
getestet werden. Die Sonde trägt eine harte Sicherheitshülle — eine validierte RelayState-Länge, niemals
gesweept — die Ist es sicher auszuführen? darlegt.
| Flag | Beschreibung |
|---|---|
TARGET | Ein oder mehrere [https://]HOST[:PORT]-Ziele; das Schema ist standardmäßig https:// |
-f, --targets-file FILE | Ziele aus einer Datei lesen (eine pro Zeile; #-Kommentare) |
--timeout SECS | Timeout pro Anfrage (Standard: 15) |
--workers N | Gleichzeitige Ziele (Standard: 16); die Ausgabe bleibt in Eingabereihenfolge |
-b, --brief | Eine ausgerichtete Zeile pro Ziel — ideal zum Scannen vieler Hosts |
--json | Strukturiertes JSON ausgeben, einschließlich jeder pro Ziel gesendeten Anfrage |
--no-color | Farbige Ausgabe deaktivieren (berücksichtigt auch NO_COLOR und Nicht-TTY) |
Eine verwundbare Appliance (die standardmäßige zweizeilige Ausgabe). Der [!]-Marker und VULNERABLE
werden auf einem TTY rot dargestellt:
$ ./cve_2026_19490_check.py https://gateway.example.com:9443
[!] https://gateway.example.com:9443: VULNERABLE [internal-error-43524]
HTTP 500 / 43524: the decoded RelayState length was propagated as the internal error, so the CTX696939 fix is absent
Eine gepatchte Appliance:
$ ./cve_2026_19490_check.py https://vpn.example.com
[+] https://vpn.example.com: PATCHED [fixed-error-returned]
HTTP 200 "Malformed Assertion": the fixed error was returned on the path this probe reached, so the CTX696939 fix is present (>= 13.1-63.21 / 14.1-73.32)
Der False-Positive-Guard greift. Die Sonde und die gleich lange Kontrolle lieferten beide das ungepatchte Signal zurück, sodass die Antwort nicht davon abhängt, was gesendet wurde, und die entscheidend wirkende Antwort zurückgezogen wird:
$ ./cve_2026_19490_check.py https://sp-strict.example.com
[?] https://sp-strict.example.com: INCONCLUSIVE [flat-response]
the probe and the same-length control both answered HTTP 500 / 43524, so the reply does not depend on what was sent and the fix was never exercised; unknown, not patched
Sweeping eines Bestands (--brief). Die beiden gateway.example.com-Zeilen sind die SP- und die
IdP-only-Virtual-Server auf derselben Appliance — beide antworten, was ein
Konfigurations-Vorbedingungs-Check nicht schaffen würde:
$ ./cve_2026_19490_check.py -f targets.txt --brief; echo "exit: $?"
VULNERABLE https://gateway.example.com:9443 internal-error-43524
VULNERABLE https://gateway.example.com:9444 internal-error-43524
PATCHED https://vpn.example.com fixed-error-returned
INCONCLUSIVE https://sp-strict.example.com flat-response
UNAFFECTED https://lb.example.com no-saml-endpoint
ERROR https://www.example.com not-identified
exit: 1
Maschinenlesbare Ausgabe (--json). Jede Anfrage ist enthalten, sodass ein Befund aus den Belegen
rekonstruiert werden kann, statt ihm zu vertrauen. Die Kontrolle wird über ihre Beziehung zur Sonde
erfasst und nicht als eigenes Urteil, weil eine Kontrolle, die wie ein gepatchter Build aussieht, auf
jedem Build das erwartete Ergebnis ist:
$ ./cve_2026_19490_check.py https://gateway.example.com:9443 --json
[
{
"target": "https://gateway.example.com:9443",
"verdict": "VULNERABLE",
"reason": "internal-error-43524",
"detail": "HTTP 500 / 43524: the decoded RelayState length was propagated as the internal error, so the CTX696939 fix is absent",
"netscaler_indicators": [
"CSP contains citrixng://",
"CSP contains com.citrix.nsgclient://",
"CSP contains nsgcepa://",
"CSP report-uri /nscsp_violation/report_uri",
"/vpn/js/rdx/ present (HTTP 404)"
],
"attempts": [
{
"kind": "probe",
"path": "/cgi/samlauth",
"status": 500,
"state": "unpatched",
"detail": "HTTP 500 / 43524: the decoded RelayState length was propagated as the internal error"
},
{
"kind": "control",
"path": "/cgi/samlauth",
"status": 200,
"state": "differs-from-probe",
"detail": "same-length control: HTTP 200 \"Malformed Assertion\": the fixed error was returned"
}
]
}
]
Ja. Es ist für den Produktiv- und Assessment-Einsatz konzipiert: