What this checks: whether an Apache Tomcat host runs a version affected by unauth JMX deserialization RCE (precondition: JmxRemoteLifecycleListener enabled). Read-only: it fingerprints Apache Tomcat and reads the version, no exploitation.
3. Detection — reads the Apache Tomcat version from the product's default response and version banner, compares to 6.0.48 / 7.0.73 / 8.0.39 / 8.5.8 / 9.0.0.M12 (per branch). Flags vulnerable on an affected build. No exploitation.
1. Target List — paste your Apache Tomcat URLs here, one per line (e.g. https://host.example.com).
Overview
This workflow finds Apache Tomcat and sorts each host by whether its version falls below the CVE-2016-8735 fixed floors. CVE-2016-8735 is remote code execution through JmxRemoteLifecycleListener deserialization when that listener is enabled. This check never opens JMX or RMI and never deserializes a payload. Give it hostnames, IP addresses, or URLs you are authorised to test. The workflow GETs /nonexistent-404-probe, fingerprints Tomcat from the default 404 page, parses Apache Tomcat/x.y.z, and compares that number to the per-branch floors in the graph. Every host comes back affected or not, so a fleet advisory becomes an evidenced list for exposure management. There is no JMX handshake and no confirm step.
Run it on a schedule when app servers turn over. A restored container image that still ships an unpatched 8.5 line is the reason the same fingerprint stays useful.
Pipeline
Read the target list. Hosts, URLs, or ranges, one per line, become the scope.
GET /nonexistent-404-probe on each host and fingerprint Tomcat from the 404 page body that embeds Apache Tomcat/x.y.z.
Parse the version and compare it to the CVE-2016-8735 per-branch floors in the graph.
Collect the per-host rows: product match, version, vulnerable flag, and any fetch error.
Emit the summary counts: hosts checked, product hits, vulnerable, and errors.
Inputs
Target scope. Hostnames, IP addresses, CIDR ranges, or URLs, one per line. Full URLs and host:port entries work too, since the workflow normalizes each into a bare host. Point it at scope you are authorised to test.
Outputs
results.jsonl. One row per host: URL, whether Tomcat was detected, the version read, the vulnerable flag, and detail text.
findings.jsonl. The same per-host verdict shaped for triage, with severity set from the vulnerable flag.
summary.json. Counts across the list: targets, product hits, vulnerable, and errors, plus the detection notes from the graph.
Integrations
HTTP. Unauthenticated GET of /nonexistent-404-probe only. No JMX or RMI connect and no deserialization payload.
Sample output
The records below are illustrative and do not come from a real run. They show one Tomcat host below a CVE-2016-8735 floor, one at or above its branch floor, and one host that is not the product.
Builds below the per-branch floors in the graph: 6.0.48, 7.0.73, 8.0.39, 8.5.8, and 9.0.0. A version picks the matching band, then compares to that band's floor. The compare uses the string the 404 page itself served.
Does an affected row mean JMX deserialization RCE worked?
No. The check only GETs /nonexistent-404-probe and reads Apache Tomcat/x.y.z. It does not open JMX or RMI, does not deserialize a payload, and does not confirm RCE.
Is this check safe on production?
Yes. It is a read-only unauthenticated GET that expects a 404. It does not touch the JMX listener.
Does the check need credentials?
No. It fingerprints the public HTTP error page the way an external scanner would.
What is CVE-2016-8735?
Remote code execution in Apache Tomcat through JmxRemoteLifecycleListener deserialization when that listener is enabled. This workflow maps hosts to that CVE by HTTP version exposure only.