
Detecte com segurança o bypass de autenticação SAML do Citrix NetScaler CVE-2026-19490
Uma verificação de vulnerabilidade segura e não autenticada para CVE-2026-19490, o bypass de
autenticação pré-autenticação no caminho do provedor de serviços SAML do Citrix NetScaler ADC / NetScaler Gateway
(CTX696939,
publicado em 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). Reportado por Samarth Vashisht da
equipe de testes de intrusão do JPMorgan Chase.
O appliance decodifica em base64 o parâmetro RelayState de uma resposta SAML e, quando o texto simples
começa com ctx=, entrega o restante ao desserializador de contexto nFactor. Em uma build não corrigida, uma
falha de desserialização propaga o comprimento do RelayState decodificado como o código de disposição interno
da requisição em vez de um erro, de modo que um atacante não autenticado escolhe qual ramo interno
o appliance segue em seguida simplesmente escolhendo o comprimento do RelayState. Alguns ramos geram uma sessão
real do Gateway; outros derrubam o packet engine e reiniciam o appliance. Este script não faz nenhum dos dois
— ele envia o único comprimento validado para não criar sessão e deixar o packet engine em paz, e
responde a uma única pergunta por alvo: este appliance é vulnerável? Um resultado diferente de
VULNERABLE não é por si só um atestado de saúde.
# 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+, apenas biblioteca padrão — sem pacotes de terceiros.
Aponte a ferramenta para o Gateway ou virtual server AAA, não para a interface de gerenciamento. A exposição é
por virtual server, então um appliance com vários VIPs precisa que cada um seja testado. A sonda carrega um
envelope de segurança rígido — um comprimento de RelayState validado, nunca varrido — que
É Seguro Executar? detalha.
| Flag | Descrição |
|---|---|
TARGET | Um ou mais alvos [https://]HOST[:PORT]; o esquema padrão é https:// |
-f, --targets-file FILE | Lê alvos de um arquivo (um por linha; comentários com #) |
--timeout SECS | Timeout por requisição (padrão: 15) |
--workers N | Alvos concorrentes (padrão: 16); a saída permanece na ordem de entrada |
-b, --brief | Uma linha alinhada por alvo — ideal para escanear muitos hosts |
--json | Emite JSON estruturado, incluindo cada requisição enviada por alvo |
--no-color | Desativa saída colorida (também respeita NO_COLOR e não-TTY) |
Um appliance vulnerável (a saída padrão de duas linhas). O marcador [!] e VULNERABLE são renderizados
em vermelho em um TTY:
$ ./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
Um appliance corrigido:
$ ./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)
A proteção contra falsos positivos disparando. A sonda e o controle de mesmo comprimento retornaram o sinal não corrigido, então a resposta não depende do que foi enviado e a resposta aparentemente decisiva é retirada:
$ ./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
Varrendo um parque (--brief). As duas linhas gateway.example.com são os virtual servers SP e somente IdP
no mesmo appliance — ambos respondem, o que uma verificação de pré-condição de configuração não conseguiria:
$ ./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
Saída legível por máquina (--json). Cada requisição é incluída, então um achado pode ser re-derivado
a partir da evidência em vez de ser confiado. O controle é registrado por sua relação com a sonda em vez
de como um veredito próprio, porque um controle que se lê como uma build corrigida é o resultado esperado em
toda build:
$ ./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"
}
]
}
]
Sim. Ele foi projetado para uso em produção e em avaliações: