Ghostcat (CVE-2020-1938) is a flaw in Apache Tomcat’s AJP connector, normally on port 8009, that lets an unauthenticated attacker read files inside deployed web applications and, where uploaded files land inside the application, run code. Fix it by commenting out the AJP connector in server.xml, or binding it to localhost with a required secret, then upgrading Tomcat.
What the scanner is actually detecting
This is a remote, unauthenticated network check against the AJP port, not a version comparison. Tenable marks the plugin as “exploited by Nessus”, and its file name (ajp_lfi_ghostcat.nbin) points to an active file-read test over AJP. If the connector answers the crafted request, the finding is raised.
| Scanner | Finding title | ID |
|---|---|---|
| Nessus | Apache Tomcat AJP Connector Request Injection (Ghostcat) | Plugin 134862 (Critical, CVSS v3 9.8, family Web Servers), CVE-2020-1938 and CVE-2020-1745 |
The second CVE is the equivalent AJP file inclusion bug in Undertow (2.0.29.Final and earlier), the web server inside WildFly and JBoss EAP. Tenable’s solution text is to require authorization on the AJP connector and/or upgrade Tomcat to 7.0.100, 8.5.51, 9.0.31 or later.
How serious is it?
Serious when the port is reachable, but narrower than the “Critical” label suggests. Per the Apache Tomcat security page, Tomcat 7.0.0 to 7.0.99, 8.5.0 to 8.5.50 and 9.0.0.M1 to 9.0.30 shipped with an AJP connector enabled by default and listening on all IP addresses. Through it, an attacker can:
- return any file from inside a web application, including
WEB-INFandMETA-INF, which often exposesweb.xml, configuration files and database credentials; - make Tomcat process any file in the application as a JSP. If the application accepts uploads and stores them inside the web application, that becomes remote code execution.
It does not read arbitrary operating system files outside the web applications. Apache notes that mitigation is only required if an AJP port is accessible to untrusted users. In practice, “untrusted” includes any compromised workstation on a flat internal network. Public exploits exist, and CISA added CVE-2020-1938 to its Known Exploited Vulnerabilities catalog on 3 March 2022, so treat reachable instances as a priority.
How to confirm it on the host
Check from the network first, from the same segment as the scanner:
nmap -sV -p 8009 tomcat.example.com
nmap -p 8009 --script ajp-methods tomcat.example.com
An open port identified as ajp13 confirms an AJP listener. Then look on the server itself:
# Linux: listener, bound address and connected clients
sudo ss -ltnp | grep ':8009'
sudo ss -tn state established '( sport = :8009 )'
# Connector definition and Tomcat version
grep -n 'AJP' "$CATALINA_BASE/conf/server.xml" # /etc/tomcat9/server.xml (Debian), /etc/tomcat/server.xml (RHEL)
"$CATALINA_HOME/bin/version.sh"
# Windows (PowerShell)
Get-NetTCPConnection -LocalPort 8009 | Select-Object LocalAddress, State, RemoteAddress
$tc = 'C:Program FilesApache Software FoundationTomcat 9.0'
Select-String -Path "$tcconfserver.xml" -Pattern 'AJP'
& "$tcbinversion.bat"
A listener on 0.0.0.0, :: or a routable IP, with no secret attribute on the connector, is the vulnerable state. Next, find out whether anything actually uses AJP. Established connections to 8009 show you the client. On a front-end Apache httpd, search for mod_jk or mod_proxy_ajp:
grep -rEn 'ajp://|JkMount|workers.properties' /etc/httpd /etc/apache2 2>/dev/null
IIS servers using the Tomcat ISAPI redirector (isapi_redirect.dll) also speak AJP and have their own workers.properties. If none of these exist, the connector is almost certainly unused.
How to fix it
Back up server.xml before editing (cp -p server.xml server.xml.bak, or Copy-Item on Windows).
Option 1: AJP is not used (disable it)
Comment out the connector element in server.xml. Current Tomcat releases ship with it commented out exactly like this:
<!--
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
-->
XML comments cannot be nested, so if the element already contains a comment, delete the element instead. Then test and restart:
"$CATALINA_HOME/bin/catalina.sh" configtest
sudo systemctl restart tomcat9 # tomcat on RHEL, or shutdown.sh + startup.sh
# Windows: Restart-Service Tomcat9
Option 2: AJP is used by a front-end proxy (restrict and authenticate it)
Bind the connector to the address the proxy connects from (loopback when httpd runs on the same host) and require a shared secret. Generate an ASCII value, for example with openssl rand -hex 32:
<Connector protocol="AJP/1.3"
address="127.0.0.1"
port="8009"
redirectPort="8443"
secretRequired="true"
secret="REPLACE_WITH_RANDOM_HEX_VALUE" />
On patched releases, secretRequired already defaults to true and the AJP connector refuses to start without a secret. On releases older than February 2020 the attribute was called requiredSecret, and secretRequired does not exist; use requiredSecret="..." there as a stopgap until you upgrade.
Set the same value on the proxy. For mod_jk (workers.properties, also used by the ISAPI redirector):
worker.tomcat1.type=ajp13
worker.tomcat1.host=127.0.0.1
worker.tomcat1.port=8009
worker.tomcat1.secret=REPLACE_WITH_RANDOM_HEX_VALUE
For mod_proxy_ajp, the secret parameter requires Apache httpd 2.4.42 or later:
ProxyPass "/app" "ajp://127.0.0.1:8009/app" secret=REPLACE_WITH_RANDOM_HEX_VALUE
Restart Tomcat and then httpd (apachectl configtest first). If the proxy is on another host, bind address to Tomcat’s private interface and allow only the proxy’s IP at the firewall. AJP is clear text, so the secret is visible to anyone who can sniff that traffic; keep the link on a trusted segment.
Upgrade Tomcat
Upgrade to 9.0.31, 8.5.51 or 7.0.100 at minimum. Apache describes this as defence in depth: the fixed versions block the file-return and JSP-processing vector and reject AJP requests carrying unknown request attributes with a 403. Note that 7.0.x reached end of life on 31 March 2021 and 8.5.x on 31 March 2024, so move those servers to a supported branch rather than the minimum fixed build. An in-place upgrade keeps your existing server.xml, so the connector changes above are still needed. Debian and Red Hat packages backport fixes, so check the vendor advisory rather than the upstream version number.
Block port 8009 at the firewall
# Ubuntu/Debian with ufw
sudo ufw deny 8009/tcp
# RHEL family: make sure 8009 is not opened in the active zone
sudo firewall-cmd --list-ports
sudo firewall-cmd --permanent --remove-port=8009/tcp && sudo firewall-cmd --reload
# Windows
New-NetFirewallRule -DisplayName "Block Tomcat AJP 8009" -Direction Inbound -Protocol TCP -LocalPort 8009 -Action Block
How to verify the fix and rescan
- On the server,
ss -ltnp | grep 8009(orGet-NetTCPConnection -LocalPort 8009) should return nothing if you disabled AJP, or only127.0.0.1:8009if you restricted it. - From the scanner’s network,
nmap -p 8009 tomcat.example.comshould report the port closed or filtered. - Check
catalina.out,journalctl -u tomcat9or the Windows service logs for SEVERE errors, and confirm the proxied application still loads through httpd. - Rescan with Nessus. A policy limited to plugin 134862 against the affected hosts closes the finding quickly.
What can break and how to roll back
- The proxied application goes down (503 from httpd). You disabled a connector that mod_jk, mod_proxy_ajp or the ISAPI redirector was using. Restore the connector with Option 2 settings.
- AJP connector fails to start after an upgrade. Patched Tomcat requires a secret by default; the error appears in the startup log. Add the secret on both sides.
- Address mismatch. Current defaults bind to loopback, and the shipped example uses
::1. If httpd connects to127.0.0.1or another host, the connection fails. Setaddressexplicitly to what the proxy uses. - 403 responses. Custom request attributes forwarded by the proxy are now rejected. Allow them with
allowedRequestAttributesPatternon the connector. - Secret mismatch. Every request is rejected. Compare the values character by character.
To roll back, copy the backup over server.xml, revert the proxy configuration, and restart Tomcat and httpd.
Common false positive reasons
- Not Tomcat. WildFly, JBoss EAP and other products built on Undertow can expose AJP too (CVE-2020-1745). The finding is real; the fix follows that vendor’s guidance.
- Embedded Tomcat in a vendor product. Appliances and management consoles often bundle Tomcat. Raise it with the vendor or record a risk acceptance.
- Wrong instance. Several Tomcat instances on one host each have their own
CATALINA_BASEand AJP port. Match the flagged port to the file you edited. - No restart.
server.xmlchanges take effect only after Tomcat restarts. - Stale results. The report predates the change. Because this is an active test, a remaining hit usually means the port is still answering.
FAQ
Do I have to fix it if port 8009 is only reachable internally?
Yes, in most environments. Apache ties the risk to untrusted access to the port, and an internal attacker or compromised host counts. Disable or bind it to loopback.
Is upgrading Tomcat enough on its own?
It blocks the Ghostcat vector, but an in-place upgrade keeps your old server.xml. Disable unused AJP connectors and secure the rest anyway.
Is the AJP secret encrypted?
No. AJP is clear text, so the secret only prevents unauthorized clients; it does not protect traffic on an untrusted network.
Are Tomcat 10 and 11 affected?
CVE-2020-1938 is not listed on Apache’s Tomcat 10 security page. An AJP connector exposed without a secret is still worth fixing on any version.
Tracking this finding across many hosts
Ghostcat often turns up on every server built from the same Tomcat image or template. SITEY, a self-hosted vulnerability management platform, imports findings from 16 scanners (Nessus results by uploading a .nessus export) and merges duplicates within each scanner, so plugin 134862 hits can be followed host by host. Nessus findings can be retested individually, and AI-written, host-specific remediation scripts are deployed by its agents on Windows and Linux only after human approval, followed by a re-test to confirm closure.
Sources
- Tenable: Apache Tomcat AJP Connector Request Injection (Ghostcat), plugin 134862
- Apache Tomcat 9.x vulnerabilities (CVE-2020-1938, fixed in 9.0.31)
- Apache Tomcat 9: The AJP Connector
- Apache Tomcat Connectors: workers.properties reference (mod_jk secret)
- Apache HTTP Server 2.4: mod_proxy ProxyPass parameters (secret)