
CVE-2026-11374 탐지 스크립트
CVE-2026-11374의 익스플로잇 선행 조건을 탐지하는 스크립트입니다. 이 취약점은 예측 가능한 SSO 티켓 결함으로, ManageEngine AD360 제품군 전반에서 비인증 계정 탈취로 이어집니다. 범위 내 4개 제품은 공통으로 ManageEngineADSFramework를 사용합니다:
| 제품 | 영향을 받는 빌드 | 수정 빌드 |
|---|---|---|
| ADSelfService Plus | ≤ 6528 | 6529 |
| RecoveryManager Plus | ≤ 6320 | 6321 |
| M365 Manager Plus | ≤ 4816 | 4817 |
| ADAudit Plus | ≤ 8702 | 8703 |
영향을 받는 빌드에서 SSO 티켓은 피해자 로그인 시점에 샘플링된 System.currentTimeMillis()(예측 가능한 타임스탬프)일 뿐이므로, CUSTOM_SSO_TICKET 쿠키를 통해 재생하여 해당 세션을 탈취할 수 있습니다. 이 수정은 티켓을 UUID.randomUUID()(충분히 무작위한 식별자)로 대체합니다. 재생 경로는 제품이 AD360과 통합된 경우에만 도달할 수 있습니다. ADSFilter가 isProductIntegrated()를 기준으로 이를 통제합니다. 이 통제 조건이 바로 이 도구가 확인하는 선행 조건입니다.
참고: 이 탐지기는 취약점 자체가 아니라 선행 조건을 확인합니다.
POTENTIALLY_AFFECTED결과는 취약함이 확인된 판정이 아닙니다. 자세한 내용은 아래를 참조하세요.
예. 운영 환경 및 평가 용도로 제작되었습니다.
1700000000000, 캐시에 상주할 수 없을 만큼 충분히 과거의 밀리초 값)을 전송하므로 어떤 세션도 복구되지 않습니다. 서버는 우리가 보낸 쿠키를 삭제하라고 알려줄 뿐입니다.GET이며, 영향을 받는 유일한 쿠키는 도구가 보내는 일회성 쿠키뿐입니다.CVE-2026-11374의 패치는 티켓 생성 방식(밀리초 → UUID)만 변경했으며, 이 프로브가 사용하는 쿠키 재생 경로는 변경하지 않았습니다. 그 결과, 패치된 설치본은 인증되지 않은 요청에 대해 취약한 설치본과 바이트 단위로 동일하게 응답합니다.
이 도구가 비인증·비파괴적으로 할 수 있는 작업:
할 수 없는 작업:
POTENTIALLY_AFFECTED 결과는 선행 조건이 충족되었음을 의미하며, 호스트가 취약함이 확인되었다는 뜻이 아니라 패치 수준을 검증해야 한다는 뜻입니다.conf/product.conf 읽기, 또는 활성 티켓을 능동적으로 복원(실제 익스플로잇이며 의도적으로 구현하지 않음)이 필요합니다.?build=는 실제 빌드인 경우도 있고 수정 빌드보다 낮게 나오는 고정된 자리 표시자인 경우도 있어서, 수정 빌드보다 낮은 값은 모호하며 도구는 이를 불확정으로 표시합니다. 수정 빌드 이상의 값은 여전히 신뢰할 만한 패치 신호입니다. 자리 표시자는 항상 너무 낮아 그 값에 도달할 수 없기 때문입니다.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이 적용됩니다.
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). 호스트 중 하나라도 POTENTIALLY_AFFECTED이면 종료 상태는 1, 그렇지 않으면 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"
}
]
| 코드 | 의미 |
|---|---|
0 | 어떤 대상도 POTENTIALLY_AFFECTED를 반환하지 않음 |
1 | 하나 이상의 대상이 POTENTIALLY_AFFECTED |
2 | 사용법 오류(잘못된 인수 / 읽을 수 없는 대상 파일) |
유효하지 않은 티켓과 앱 태그를 임의의 CustomSSO URL(*.do)에 전송합니다:
GET /AppsHome.do
Cookie: CUSTOM_SSO_TICKET=1700000000000; CUSTOM_SSO_APP_TAG_NAME=AD360
AD360 통합 설치본에서는 ADSFilter → CustomSSOFilter → CookieSSOImpl 체인이 해석 분기를 실행하고(태그가 다른 제품을 가리키므로 appName != prodName), 가짜 티켓 해석에 실패하여 쿠키 정리 응답 헤더를 출력합니다:
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; ...
독립형 설치본은 해당 경로를 실행하지 않으므로 정리 동작이 없습니다. 제품은 모든 응답에 설정되는 제품별 고정 Tomcat 세션 쿠키(ADSelfService Plus의 경우 JSESSIONIDADSSP 등)로 식별됩니다. 통합된 ADSSP는 302를 반환하는 반면 ADAudit, M365, RMP는 동일한 프로브에 200을 반환하므로, 검사는 상태 코드가 아닌 헤더를 기준으로 합니다.
POTENTIALLY_AFFECTED은 "취약함이 확인됨"이 아닙니다. 이는 익스플로잇 선행 조건입니다. 패치가 이 코드 경로를 건드리지 않았으므로 패치된 빌드도 동일하게 응답하며, 따라서 패치 수준을 별도로 확인해야 합니다(다음 단계 참조). 이것이 비인증 관찰자가 알 수 있는 한계입니다.HTTP 400은 실제 응답이 아니라 속도 제한 잠금일 수 있습니다. Zoho IAM은 소스 IP를 60초당 약 40회 요청으로 제한한 뒤 해당 경로를 약 60초간 잠그는 URL 롤링 스로틀을 적용합니다. 잠금 중에는 요청이 HTTP 400을 반환하고 통합 신호가 가려집니다. 도구는 이를 INCONCLUSIVE로 보고합니다. 해당 IP에서 약 1분 후 재시도하세요.UNAFFECTED로 오판될 수 있습니다. 해당 위치에 ManageEngine 제품이 예상된다면 직접 확인하세요.?v=)과 M365(?bN=)는 항상 실제 빌드를 노출합니다. ADSSP의 ?build=는 실제인 경우도 있고 수정 빌드보다 낮게 나오는 고정된 자리 표시자인 경우도 있어 수정 빌드보다 낮은 ADSSP 값은 모호합니다. RMP는 아무것도 노출하지 않습니다. 모든 경우에 수정 빌드 이상의 빌드는 패치되었음을 강하게 시사하며, 그보다 낮으면 불확정입니다.대상이 POTENTIALLY_AFFECTED로 나오면 노출된 것으로 간주하고 확인할 가치가 있다고 판단한 후 다음을 수행하세요:
패치 수준을 직접 확인하세요(도구는 불가). 호스트에서 conf/product.conf(build_number)를 읽어 해당 제품의 수정 빌드(ADSelfService Plus 6529, RecoveryManager Plus 6321, M365 Manager Plus 4817, ADAudit Plus 8703)와 비교하세요. 방향성 있게 해석하세요: 수정 빌드 이상의 빌드는 UUID 수정이 해당 트리에 포함되어 있으므로 호스트가 패치되었음을 강하게 시사합니다. 수정 빌드보다 낮은 빌드는 취약함의 증거라기보다 불확정입니다. 대역 외 핫픽스와 백포트가 빌드 번호를 올리지 않고 UUID 수정을 적용할 수 있기 때문입니다. 이 경우 아래의 티켓 형식 확인으로 대체하세요.
로그인할 수 있다면 발급된 티켓 형식을 확인하세요. CUSTOM_SSO_TICKET 쿠키는 HttpOnly가 아니므로 인증 후 해당 값을 읽을 수 있습니다: 13자리 숫자면 취약, UUID면 패치됨. 이것이 가장 명확한 긍정 확인 방법입니다.
취약하거나 패치 여부를 확인할 수 없는 모든 호스트를 수정 빌드(또는 그 이상)로 업데이트하세요. 수정은 예측 가능한 티켓을 무작위 UUID로 대체합니다.
익스플로잇 시도를 찾아보세요. 호스트에서 serverOut_<date>.txt는 스로틀 발생을 다음과 같은 버스트 형태로 기록합니다:
IAMSecurityException ErrorCode: URL_ROLLING_THROTTLES_LIMIT_EXCEEDED,
RequestURI: "/showLogin.cc", RemoteAddr: <source IP>
단일 소스 IP에서 SSO/로그인 디스패치 경로에 URL_ROLLING_THROTTLES_LIMIT_EXCEEDED가 급증하는 것은 티켓 무차별 대입의 강력한 지표입니다. 또한 최근 성공한 로그인 및 세션 활동을 검토하여 탈취 징후가 있는지 확인하세요.
그동안 노출을 줄이세요. AD360 제품군에 대한 네트워크 접근을 제한하고(인터넷에 노출되어서는 안 됨), 재생 경로가 필요 없다면 CustomSSO 통합을 비활성화하여 선행 조건 자체를 제거하세요.
영향을 받는 제품을 최소 수정 빌드(ADSelfService Plus 6529, RecoveryManager Plus 6321, M365 Manager Plus 4817, ADAudit Plus 8703) 이상으로 업데이트하세요. 수정은 예측 가능한 System.currentTimeMillis() SSO 티켓을 UUID.randomUUID()로 대체합니다.
이 코드는 MIT 라이선스에 따라 배포됩니다.
사전 상호 동의 없이 대상을 공격하기 위해 이 도구를 사용하는 것은 불법입니다. 모든 해당 지역, 주, 연방 법률을 준수할 책임은 최종 사용자에게 있습니다. 개발자는 이 프로그램으로 인해 발생하는 오용이나 피해에 대해 책임을 지지 않으며, 어떠한 손해 배상 책임도 없습니다.
| 플래그 | 설명 |
|---|
targets | 하나 이상의 host, host:port, 또는 https://host:port |
-f, --targets-file FILE | 파일에서 대상 읽기(줄당 하나, #은 주석) |
--brief | 대상별 정렬된 한 줄 출력, 많은 호스트를 스캔할 때 유용 |
--json | 구조화된 JSON 결과 출력 |
--timeout SECS | 요청별 타임아웃(기본값: 15) |
--no-build | 긍정 결과에서 추가 빌드 번호 요청 생략 |
--no-color | 색상 출력 비활성화(NO_COLOR 및 비-TTY 환경도 인식) |
| 판정 | 의미 |
|---|
POTENTIALLY_AFFECTED | AD360과 통합된 범위 내 제품. CustomSSO 쿠키 재생 경로가 활성화되어 CVE-2026-11374 익스플로잇 선행 조건이 충족됨. 취약함이 확인된 판정이 아니므로 패치 수준을 검증하세요(다음 단계 참조). |
UNAFFECTED | 재생 경로에 도달할 수 없음. 다음 중 하나: 범위 내 제품의 독립형 설치본(AD360 미통합 — 나중에 통합되면 도달 가능해지므로 빌드는 어쨌든 검증할 것), 범위 내 4개 제품이 아닌 ManageEngine ADS 프레임워크 제품, 또는 응답에 범위 내 제품이 전혀 없는 경우(CVE-2026-11374 대상 4개 제품이 아니거나 세션 쿠키를 제거하는 프록시). |
INCONCLUSIVE | HTTP 400 — 신호를 가리는 IAM 속도 제한 잠금일 가능성이 높음. 약 60초 후 재시도. |
ERROR | 연결/타임아웃/TLS 실패. |
| 프로브 응답 | 판정 |
|---|
| 정리 헤더 및 알려진 범위 내 제품 쿠키 | POTENTIALLY_AFFECTED |
| 제품 쿠키, 정리 없음, 상태 ≠ 400 | UNAFFECTED (독립형 / 미통합) |
| 범위 내 제품 쿠키 없음, 상태 ≠ 400 | UNAFFECTED (4개 제품 중 하나가 아님) |
| 정리 헤더가 있지만 인식되지 않는 제품 쿠키 | UNAFFECTED (ManageEngine이지만 범위 밖) |
HTTP 400 | INCONCLUSIVE (IAM 속도 제한 잠금일 가능성 높음. 제한 사항 참조) |