
Безопасное обнаружение обхода аутентификации SAML в Citrix NetScaler CVE-2026-19490
Безопасная проверка уязвимости без аутентификации для CVE-2026-19490 — обхода
аутентификации до её прохождения в пути поставщика услуг SAML Citrix NetScaler ADC / NetScaler Gateway
(CTX696939,
опубликовано 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). Сообщено Samarth Vashisht из
команды тестирования на проникновение JPMorgan Chase.
Устройство декодирует base64 параметр RelayState SAML-ответа и, когда открытый текст
начинается с ctx=, передаёт остаток десериализатору контекста nFactor. На неисправленной сборке
сбой десериализации передаёт длину декодированного RelayState в качестве внутреннего
кода диспозиции запроса вместо ошибки, поэтому неаутентифицированный атакующий выбирает, какую внутреннюю ветвь
устройство выберет далее, просто выбирая длину RelayState. Некоторые ветви создают настоящую
сессию Gateway; другие приводят к падению packet engine и перезапуску устройства. Этот скрипт не делает ни того,
ни другого — он отправляет одну длину, проверенную на то, что она не создаёт сессию и не затрагивает packet engine, и
отвечает на один вопрос для каждой цели: уязвимо ли это устройство? Результат, отличный от
VULNERABLE, сам по себе не является подтверждением безопасности.
# 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+, только стандартная библиотека — без сторонних пакетов.
Направьте инструмент на Gateway или AAA virtual server, а не на интерфейс управления. Подверженность
определяется для каждого virtual server, поэтому устройство с несколькими VIP требует проверки каждого из них. Проба несёт
жёсткие рамки безопасности — одна проверенная длина RelayState, без перебора — которые
описаны в разделе Безопасно ли это запускать?.
| Флаг | Описание |
|---|---|
TARGET | Одна или несколько целей [https://]HOST[:PORT]; схема по умолчанию https:// |
-f, --targets-file FILE | Читать цели из файла (по одной на строку; комментарии #) |
--timeout SECS | Тайм-аут на запрос (по умолчанию: 15) |
--workers N | Одновременных целей (по умолчанию: 16); вывод сохраняется в порядке ввода |
-b, --brief | Одна выровненная строка на цель — идеально для сканирования множества хостов |
--json | Вывод структурированного JSON, включая каждый отправленный запрос на цель |
--no-color | Отключить цветной вывод (также учитывает NO_COLOR и не-TTY) |
Уязвимое устройство (вывод по умолчанию в две строки). Маркер [!] и VULNERABLE отображаются
красным на 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
Исправленное устройство:
$ ./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)
Срабатывание защиты от ложных срабатываний. Проба и контроль той же длины оба вернули неисправленный сигнал, поэтому ответ не зависит от того, что было отправлено, и решающий на вид ответ отзывается:
$ ./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
Сканирование парка (--brief). Две строки gateway.example.com — это SP и IdP-only
virtual server на одном устройстве — оба отвечают, чего проверка предусловий конфигурации не смогла бы
обеспечить:
$ ./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
Машиночитаемый вывод (--json). Включён каждый запрос, поэтому находку можно вывести заново
из доказательств, а не доверять ей. Контроль записывается по его отношению к пробе, а
не как самостоятельный вердикт, потому что контроль, читающийся как исправленная сборка, — ожидаемый
результат на любой сборке:
$ ./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"
}
]
}
]
Да. Он предназначен для использования в продакшене и при оценке: