Back to Articles

Building a Ghostcat Detector: How fatal0's Go Tool Probes the CVE-2020-1938 Vulnerability

[ View on GitHub ]

Building a Ghostcat Detector: How fatal0's Go Tool Probes the CVE-2020-1938 Vulnerability

Hook

In February 2020, security researchers discovered that millions of Tomcat servers were exposing their entire web application filesystem to anyone who knew how to speak AJP—a binary protocol most developers have never heard of.

Context

CVE-2020-1938, nicknamed "Ghostcat," represented one of those rare vulnerability classes that fundamentally breaks security assumptions. For nearly 13 years, every version of Apache Tomcat shipped with the AJP connector enabled by default on port 8009, listening for requests from Apache HTTP Server via mod_jk. The problem? The AJP protocol implementation didn't properly validate file path attributes, allowing attackers to inject arbitrary path values and read any file from the web application, including WEB-INF/web.xml with database credentials, application.properties files, and even source code in certain configurations.

The vulnerability was particularly insidious because AJP ports were often left exposed on internal networks under the assumption that internal traffic was trusted. Worse, many security teams didn't even know AJP was running—it was just there, silently accepting connections on 8009. When the vulnerability went public, organizations needed rapid detection capabilities. Traditional vulnerability scanners required signature updates and full-system scans. What was needed was a lightweight, purpose-built tool that could be pointed at suspected Tomcat instances and answer one question: "Is this server vulnerable to Ghostcat?" That's the gap fatal0/tomcat-cve-2020-1938-check attempted to fill.

Technical Insight

Attack Vector

Target Host

Build Request

Magic 0x1234 + Payload

javax.servlet.include attributes

WEB-INF/web.xml

Validate Vulnerability

CVE-2020-1938 Scanner

AJP13 Protocol Client

AJP Packet Builder

Tomcat Server:8009

Path Traversal Exploit

File Content Response

System architecture — auto-generated

The elegance of this Go-based scanner lies in its direct implementation of the AJP13 protocol. Rather than relying on HTTP endpoints or version string parsing, it speaks the same binary protocol that mod_jk uses to communicate with Tomcat. AJP13 is a packet-oriented protocol where each message starts with a magic number (0x1234 for requests from the client), followed by a data length, and then the payload containing HTTP request metadata.

The attack vector exploits the javax.servlet.include.request_uri and javax.servlet.include.path_info attributes in AJP forward requests. In vulnerable Tomcat versions, these attributes could override the actual request path, bypassing security constraints defined in web.xml. Here's a simplified representation of what the exploit packet structure looks like:

type AJPForwardRequest struct {
    Prefix       uint16  // 0x1234 (magic number)
    Length       uint16  // packet length
    Code         byte    // 0x02 (forward request)
    Method       byte    // 0x02 (GET)
    Protocol     string  // "HTTP/1.1"
    ReqURI       string  // "/"
    RemoteAddr   string  // "127.0.0.1"
    ServerName   string  // target server
    ServerPort   uint16  // 8009
    IsSSL        bool    // false
    NumHeaders   uint16  // number of headers
    Headers      map[string]string
    Attributes   []AJPAttribute // The attack vector
}

type AJPAttribute struct {
    Code  byte    // 0x0A for req_attribute
    Name  string  // "javax.servlet.include.request_uri"
    Value string  // "/WEB-INF/web.xml" - the file we want
}

The scanner constructs an AJP packet with malicious attributes pointing to sensitive files like /WEB-INF/web.xml or /WEB-INF/classes/application.properties. When sent to a vulnerable Tomcat instance, the server processes these attributes, reads the requested file, and returns its contents in the AJP response—completely bypassing normal servlet security constraints.

The tool's implementation handles the full AJP response parsing, which requires careful attention to packet framing. AJP responses come back with a different magic number (0x4142, ASCII "AB"), followed by body chunks that must be assembled. Each chunk is prefixed with its length, and the scanner must continue reading until it encounters an end-of-response marker.

What makes this approach superior to simple port scanning or banner grabbing is that it provides definitive proof of vulnerability. You're not inferring vulnerability from a version string that might have been patched—you're actually attempting the file read and verifying whether restricted content is returned. This eliminates false positives from version detection and catches scenarios where administrators think they've patched but haven't, or where custom builds include the vulnerability despite version numbers suggesting otherwise.

The Go implementation provides practical advantages for security teams. The compiled binary is statically linked with no runtime dependencies, making it trivial to deploy to jump boxes, bastion hosts, or include in security scanning pipelines. Go's networking stack handles the TCP connection lifecycle cleanly, and the language's byte-slice manipulation capabilities make binary protocol implementation more pleasant than in higher-level languages. The tool can be invoked in shell scripts, integrated into CI/CD security gates, or wrapped in orchestration tools for scanning entire network ranges.

Gotcha

The primary limitation is temporal relevance. CVE-2020-1938 was disclosed over four years ago, and any reasonably maintained Tomcat installation should have been patched by now—either through vendor updates or by disabling the AJP connector entirely. Running this scanner in 2024 is more of an archaeology expedition than active threat hunting. If you're still finding vulnerable instances, you have much bigger problems than detection tooling.

The repository's abandonment is another red flag. With only 7 stars, no documentation beyond what appears to be a screenshot, no test suite, and no apparent maintenance since creation, you're essentially trusting unvetted code to accurately implement a security-critical protocol. The AJP protocol has subtle edge cases—incorrect packet framing, improper attribute encoding, or botched response parsing could lead to false negatives where vulnerable servers appear safe. Without test coverage or community vetting, you can't have confidence in the results. For security tooling, this is unacceptable. You need provenance, testing, and ideally security researchers vouching for correctness. Modern alternatives like Nuclei templates have thousands of eyes on them and extensive validation. Additionally, running this tool requires direct network access to port 8009, which is often firewalled off from external networks in properly segmented environments, limiting its usefulness to internal network assessments.

Verdict

Use if: You're conducting internal network security audits on legacy environments where Tomcat instances might have been forgotten, you need a standalone binary with zero dependencies for air-gapped network scanning, or you're studying AJP protocol implementation for educational purposes and want a minimal Go example to dissect. It's also reasonable if you're specifically hunting for this CVE as part of red team exercises where you need quick, targeted checks. Skip if: You need production-grade security scanning with verifiable accuracy and maintenance, you're working with modern infrastructure where this vulnerability should already be patched, you require comprehensive Tomcat security assessment beyond this single CVE, or you need documented, tested tooling with community support. Instead, use Nuclei with updated CVE-2020-1938 templates, Metasploit's auxiliary scanner module, or better yet, deploy comprehensive vulnerability management solutions like Nessus or Qualys that include this check alongside thousands of others. For most organizations in 2024, this specific tool is a historical curiosity rather than a practical security utility.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/fatal0-tomcat-cve-2020-1938-check.svg)](https://starlog.is/api/badge-click/developer-tools/fatal0-tomcat-cve-2020-1938-check)