
Поведенческий детектор состояния патча для Citrix NetScaler CVE-2026-8452. Отправляет специально сформированные SAML-запросы, чтобы определить, присутствует ли проверка размера PrefixList, без эксплуатации или повреждения памяти.
A safe, non-destructive patch-state check for CVE-2026-8452, the pre-authentication heap overflow
in the Citrix NetScaler ADC / NetScaler Gateway SAML signature canonicalizer
(CTX696604,
CVSS 8.8). An oversized exclusive-canonicalization PrefixList overflows a fixed-size buffer during
canonicalization, which NetScaler performs before validating the signature that carries it — so the
whole path is reachable with no credentials, no session, and no valid signature. Reported by Michael
Tucker of the JPMorgan Chase XOR team; root-cause and exploitation analysis credit to
watchTowr Labs.
This script does not exploit the bug and does not corrupt memory. It answers one question per target: is the fix present on this appliance? — determined behaviourally, by observing the patch rather than guessing the build.
Yes. It is designed for production and assessment use:
PrefixList размером 512 байт и отклоняют 513 и более. Этот предел был определён с точностью до байта, одинаков на обеих поддерживаемых ветвях и не зависит от конфигурации устройства
или формы окружающего SAML-сообщения — что подтверждено проверкой обоих маршрутов, которые оборачивают значение в существенно разные объёмы XML, и обнаружением, что они меняют поведение на одном и том же
байте. 575 байт преодолевают предел с запасом в 63 байта, так что вердикт не зависит от того, как настроена цель.PrefixList. Раздувание других полей за его пределы — URL-адресов службы обработчика утверждений, имён издателя, идентификаторов алгоритмов, значений дайджеста и подписи — ничего не меняет на исправленной сборке, поэтому применение исправления не должно приводить к сбоям в рабочей конфигурации SAML.Если вы изменяете пробную нагрузку, не меняйте
PROBE_PREFIXESи не перебирайте длины. 575 байт — несущая конструкция. Другие длиныPrefixListмогут дестабилизировать устройство, по крайней мере в одном случае на сборке, содержащей это исправление, поэтому перебор длин — небезопасный способ исследования этой уязвимости, и меньшее значение не безопаснее.
Исправленные сборки чётко отклоняют слишком большой PrefixList с характерным сообщением. Неисправленные сборки
проходят через парсер и возвращают общую внутреннюю ошибку. Один идентичный запрос — два разных ответа:
575-байтовый PrefixList | Ответ |
|---|---|
| Не исправлено | 500 Internal Server Error 43549 |
| Исправлено | 200 Malformed Assertion sent to Netscaler |
Пробуются два маршрута, сначала IdP, с остановкой, как только один даёт ответ. Каждого из них достаточно по отдельности, а вместе они охватывают обе роли SAML:
| Маршрут | Запрос | Требует |
|---|---|---|
| 1 (первый) | POST /saml/login — подписанный AuthnRequest, PrefixList в ds:SignedInfo | политику SAML IdP, привязанную к целевому vserver |
| 2 (запасной) | POST /cgi/samlauth — SAMLResponse, PrefixList в подписи утверждения | службу обработчика утверждений (assertion consumer service) SAML SP на целевом vserver |
Маршрут IdP идёт первым, потому что он более устойчив из двух. Он нечувствителен к значению Issuer,
к AssertionConsumerServiceURL и к расхождению часов — IssueInstant, значительно выходящий за пределы
допуска устройства по расхождению времени, по-прежнему корректно различает сборки, поскольку канонизация предшествует и проверке времени, и проверке подписи.
AuthnRequestна маршруте 1 должен быть подписан. Неподписанный запрос возвращает200 Malformed Assertion sent to Netscalerна исправленных и неисправленных сборках, что побайтно идентично сигналу исправленной сборки, поэтому пробная нагрузка без блока подписи сообщает о каждом устройстве как об исправленном. Подпись не обязана быть действительной, и у этого инструмента она не действительна; она просто должна присутствовать, потому что еёSignedInfo— это то, что доставляетPrefixListв канонизатор.
Обе поддерживаемые ветви меняют поведение ровно на своей исправленной сборке, на обоих маршрутах:
| Сборка | Вердикт | |
|---|---|---|
13.1-63.16 | последняя уязвимая 13.1 | VULNERABLE |
13.1-63.18 | первая исправленная 13.1 | PATCHED |
14.1-66.59 | уязвимая 14.1 | VULNERABLE |
14.1-72.61 | первая исправленная 14.1 | PATCHED |
13.1-63.16 и 63.18 — последовательные выпуски, поэтому изменение относится к самому исправлению,
а не к дрейфу между промежуточными сборками.
Это сборки, в которых впервые появилось это исправление, и пробная нагрузка обнаруживает именно этот переход.
Они больше не являются сборками, до которых следует обновляться: более поздние бюллетени вытеснили их, поэтому 13.1-63.18 и
14.1-72.61 здесь обе отвечают PATCHED, оставаясь при этом подверженными более новым проблемам. См.
Remediation для актуальных исправленных сборок.
Потому что это не может работать с этой уязвимостью даже в принципе. 13.1-63.16 и 13.1-63.18, сборки
непосредственно по обе стороны от исправления, отдают побайтно идентичные tmindex.html, base.css и
resources.js — исправление не затрагивает ни одного веб-ресурса. Хэши статических ресурсов также совпадают между ветвями, поэтому
подход на основе хэшей может определить уязвимое устройство как исправленную сборку и сообщить о нём как о чистом,
что является наихудшим режимом отказа для инструмента обнаружения. Поэтому определение сборки по отпечатку намеренно
не реализовано. Состояние исправления определяется пробной нагрузкой или show ns version, если у вас есть
учётные данные.
./cve_2026_8452_check.py https://gateway.example.com
./cve_2026_8452_check.py https://gateway.example.com:9443
./cve_2026_8452_check.py -f targets.txt --brief