What this checks: whether an Apache Tomcat host runs a version affected by unauth AJP arbitrary file read leading to RCE. 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 7.0.100 / 8.5.51 / 9.0.31 (per branch); 6.0.x and 8.0.x are EOL and never patched. 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-2020-1938 Ghostcat fixed floors. CVE-2020-1938 is an AJP connector flaw that can allow file read or remote code execution on misconfigured Tomcat. This check never opens AJP port 8009 and never reads a file. Give it hostnames, IP addresses, or URLs you are authorised to test. The workflow GETs /nonexistent-ghostcat-probe-404, 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 (7.0.100, 8.5.51, 9.0.31, with 6.0 and 8.0 treated as EOL stubs). Every host comes back affected or not, so a fleet advisory becomes an evidenced list for exposure management. There is no AJP 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-ghostcat-probe-404 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-2020-1938 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-ghostcat-probe-404 only. No AJP:8009 connect and no file-read probe.
Sample output
The records below are illustrative and do not come from a real run. They show one Tomcat host below a CVE-2020-1938 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: 7.0.100, 8.5.51, and 9.0.31. The 6.0 and 8.0 lines are EOL stubs in the same table. 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 Ghostcat AJP worked?
No. The check only GETs /nonexistent-ghostcat-probe-404 and reads Apache Tomcat/x.y.z. It does not connect to AJP:8009, does not read a file, 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 AJP connector.
Does the check need credentials?
No. It fingerprints the public HTTP error page the way an external scanner would.
What is CVE-2020-1938?
Ghostcat, an AJP connector vulnerability in Apache Tomcat that can allow file read or remote code execution. This workflow maps hosts to that CVE by HTTP version exposure only.