A forged passport office that issues diplomatic credentials to anyone who walks in with a library card
CVE-2026-54121, dubbed Certighost, is an improper authorization flaw in Active Directory Certificate Services (AD CS) affecting Windows Server 2012 through 2025. When a Certification Authority processes an enrollment request and cannot immediately resolve the requester's directory object, it falls back to a "chase" mechanism — contacting a Domain Controller specified *by the requester* via the cdc attribute. The CA never validated that the host it was calling was actually a Domain Controller. An attacker with any domain account creates a machine account (default ms-DS-MachineAccountQuota allows 10), stands up rogue LDAP and SMB/LSA listeners, and submits a certificate request pointing cdc at themselves and rmd at the real DC's DNS name. The CA connects back, the rogue services relay Netlogon auth to the real DC while injecting the target DC's identity into responses. The CA issues a certificate identifying the attacker as the Domain Controller. Via PKINIT, the attacker obtains a TGT as DC$, then runs DCSync to extract krbtgt — game over, full domain compromise from a helpdesk-tier account.
Microsoft rated this HIGH (8.8), but that label undersells the blast radius. The Scope metric is set to Unchanged (S:U), which ignores the most important fact: compromising the CA doesn't just affect the CA — it grants a Golden Ticket to every resource in the domain. A S:C assessment would push the base score past 9.0. The public PoC (certighost.py by Aniq Fakhrul) is weaponized and turnkey. Worse, Microsoft's fix is gated behind a velocity feature flag (Feature_3185813818), meaning installing the July 2026 update doesn't guarantee enforcement — mirroring the 2022 Certifried debacle where the fix sat in compatibility mode for nearly three years. AD CS is a canonical Certificate Authority component; when the bug is *in* the CA and the outcome is domain takeover, the severity floor is CRITICAL regardless of authentication prerequisites.
6 steps from start to impact.
Obtain any domain credential
certighost.py tool accepts -u/-p flags with a standard user.- Valid domain user credentials (any privilege level)
- Network access to the AD CS Certification Authority
- Requires initial foothold or credential compromise — but domain accounts are among the easiest Active Directory assets to obtain
Create a machine account
GHOSTABCDEFGH$) using the default ms-DS-MachineAccountQuota of 10, which allows any authenticated user to join machines to the domain. The certighost.py tool automates this. Alternatively, an existing controlled machine account can be reused via --computer-name.- ms-DS-MachineAccountQuota > 0 (default: 10)
- Organizations that have set MachineAccountQuota to 0 block this step — but most enterprises leave the default
win_security_adcs_certighost_ghost_machine_account_creation fires on accounts matching pattern GHOST*$.Deploy rogue LDAP and SMB/LSA listeners
- Attacker-controlled host able to bind ports 389 and 445
- Host must be reachable from the CA server (CA initiates outbound connections)
- Enterprises with default-deny egress on CA servers block leg 2 of the attack — but most CAs sit in broad 'server VLANs' without per-asset egress policy
- Binding to ports 389/445 requires admin/root on the attacker's host
certsrv.exe initiating outbound connections on port 389 or 445 to non-DC addresses is a high-fidelity anomaly.Submit poisoned certificate request
cdc (pointing to their rogue host) and rmd (the target DC's DNS name) attributes. The CA's CRequestInstance::_LoadPrincipalObject function processes the chase without validating the cdc target is a legitimate DC. The CA connects to the attacker's host, receives falsified identity data, and issues a certificate for the target Domain Controller.- AD CS Enterprise CA with chase mechanism enabled (default)
- The CA has not been patched with the July 2026 update OR the feature gate is not yet enforced
- The July 2026 patch adds
_ValidateChaseTargetIsDC— but enforcement is behind velocity feature gateFeature_3185813818, meaning it may not be active even after patching
cdc: attribute pointing to non-DC). EventID 4887 (certificate issued via chase). Both require CA audit flag AuditFilter bit 0x04 AND 'Certification Services' audit subcategory enabled.PKINIT authentication as Domain Controller
DC-01$. The KDC validates the certificate (it's legitimately signed by the Enterprise CA), finds a matching DC computer object, and issues a valid TGT. The attacker now holds a Kerberos identity with directory replication privileges.- Forged DC certificate from step 4
- PKINIT enabled in the domain (default)
- None — PKINIT is enabled by default in all modern AD environments and the certificate is legitimately signed
DCSync to extract krbtgt
secretsdump.py (Impacket) via KRB5CCNAME to perform DCSync (DRSGetNCChanges), extracting the krbtgt NT hash. This enables Golden Ticket creation and persistent, undetectable domain-wide access. The entire chain executes in under 60 seconds with the PoC tool.- Valid DC TGT from step 5
- Network access to a Domain Controller on RPC (port 135/dynamic)
- Organizations blocking DRSGetNCChanges from non-DC sources (MS-DRSR interface
e3514235-4b06-11d1-ab04-00c04fc2dcd2) stop this final leg — but this control is rare
certutil -setreg policy\EditFlags -EDITF_ENABLECHASECLIENTDC followed by Restart-Service CertSvc -Force on every Enterprise CA. This removes the vulnerable code path entirely by disabling the CDC chase fallback. This is the single highest-impact mitigation — it collapses the attack at step 4 before any identity confusion occurs. Per the noisgate mitigation SLA for CRITICAL, deploy within 3 days. Microsoft documents this as a safe change for single-forest environments; multi-forest PKI with cross-forest enrollment may need testing.ms-DS-MachineAccountQuota attribute on the domain root to 0. This prevents any non-admin user from creating machine accounts, breaking step 2 of the chain. Attackers could still reuse a pre-existing machine account they control, so this is defense-in-depth, not a standalone fix. Deploy within 3 days per CRITICAL mitigation SLA.certsrv.exe outbound connections on ports 389 and 445 only to known Domain Controller IPs. This blocks leg 2 of the attack — the CA cannot reach the attacker's rogue listeners. Requires per-asset scoping (not per-subnet). Deploy within 3 days. This control is durable against future AD CS vulnerabilities in the same class.e3514235-4b06-11d1-ab04-00c04fc2dcd2 opnum 3 (DRSGetNCChanges) from any source that is not a Domain Controller. This blocks step 6 (DCSync) and is effective against Certighost, all AD CS ESC chains, Zerologon-based attacks, and any future DC impersonation technique. Low breakage risk — only DCs should replicate. Deploy within 3 days.AuditFilter bit 0x04) and OS subcategory 'Certification Services' on all CAs. Deploy the Nextron SIGMA rules for EventIDs 4886 (chase request), 4887 (chase issuance), 4741 (ghost machine account), and 4768 (DC TGT from non-DC IP). These provide detection across the full chain but do not prevent exploitation. Deploy within 3 days as a detection complement to the blocking controls above.Feature_3185813818 is enforced — do not assume the patch alone activates the fix. Test by attempting a chase enrollment from a test account and confirming rejection. Per the noisgate remediation SLA for CRITICAL, complete patching within 90 days, but given the weaponized PoC, treat this as urgent and patch within the 3-day mitigation window alongside the registry fix.- MFA on user accounts — Certighost exploits certificate enrollment, not interactive logon. MFA does not gate certificate requests and would not prevent exploitation once the attacker has any form of valid domain credentials (password hash, Kerberos ticket, or NTLM hash).
- Disabling NTLM domain-wide — The attack's authentication leg uses Kerberos PKINIT, not NTLM relay. While the rogue listeners perform Netlogon relay (which uses NTLM-adjacent Netlogon secure channel), disabling NTLM at the domain level does not affect Netlogon RPC authentication.
- Web Application Firewall — The attack uses RPC (MS-WCCE on named pipes) and LDAP, not HTTP. WAF rules have zero visibility into this protocol layer.
- LAPS (Local Administrator Password Solution) — LAPS protects local admin passwords on endpoints. Certighost uses domain credentials and machine account passwords, neither of which LAPS manages.
The supporting signals.
| In-the-Wild Exploitation | No confirmed threat-actor exploitation as of October 2026. Microsoft states activity observed is consistent with security testing. Not listed in CISA KEV. |
|---|---|
| Proof of Concept | Public, weaponized. aniqfakhrul/CVE-2026-54121 (certighost.py) — turnkey Python tool. Also forked by chxsec/CVE-2026-54121. Original research gist by H0j3n. Listed on pocindex.io aggregated sources. |
| EPSS | 0.00778 (0.78% probability of exploitation in next 30 days). Given the weaponized PoC and AD CS attack surface interest from red teams (ESC1-ESC14 lineage), this score likely underestimates real-world risk. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H — Network-attackable, low complexity, low privilege required, no user interaction. noisgate assessment: Scope should be Changed (S:C) given cross-security-boundary impact (CA compromise → domain takeover). |
| Affected Versions | All Windows Server editions running AD CS Enterprise CA role: Server 2012, 2012 R2, 2016, 2019, 2022, 2025 (including Server Core). Also Windows 10 versions 1607 and 1809 (unlikely CA hosts in practice). |
| Fixed Versions | July 14, 2026 cumulative updates. Patched builds: Server 2016 ≥ 10.0.14393.9339, Server 2019 ≥ 10.0.17763.9020, Server 2022 ≥ 10.0.20348.5386, Server 2025 ≥ 10.0.26100.33158. Warning: fix is behind velocity feature gate Feature_3185813818 — verify enforcement independently. 0patch micropatches available for EOL Server 2012/2012 R2. |
| Scanning & Exposure | AD CS Enterprise CAs are internal-only by design — not directly Shodan/Censys-visible. However, any host on the internal network with domain credentials can reach them. Nextron SIGMA rules provide detection coverage across the full chain. |
| Disclosure Timeline | Reported to Microsoft May 14, 2026 by H0j3n (Muhammad Ali) and Aniq Fakhrul. Microsoft confirmed May 22, 2026. Patch shipped July 14, 2026. Public PoC released July 24, 2026 (10 days post-patch). |
| Researcher / Org | Muhammad Ali (H0j3n) and Aniq Fakhrul — independent researchers with prior AD CS exploitation research. Named the technique *Certighost* for the ghost machine account + DC impersonation pattern. |
| Related Techniques | Extends the AD CS ESC family (ESC1–ESC14). Attack pattern resembles Certifried (CVE-2022-26923) — both abuse certificate enrollment to impersonate DCs. Certighost specifically exploits the chase/CDC fallback path rather than SAN manipulation. |
Sources.
- NVD - CVE-2026-54121 Detail
- aniqfakhrul/CVE-2026-54121 — Certighost PoC (GitHub)
- Help Net Security — PoC exploit released for critical AD CS domain-takeover flaw
- Zero Networks — How Certighost Turns a Basic Account Into a Domain Controller
- Nextron Systems — Detecting Certighost: SIGMA Coverage Across the Full Attack Chain
- Microsoft Tech Community — Detecting CVE-2026-54121 with Microsoft Defender
- 0patch — Micropatches for Certighost (Server 2012/2012 R2)
- CrowdStrike — July 2026 Patch Tuesday Analysis
Why this verdict
- Public weaponized PoC eliminates attacker development cost.
certighost.pyis turnkey — domain, username, password, DC IP. Any operator who can run a Python script can execute this chain in under 60 seconds. This is not a theoretical advisory; it is a copy-paste domain takeover. - Default configurations enable every prerequisite.
ms-DS-MachineAccountQuotadefaults to 10 (step 2 succeeds). PKINIT is enabled by default (step 5 succeeds). Chase mechanism is enabled by default (step 4 succeeds). Most CA servers lack per-asset egress filtering (step 3 succeeds). An enterprise that has changed none of these defaults is fully exposed. - Patch enforcement is uncertain. Microsoft's fix is gated behind velocity feature flag
Feature_3185813818, meaning the July 2026 update may install without actually enabling the validation. This mirrors the Certifried precedent where the fix remained in compatibility mode for ~3 years. Defenders cannot rely on 'patch installed = vulnerability closed' without independent verification. - Vendor Scope metric understates blast radius. Microsoft scored
S:U(Unchanged), which models impact as confined to the CA. In reality, a forged DC certificate → PKINIT → DCSync →krbtgtextraction → Golden Ticket = complete domain compromise across every host, every user, every service. The true scope is Changed. - Role multiplier: AD CS is a canonical Certificate Authority. Per the high-value role catalog, the bug is *in* a CA — 100% of AD CS Enterprise CA deployments are high-value by definition. The blast radius on compromise is domain-scale (not host-scale). The documented chain succeeds in the canonical deployment role. The floor rule mandates CRITICAL when a canonical high-value component's compromise yields domain takeover, and no friction point provides installed-base evidence sufficient to break the floor.
- Authentication prerequisite does not materially reduce severity. PR:L (any domain user) is the lowest meaningful bar in an enterprise AD environment. Thousands of users hold domain accounts. Credential acquisition via phishing, spray, or broker is routine post-initial-access — this prerequisite does not represent a meaningful attacker bottleneck for any adversary who has reached the internal network.
Why not higher?
The score is 9.6, not 10.0, because the attack does require valid domain credentials (PR:L) and internal network access — it is not an unauthenticated, internet-facing exploit. Additionally, no confirmed in-the-wild exploitation has been observed, and the EPSS score remains below 1%, suggesting exploit adoption has not yet reached commodity-tool integration. These factors keep it below the absolute ceiling.
Why not lower?
AD CS is explicitly a Certificate Authority — a canonical high-value-role component where the verdict floor is CRITICAL per the deployment-role blast radius rule. The outcome is not 'CA compromise' but 'domain takeover' (krbtgt extraction → Golden Ticket → fleet-scale impact). A weaponized public PoC exists. Default configurations enable every step. The patch is unreliable due to the feature gate. Downgrading to HIGH would require evidence that <1% of AD CS Enterprise CA installations occupy the high-value role, which is definitionally impossible — they ARE the CA.
Crowdsourced verification payload.
Run this script on each Enterprise CA server as a local administrator. Execute: powershell -ExecutionPolicy Bypass -File .\Check-Certighost.ps1. The script checks whether the AD CS Certification Authority role is installed, whether the vulnerable chase flag is enabled, and whether the OS build meets the July 2026 patch threshold. It outputs VULNERABLE, PATCHED, or UNKNOWN.
#Requires -RunAsAdministrator
<#
Check-Certighost.ps1
Checks for CVE-2026-54121 (Certighost) exposure on AD CS Enterprise CAs.
Output: VULNERABLE / PATCHED / UNKNOWN
Exit codes: 1 = VULNERABLE, 0 = PATCHED, 2 = UNKNOWN
#>
$ErrorActionPreference = 'Stop'
# Step 1: Check if Certification Authority role is installed
try {
$caRole = Get-WindowsFeature -Name ADCS-Cert-Authority -ErrorAction SilentlyContinue
if (-not $caRole -or -not $caRole.Installed) {
Write-Host "[INFO] AD CS Certification Authority role is NOT installed on this host."
Write-Host "PATCHED" # Not applicable - no CA role
exit 0
}
Write-Host "[CHECK] AD CS Certification Authority role is INSTALLED."
} catch {
Write-Host "[WARN] Could not determine CA role status: $_"
Write-Host "UNKNOWN"
exit 2
}
# Step 2: Check EditFlags for EDITF_ENABLECHASECLIENTDC (0x00000100, bit 8)
try {
$rawFlags = certutil -getreg policy\EditFlags 2>&1
$flagLine = $rawFlags | Select-String 'REG_DWORD'
if ($flagLine -match '0x([0-9a-fA-F]+)') {
$editFlags = [Convert]::ToUInt32($Matches[1], 16)
$chaseEnabled = ($editFlags -band 0x100) -ne 0
if ($chaseEnabled) {
Write-Host "[VULN] EDITF_ENABLECHASECLIENTDC is ENABLED (EditFlags = 0x$($editFlags.ToString('X8'))). Chase mechanism is active."
} else {
Write-Host "[OK] EDITF_ENABLECHASECLIENTDC is DISABLED (EditFlags = 0x$($editFlags.ToString('X8'))). Chase mechanism is off."
}
} else {
Write-Host "[WARN] Could not parse EditFlags from certutil output."
$chaseEnabled = $null
}
} catch {
Write-Host "[WARN] Could not read EditFlags: $_"
$chaseEnabled = $null
}
# Step 3: Check OS build against patched versions
$osBuild = [System.Environment]::OSVersion.Version
$buildNumber = $osBuild.Build
$revisionRaw = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name UBR -ErrorAction SilentlyContinue).UBR
$revision = if ($revisionRaw) { [int]$revisionRaw } else { 0 }
Write-Host "[INFO] OS Build: $($osBuild.Major).$($osBuild.Minor).$buildNumber.$revision"
$patchedBuilds = @{
14393 = 9339 # Server 2016
17763 = 9020 # Server 2019
20348 = 5386 # Server 2022
26100 = 33158 # Server 2025
}
$osPatched = $false
$osBuildKnown = $false
if ($patchedBuilds.ContainsKey($buildNumber)) {
$osBuildKnown = $true
$requiredRev = $patchedBuilds[$buildNumber]
if ($revision -ge $requiredRev) {
Write-Host "[OK] OS build $buildNumber.$revision meets or exceeds patched revision $requiredRev."
$osPatched = $true
} else {
Write-Host "[VULN] OS build $buildNumber.$revision is BELOW patched revision $requiredRev."
$osPatched = $false
}
} elseif ($buildNumber -lt 14393) {
# Server 2012 / 2012 R2 — check for 0patch or manual mitigation
$osBuildKnown = $true
Write-Host "[WARN] Server 2012/2012 R2 detected. Check for 0patch micropatch or registry mitigation."
$osPatched = $false
} else {
Write-Host "[WARN] Unrecognized build number $buildNumber. Manual verification needed."
}
# Step 4: Verdict
if ($chaseEnabled -eq $false) {
Write-Host "`n=== RESULT ==="
Write-Host "PATCHED" # Chase disabled = mitigated regardless of patch level
Write-Host "(Chase mechanism is disabled. CVE-2026-54121 is mitigated.)"
exit 0
} elseif ($chaseEnabled -eq $true -and -not $osPatched) {
Write-Host "`n=== RESULT ==="
Write-Host "VULNERABLE"
Write-Host "(Chase mechanism is enabled AND OS is unpatched. Immediate action required.)"
exit 1
} elseif ($chaseEnabled -eq $true -and $osPatched) {
Write-Host "`n=== RESULT ==="
Write-Host "VULNERABLE"
Write-Host "(OS patch is installed but chase flag is still enabled. Feature gate enforcement is uncertain — verify independently. Disable chase as defense-in-depth.)"
exit 1
} else {
Write-Host "`n=== RESULT ==="
Write-Host "UNKNOWN"
Write-Host "(Could not determine chase flag status. Manual verification required.)"
exit 2
}