
Detection script for CVE-2026-11374
A detection script for the exploit precondition of CVE-2026-11374, a predictable SSO-ticket
flaw that leads to unauthenticated account takeover across the ManageEngine AD360 suite. The
four in-scope products share the ManageEngineADSFramework:
| Product | Affected build | Fixed build |
|---|---|---|
| ADSelfService Plus | ≤ 6528 | 6529 |
| RecoveryManager Plus | ≤ 6320 | 6321 |
| M365 Manager Plus | ≤ 4816 | 4817 |
| ADAudit Plus | ≤ 8702 | 8703 |
In affected builds the SSO ticket is just System.currentTimeMillis() (a predictable timestamp) sampled at the victim's
login, so it can be replayed through the CUSTOM_SSO_TICKET cookie to hijack that session. The
fix replaces the ticket with UUID.randomUUID() (a sufficiently random identifier). The replay path is only reachable when the
product is AD360-integrated: ADSFilter gates it on isProductIntegrated(). That gate is the
precondition this tool checks for.
NOTE: this detector confirms the precondition, not the vulnerability itself. A
POTENTIALLY_AFFECTEDresult is not a confirmed-vulnerable verdict — see below for more info.
Yes. It's built for production and assessment use.
1700000000000, a millisecond
value far enough in the past that it can never be cache-resident), so no session is ever
recovered. The server just tells us to clear the cookies we sent.GET, and the only cookies affected are the
throwaway ones the tool sends.CVE-2026-11374's patch changed only how the ticket is generated (milliseconds to UUID); it did not change the cookie-replay path this probe exercises. As a result, a patched install responds byte-for-byte identically to a vulnerable one for any unauthenticated request.
What the tool can do, unauthenticated and non-destructively:
What it can't do:
POTENTIALLY_AFFECTED result means the precondition is met and you should go verify the patch
level, not that the host is confirmed vulnerable.conf/product.conf locally, or actively
resolving a live ticket (real exploitation, intentionally not implemented here).?build= is sometimes the
real build and sometimes a frozen placeholder that reads below the fixed build, so a below-fixed value
is ambiguous and the tool marks it inconclusive. A value at or above the fixed build is still a
trustworthy patched signal, since the placeholder is always too low to reach it.requests library. Install with
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
Default ports differ per product (ADSelfService Plus 8888, ADAudit Plus 8081, M365 Manager Plus
8365, RecoveryManager Plus 8090), so pass a URL or host:port. A bare host defaults to 8888.
| Flag | Description |
|---|---|
targets | One or more host, host:port, or https://host:port |
-f, --targets-file FILE | Read targets from a file (one per line; # comments) |
--brief | Single aligned line per target, good for scanning many hosts |
--json | Emit structured JSON results |
--timeout SECS | Per-request timeout (default: 15) |
--no-build | Skip the extra build-number request on a positive finding |
--no-color | Disable coloured output (also honours NO_COLOR and non-TTY) |
An AD360-integrated console (verbose, the default). The second line spells out that this is the precondition and not a vulnerable verdict; the third is the best-effort build hint:
$ ./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)
A standalone install of the same product, where the replay path isn't active:
$ ./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.
Scanning a list with one aligned line per host (--brief). Exit status is 1 if any host is
POTENTIALLY_AFFECTED, otherwise 0, which is handy in scripts. The trailing note shows the
identified product, plus the build number on a finding:
$ ./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
Machine-readable output (--json). Each result carries the verdict, the state and detail
behind it, the identified product, and — on a finding — a build object: build is the number
found, fixed_build the threshold for that product, patch_hint the directional call
(likely_patched at or above the fixed build, otherwise inconclusive), and note a short
explanation:
$ ./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"
}
]