Skip to content

AD-ADCS-010: ESC8: web enrollment accepts NTLM over HTTP

high   Certificate services   MITRE ATT&CK T1649, T1557.001   Reads: HTTP probe, LDAP

AD CS web enrollment (/certsrv/) accepts NTLM or Negotiate over plain HTTP, so relayed NTLM authentication from a DC yields a certificate for that DC.

Also known as: esc8

Why it matters

The Certification Authority Web Enrollment role serves /certsrv/ from IIS. Over plain HTTP, NTLM authentication has no channel to bind to, so an attacker can relay it:

  1. Coerce a domain controller into authenticating to the attacker's host, for example with the Print Spooler (PrinterBug) or EFSRPC (PetitPotam).
  2. Relay the DC's NTLM authentication to http://<ca>/certsrv/ and request a certificate from the DomainController (or Machine) template as the DC's computer account.
  3. Use the certificate for PKINIT, obtain the DC's TGT and NT hash, and run DCSync.

Any domain user can do this, and with some coercion methods no account at all. Requiring HTTPS with Extended Protection for Authentication (EPA) binds the authentication to the TLS channel and breaks the relay.

How ad-doctor detects it

For each enterprise CA found in LDAP (dNSHostName of each pKIEnrollmentService), ad-doctor sends an unauthenticated GET /certsrv/ over plain HTTP to the port given by --http-port (default 80). The CA is reported when the server answers 401 and the WWW-Authenticate headers offer NTLM or Negotiate (which falls back to NTLM). A refused connection means no web enrollment; any other error is noted in the report as not probed rather than clean. A CES URL in msPKI-Enrollment-Servers that uses plain HTTP gets the same probe and is reported here too.

The probe sends no credentials and does not request a certificate. Each object is the endpoint URL, for example http://ca01.corp.example/certsrv/. It runs unless --no-probes is given or --http-port 0 is set.

Attributes and probes read
  • dNSHostName (on pKIEnrollmentService)
  • HTTP GET /certsrv/: status and WWW-Authenticate

Severity

Always high: with any coercion primitive the endpoint turns a domain user into a DC. AD-CHAIN-003 reports the coercion path as well, graded by what the scan knows about the spooler and outgoing NTLM on the DCs.

False positives and limits

  • Not covered here: HTTPS endpoints, which are also relayable without EPA. AD-ADCS-019 lists them with EPA as unknown. CES and web enrollment installed at a path other than the default and not published in the directory are not found at all.
  • Negotiate restricted to Kerberos. If IIS offers Negotiate but NTLM is disabled for the site, the relay fails; ad-doctor cannot tell from the header alone. Confirm with the read-only commands below.

Fix

Follow KB5005413; apply at least one of these, in order of preference:

  1. Remove web enrollment if nobody uses it (run on the CA):

    Uninstall-WindowsFeature ADCS-Web-Enrollment -WhatIf
    
  2. Require HTTPS and Extended Protection on the CertSrv application. Read the current settings first:

    Import-Module WebAdministration
    Get-WebConfigurationProperty -PSPath 'IIS:\' -Location 'Default Web Site/CertSrv' `
        -Filter 'system.webServer/security/access' -Name sslFlags
    Get-WebConfigurationProperty -PSPath 'IIS:\' -Location 'Default Web Site/CertSrv' `
        -Filter 'system.webServer/security/authentication/windowsAuthentication/extendedProtection' `
        -Name tokenChecking
    

    Then, after review, set sslFlags to Ssl and tokenChecking to Require, remove the HTTP binding if nothing else needs it, and restart IIS.

  3. Disable NTLM for the site (Windows Authentication providers: Negotiate:Kerberos only), or block incoming NTLM on the CA server by policy.

  4. Reduce coercion: disable the Print Spooler on DCs (AD-INF-009). This narrows, but does not close, the relay path.

Verify

ad-doctor scan dc01.corp.example --ldaps -u 'CORP\auditor' --only AD-ADCS-010

To accept the risk instead, add an entry to the file passed with --accept (see accepted risks):

[[accept]]
check = "AD-ADCS-010"
objects = ["..."]        # optional; without it the whole check is accepted
reason = "..."
expires = 2027-03-31

References

Microsoft and vendors

Research

Tools

MITRE ATT&CK

Available since 0.1.0.