
Скрипт обнаружения для CVE-2026-11374
Скрипт обнаружения предусловия эксплуатации CVE-2026-11374 — предсказуемой уязвимости SSO-билетов, которая приводит к неаутентифицированному захвату учётных записей во всём комплексе ManageEngine AD360. Четыре продукта, попадающие в область действия, используют общий ManageEngineADSFramework:
| Продукт | Затронутая сборка | Исправленная сборка |
|---|
| ADSelfService Plus | ≤ 6528 | 6529 |
| RecoveryManager Plus | ≤ 6320 | 6321 |
| M365 Manager Plus | ≤ 4816 | 4817 |
| ADAudit Plus | ≤ 8702 | 8703 |
В затронутых сборках SSO-билет — это просто System.currentTimeMillis() (предсказуемая метка времени), снятая при входе жертвы, поэтому его можно воспроизвести через cookie CUSTOM_SSO_TICKET, чтобы перехватить эту сессию. Исправление заменяет билет на UUID.randomUUID() (достаточно случайный идентификатор). Путь воспроизведения доступен только когда продукт интегрирован с AD360: ADSFilter допускает его при условии isProductIntegrated(). Это условие и есть предусловие, которое проверяет данный инструмент.
ПРИМЕЧАНИЕ: этот детектор подтверждает предусловие, а не саму уязвимость. Результат
POTENTIALLY_AFFECTED— это не вердикт «подтверждённо уязвим»; подробнее см. ниже.
Да. Он предназначен для использования в производственной среде и при оценке безопасности.
1700000000000 — значение в миллисекундах, достаточно старое, чтобы никогда не оказаться в кэше), поэтому ни одна сессия не восстанавливается. Сервер лишь просит очистить отправленные нами cookie.GET, и единственные затронутые cookie — одноразовые, которые отправляет инструмент.Патч CVE-2026-11374 изменил только способ генерации билета (с миллисекунд на UUID); он не менял путь воспроизведения cookie, который проверяет этот зонд. В результате исправленная установка отвечает побайтово идентично уязвимой на любой неаутентифицированный запрос.
Что инструмент может сделать без аутентификации и без разрушающих воздействий:
Чего он не может:
POTENTIALLY_AFFECTED означает, что предусловие выполнено, и вам следует проверить уровень патча, а не то, что хост подтверждённо уязвим.conf/product.conf либо активного разрешения действующего билета (реальная эксплуатация, намеренно не реализована здесь).?build= у ADSSP иногда показывает реальную сборку, а иногда — замороженное значение-заглушку, которое оказывается ниже исправленной сборки, поэтому значение ниже исправленной неоднозначно, и инструмент помечает его как неопределённое. Значение на уровне исправленной сборки или выше по-прежнему является надёжным признаком установки патча, поскольку заглушка всегда слишком низкая, чтобы достичь его.requests. Установка: pip install requests.# single host (prefer a URL or host:port; ports differ per product)
./cve_2026_11374_check.py https://adssp.example.com:8888
# multiple hosts (scheme optional: https is tried first, then http)
./cve_2026_11374_check.py host-a:8081 host-b:8365
# scan a list, one target per line ('#' comments allowed), compact output
./cve_2026_11374_check.py -f targets.txt --brief
# machine-readable output for pipelines
./cve_2026_11374_check.py -f targets.txt --json > results.json
Порты по умолчанию различаются для каждого продукта (ADSelfService Plus 8888, ADAudit Plus 8081, M365 Manager Plus 8365, RecoveryManager Plus 8090), поэтому передавайте URL или host:port. Для адреса без порта по умолчанию используется 8888.
| Флаг | Описание |
|---|---|
targets | Один или несколько адресов host, host:port или https://host:port |
-f, --targets-file FILE | Читать цели из файла (по одной на строку; комментарии #) |
--brief | Одна выровненная строка на цель, удобно при сканировании многих хостов |
--json | Выдавать структурированные результаты в JSON |
--timeout SECS | Таймаут на запрос (по умолчанию: 15) |
--no-build | Пропускать дополнительный запрос номера сборки при положительном результате |
--no-color | Отключить цветной вывод (также учитывает NO_COLOR и не-TTY) |
Интегрированная с AD360 консоль (подробный режим, используется по умолчанию). Во второй строке поясняется, что это предусловие, а не вердикт об уязвимости; третья — полученная по возможности подсказка о сборке:
$ ./cve_2026_11374_check.py https://adssp.example.com:8888
[!] https://adssp.example.com:8888: POTENTIALLY_AFFECTED
ADSelfService Plus: AD360-integrated, CustomSSO replay path active - precondition met. Not confirmed vulnerable; verify patch level (fixed build 6529).
build: 6519 (below fixed 6529 - inconclusive: ?build= may be a stale placeholder or a hotfix)
Автономная установка того же продукта, где путь воспроизведения неактивен:
$ ./cve_2026_11374_check.py https://adssp.example.com:8888
[+] https://adssp.example.com:8888: UNAFFECTED
ADSelfService Plus: standalone / not AD360-integrated (no cleanup), so the replay path isn't reachable here. Verify build >= 6529 regardless.
Сканирование списка с одной выровненной строкой на хост (--brief). Код возврата равен 1, если хотя бы один хост — POTENTIALLY_AFFECTED, иначе 0; это удобно в скриптах. Завершающая пометка показывает определённый продукт, а при находке — номер сборки:
$ ./cve_2026_11374_check.py -f targets.txt --brief; echo "exit: $?"
POTENTIALLY_AFFECTED https://host-a:8888 ADSelfService Plus 6519
POTENTIALLY_AFFECTED http://host-b:8081 ADAudit Plus 8530
UNAFFECTED http://host-c:8365 M365 Manager Plus
UNAFFECTED https://host-d:443
INCONCLUSIVE http://host-e:8888 ADSelfService Plus
ERROR host-f:8888 timeout
exit: 1
Машиночитаемый вывод (--json). Каждый результат содержит verdict, а также state и detail, лежащие в его основе, определённый product и — при находке — объект build: build — найденный номер, fixed_build — порог для этого продукта, patch_hint — направленная оценка (likely_patched на уровне исправленной сборки или выше, иначе inconclusive), а note — краткое пояснение:
$ ./cve_2026_11374_check.py https://host-b:8081 --json
[
{
"target": "https://host-b:8081",
"state": "potentially_affected",
"detail": "ADAudit Plus: AD360-integrated, CustomSSO replay path active - precondition met ...",
"product": "ADAudit Plus",
"build": { "build": "8530", "fixed_build": "8703", "patch_hint": "inconclusive", "note": "below fixed 8703 - inconclusive: may be a hotfix" },
"verdict": "POTENTIALLY_AFFECTED"
}
]
| Вердикт | Значение |
|---|---|
POTENTIALLY_AFFECTED | Продукт из области действия интегрирован с AD360; путь воспроизведения cookie CustomSSO активен, поэтому предусловие эксплуатации CVE-2026-11374 выполнено. Это не вердикт «подтверждённо уязвим»; проверьте уровень патча (см. дальнейшие шаги). |
UNAFFECTED | Путь воспроизведения недоступен. Варианты: автономный продукт из области действия (не интегрирован с AD360 — хотя он станет доступен при последующей интеграции, поэтому всё равно проверьте сборку); продукт ManageEngine на базе ADS-framework, не входящий в число четырёх; или в ответе вообще нет продукта из области действия (не один из четырёх продуктов CVE-2026-11374, либо прокси, удаляющий cookie сессии). |
INCONCLUSIVE | HTTP 400 — вероятно, блокировка из-за ограничения частоты запросов IAM маскирует сигнал; повторите попытку примерно через 60 с. |
ERROR | Ошибка соединения/таймаута/TLS. |
| Код | Значение |
|---|---|
0 | Ни одна цель не вернула POTENTIALLY_AFFECTED |
1 | Хотя бы одна цель получила POTENTIALLY_AFFECTED |
2 | Ошибка использования (неверные аргументы / нечитаемый файл целей) |
Отправьте недействительный билет и метку приложения на любой URL CustomSSO (*.do):
GET /AppsHome.do
Cookie: CUSTOM_SSO_TICKET=1700000000000; CUSTOM_SSO_APP_TAG_NAME=AD360
На установке, интегрированной с AD360, цепочка ADSFilter → CustomSSOFilter → CookieSSOImpl выполняет ветвь разрешения (метка указывает на другой продукт, поэтому appName != prodName), не может разрешить поддельный билет и выдаёт заголовки ответа с очисткой cookie:
Set-Cookie: CUSTOM_SSO_TICKET=removed; Max-Age=0; ...
Set-Cookie: CUSTOM_SSO_APP_NAME=removed; Max-Age=0; ...
Set-Cookie: CUSTOM_SSO_APP_TAG_NAME=removed; Max-Age=0; ...
Автономная установка никогда не выполняет этот путь и не выдаёт очистку cookie. Продукт определяется по фиксированной для каждого продукта cookie сессии Tomcat (JSESSIONIDADSSP для ADSelfService Plus и т. д.), которая устанавливается в каждом ответе. Проверка опирается на заголовки, а не на код состояния, поскольку интегрированный ADSSP возвращает 302, а ADAudit, M365 и RMP на тот же зонд отвечают 200.
| Ответ на зонд | Вердикт |
|---|---|
| заголовки очистки и известная cookie продукта из области действия | POTENTIALLY_AFFECTED |
| cookie продукта, без очистки, статус ≠ 400 | UNAFFECTED (автономный / не интегрирован) |
| нет cookie продукта из области действия, статус ≠ 400 | UNAFFECTED (не один из четырёх продуктов) |
| заголовки очистки, но неизвестная cookie продукта | UNAFFECTED (ManageEngine, вне области действия) |
HTTP 400 | INCONCLUSIVE (вероятно, блокировка из-за ограничения частоты IAM; см. Ограничения) |
POTENTIALLY_AFFECTED — это не «подтверждённо уязвим». Это предусловие эксплуатации. Поскольку патч не затрагивал этот код, исправленная сборка отвечает идентично, поэтому уровень патча нужно подтверждать вне полосы (см. дальнейшие шаги). Это верхняя граница того, что может знать неаутентифицированный наблюдатель.HTTP 400 может быть блокировкой из-за ограничения частоты запросов, а не реальным ответом. Zoho IAM применяет скользящее ограничение по URL, которое ограничивает исходный IP примерно 40 запросами за 60 с, а затем блокирует этот путь на ~60 с. Во время блокировки запросы возвращают HTTP 400, и сигнал интеграции маскируется. Инструмент сообщает об этом как INCONCLUSIVE; повторите попытку с этого IP примерно через минуту.UNAFFECTED. Если вы ожидаете там продукт ManageEngine, подтвердите напрямую.?v=) и M365 (?bN=) всегда показывают реальную сборку; ?build= у ADSSP иногда реальный, а иногда замороженная заглушка, которая показывает значение ниже исправленной сборки, поэтому значение ADSSP ниже исправленной неоднозначно; RMP не раскрывает никакой. В любом случае сборка на уровне исправленной или выше с большой вероятностью означает установку патча, а ниже — неопределённость.Если цель вернула POTENTIALLY_AFFECTED, считайте её открытой и требующей подтверждения. Далее:
Подтвердите уровень патча напрямую (инструмент не может). Прочитайте conf/product.conf (build_number) на хосте и сравните с исправленной сборкой для этого продукта (ADSelfService Plus 6529, RecoveryManager Plus 6321, M365 Manager Plus 4817, ADAudit Plus 8703). Интерпретируйте направленно: сборка на уровне исправленной или выше — сильный признак того, что хост запропатчен, поскольку исправление с UUID находится в этом дереве. Сборка ниже исправленной неопределённа, а не доказательство уязвимости, потому что внешние горячие исправления и бэкпорты могут применить UUID-фикс без увеличения номера сборки; в этом случае прибегните к проверке формата билета ниже.
Проверьте формат выданного билета, если можете войти. Cookie CUSTOM_SSO_TICKET не является HttpOnly, поэтому после аутентификации вы можете прочитать её значение: 13-значное число означает уязвимость, UUID — патч. Это самая чёткая положительная проверка.
Обновите до исправленной сборки (или более поздней) на любом хосте, который уязвим или который вы не можете подтвердить как запропатченный. Исправление заменяет предсказуемый билет на случайный UUID.
Ищите попытки эксплуатации. На хосте serverOut_<date>.txt фиксирует троттлинг как всплески:
IAMSecurityException ErrorCode: URL_ROLLING_THROTTLES_LIMIT_EXCEEDED,
RequestURI: "/showLogin.cc", RemoteAddr: <source IP>
Всплеск URL_ROLLING_THROTTLES_LIMIT_EXCEEDED на путях диспетчеризации SSO/входа с одного исходного IP — сильный признак перебора билетов. Также просмотрите недавние успешные входы и активность сессий на предмет признаков захвата.
Тем временем снизьте подверженность риску. Ограничьте сетевой доступ к комплексу AD360 (он не должен быть доступен из интернета), а если путь воспроизведения не нужен, отключение интеграции CustomSSO полностью устраняет предусловие.
Обновите затронутый продукт как минимум до исправленной сборки (ADSelfService Plus 6529, RecoveryManager Plus 6321, M365 Manager Plus 4817, ADAudit Plus 8703) или более поздней. Исправление заменяет предсказуемый SSO-билет System.currentTimeMillis() на UUID.randomUUID().
Этот код распространяется по лицензии MIT.
Использование этого инструмента для атак на цели без предварительного взаимного согласия незаконно. Ответственность за соблюдение всех применимых местных, государственных и федеральных законов лежит на конечном пользователе. Разработчики не несут ответственности и не отвечают за любое неправомерное использование или ущерб, причинённый этой программой.