CertiGhost (CVE-2026-54121): How a Low-Privileged AD User Impersonated a Domain Controller via AD CS

Summary: CVE-2026-54121 AD CS Exploit
On July 24, 2026, security researchers H0j3n and Aniq Fakhrul publicly disclosed CertiGhost — a vulnerability in Microsoft's Active Directory Certificate Services (AD CS) that fundamentally breaks the trust boundary between certificate enrollment and directory identity resolution. Assigned CVE-2026-54121 with a CVSS 8.8 score, the flaw allows any low-privileged domain user — even a standard Domain Users member with no administrative rights — to obtain a CA-signed certificate that authenticates as a Domain Controller, and from there execute a DCSync attack to dump the krbtgt hash and fully compromise the Active Directory domain.
The root cause is deceptively simple: the AD CS enrollment protocol includes a fallback mechanism called a "chase" that lets a certificate requester tell the Certification Authority (CA) which host to contact for directory lookups. The CA blindly trusted that requester-supplied hostname, connected to it over SMB and LDAP, and used whatever identity data that host returned — including a Domain Controller's objectSid and dNSHostName — to build the certificate. No validation. No verification. Just pure, unauthenticated trust in attacker-controlled input.
Microsoft patched the issue on July 14, 2026 as part of their monthly security updates, but the full proof-of-concept was already public by July 24. This post walks through the vulnerability's root cause, dissects the exploit mechanics step by step, analyzes the patched code path, and frames the attack from a red team operator's perspective.
Understanding the Vulnerability
::The AD CS Chase Fallback — A Trust Boundary Failure
To understand CertiGhost, you need to understand how AD CS resolves a requester's identity during certificate enrollment. In the normal flow, the CA contacts the domain's real directory services to look up the requester's account and populate the certificate's subject information. But AD CS also has a secondary lookup path — the chase — designed for cross-domain enrollment scenarios where the requester's domain controller might be different from the CA's.
Two request attributes control this chase behavior:
- ▹
cdc(Client DC): Identifies the host the CA should contact for the directory lookup. - ▹
rmd(Remote Domain): Identifies the principal the CA should look up on that host.
When both attributes are present in a certificate request, the CA initiates the chase: it opens SMB and LDAP connections to the host specified in cdc, authenticates against that host, and searches for the principal named in rmd. The CA then uses the returned directory data — including the objectSid, dNSHostName, and sAMAccountName — to construct the certificate's identity material.
::Where It Breaks: No Authentication of the Chase Target
The critical flaw is that the CA never verified that the cdc host was actually a Domain Controller. The chase was designed to follow whatever host the requester specified, and the CA treated the response from that host as authoritative directory data. This created a textbook confused deputy problem: the CA — a trusted authority in the domain — was acting on untrusted routing information supplied by the requester.
An attacker could:
- ▹Supply
cdcpointing to their own host — a machine they fully control. - ▹Run rogue LSA and LDAP services on that host — responding to the CA's queries with crafted data.
- ▹Return a Domain Controller's identity attributes — specifically the target DC's
objectSidanddNSHostNameinstead of the attacker's own. - ▹Satisfy the CA's authentication requirements — because a machine account created through the default
ms-DS-MachineAccountQuota(set to 10 by default in most domains) is a perfectly valid domain principal, and the rogue LSA service can relay authentication challenges to the real DC via Netlogon.
The CA would then issue a certificate containing the Domain Controller's identity, signed by the Enterprise CA — a certificate that the KDC would accept during PKINIT authentication, mapping it to the DC's account and issuing a TGT for that DC.
::Why This Is Catastrophic
Domain Controller accounts hold directory replication rights by design — they need those permissions to sync the directory across the forest. Once an attacker authenticates as a DC, they can invoke the DRSUIOIDLGetNCChanges RPC method (the same one used by legitimate DCs for replication) to request any account's secrets, including the krbtgt hash. This is the DCSync attack, and with krbtgt in hand, the attacker can forge Golden Tickets and achieve persistent, forest-level compromise.
Exploit Prerequisites for CVE-2026-54121: The entire chain — from a single Domain Users account to full Active Directory forest compromise — requires:
- ▹Network access to the domain (even from a non-domain machine, as long as it can reach the CA and DC).
- ▹A domain account with no special privileges.
- ▹An Enterprise CA that follows the vulnerable chase path (the default configuration).
- ▹The default Machine certificate template with its default ACL (Domain Users can enroll).
- ▹The default
ms-DS-MachineAccountQuotavalue (10), allowing any user to create machine accounts.
No administrator rights. No user interaction. No special software beyond a Python script.
Exploit Mechanics — Technical Deep Dive
CertiGhost (CVE-2026-54121) Chase Flow
Now let's walk through the exploitation chain in detail, analyzing the published proof-of-concept from the GitHub repository and the technical analysis Gist.
::Step 0: Lab Setup and Prerequisites
The researchers tested against a Windows Server 2016+ forest with:
| Component | Value |
|---|---|
| Domain Controller | WDC01.abc.local at 192.168.8.128 |
| Enterprise CA | abc-WCA01-CA on WCA01.abc.local at 192.168.8.129 |
| Attacker host | 192.168.8.134 |
| Low-privileged account | normaluser (Domain Users member) |
| Machine Account Quota | Default (10) |
| Certificate Template | Default Machine template with default ACL |
The PoC is a self-contained Python script — certighost.py — that automates the entire chain. Dependencies include impacket, cryptography, pyasn1, asn1crypto, and dnspython:
::Step 1: Discovery — Enumerating CA and DC Information
The script first connects to LDAP using the low-privileged account to discover critical domain infrastructure:
Through LDAP queries, the script retrieves:
- ▹The Enterprise CA name and its hosting server.
- ▹The target Domain Controller's DNS name, IP address, and SID.
- ▹The domain SID and domain GUID — needed for Kerberos and Netlogon operations.
This step requires nothing more than a standard Domain Users account. All of this information is queryable by any authenticated domain member.
::Step 2: Machine Account Creation — Establishing a Valid Domain Principal
The attacker creates a new computer account using the default ms-DS-MachineAccountQuota. This quota, set to 10 in most default installations, allows any domain user to create up to 10 machine accounts in the domain. The PoC generates a random machine name (e.g., GHOSTABCDEFGH$) and sets it up with the required service principal names (SPNs) so that the CA's authentication protocols can resolve it:
The machine account serves a dual purpose: it provides the valid domain identity needed to satisfy the CA's authentication checks during the chase, and its password hash is used for the Netlogon secure channel that relays the CA's challenge to the real DC.
You can also reuse an existing machine account by passing --computer-name to the script, which is useful if ms-DS-MachineAccountQuota has been set to 0 (a common hardening measure, though one that doesn't stop this attack if the attacker already controls a machine account).
::Step 3: Rogue Services — The LSA/SMB and LDAP Servers
This is where the magic happens. The PoC starts two rogue listeners on the attacker's host:
- ▹
SMB/LSA server on port 445: Handles the CA's SMB connection and the Local Security Authority (LSA) authentication challenge. When the CA connects and presents an NTLM challenge, the rogue LSA service relays that challenge to the real Domain Controller over a Netlogon secure channel, using the machine account's password hash. The real DC validates the challenge, and the rogue service returns the authentication response — making the attacker's host appear as a valid domain principal.
- ▹
LDAP server on port 389: Handles the CA's LDAP directory lookup. When the CA queries for the principal named in
rmd(the target DC), the rogue LDAP server responds with crafted attributes — specifically, it returns the target DC'sobjectSid,dNSHostName, andsAMAccountNamerather than the attacker's own identity.
The NLOracle class in the PoC handles the Netlogon relay:
The rogue LDAP server returns the target DC's attributes when queried:
Key insight: The CA doesn't distinguish between "data from a real DC" and "data from an attacker-controlled host claiming to be a DC." The chase protocol has no authenticity check for the cdc target, and the CA treats whatever the chase endpoint returns as gospel.
::Step 4: Certificate Request — Embedding the Chase Attributes
The PoC constructs a certificate enrollment request using the machine account's identity, but crucially embeds the cdc and rmd attributes that trigger the chase:
- ▹
cdc= the attacker's IP address (auto-detected or specified via--listener) - ▹
rmd= the target Domain Controller's DNS name

The request is submitted through the standard MS-WCCE (Windows Client Certificate Enrollment) protocol against the default Machine template. The machine account is enrolled using its newly created credentials, and the cdc/rmd attributes are injected into the request structure.
When the CA processes this request:
- ▹It reads the
cdcattribute and sees the attacker's IP. - ▹It opens SMB + LDAP connections to that IP.
- ▹The rogue services authenticate successfully (via Netlogon relay to the real DC).
- ▹The rogue LDAP server returns the target DC's
objectSidanddNSHostName. - ▹The CA builds and signs a certificate containing the DC's identity.
::Step 5: PKINIT Authentication — Becoming the Domain Controller
With the CA-signed .pfx certificate in hand, the PoC performs PKINIT (Public Key Cryptography for Initial Authentication in Kerberos) authentication against the KDC:
The KDC's certificate mapping logic ties the certificate's strong-mapping SID and DNS identity fields to the DC's Active Directory account. Because these values came from a CA-signed certificate (not from an attacker-controlled self-signed cert), the KDC trusts them and issues a Ticket-Granting Ticket (TGT) for the Domain Controller account.
The PoC writes the results to disk:
- ▹
.pfxfile: The CA-signed certificate for the DC. - ▹
.ccachefile: The Kerberos credential cache (TGT) for the DC account. - ▹NT hash: The DC account's NT hash, derived from the PKINIT exchange.

::Step 6: DCSync — Full Domain Compromise
With a TGT for the Domain Controller, the attacker executes DCSync — the directory replication attack that leverages the DC's inherent replication permissions to dump any account's credentials:
Want to dive deeper into DCSync? Check out our Active Directory Cheatsheet for commands and additional context on executing and mitigating DCSync attacks.

From krbtgt, the attacker can forge Golden Tickets — persistent, undetectable Kerberos TGTs that grant domain admin access indefinitely, even after password changes and service restarts (until krbtgt is reset twice).
::The Vulnerable Code Path — certpdef.dll Binary Analysis
The researchers' binary analysis pinpointed the vulnerable logic in certpdef.dll — the AD CS Certificate Policy Enterprise module loaded by certsrv.exe. The core vulnerable flow lives in CRequestInstance::_LoadPrincipalObject, which reads the cdc attribute and passes it directly into the chase lookup:
The request-to-directory path is a straight shot:
Requester-controlled routing data reaches a directory-authority decision with zero validation. The CA follows the attacker's cdc value, connects to the attacker's host, accepts whatever identity data it returns, and stamps it into a certificate.
::The July 2026 Patch — What Changed
Microsoft's July update introduces a feature-gated validation gate (Feature_3185813818) around the chase path. The patched CRequestInstance::_LoadPrincipalObject now validates the cdc hostname against Active Directory before following the chase:
The new _ValidateChaseTargetIsDC function performs two layers of validation:
Layer 1 — String-level rejection:
Layer 2 — Active Directory verification:
This LDAP query requires exactly one computer object whose dNSHostName matches the supplied cdc value AND whose userAccountControl includes the SERVER_TRUST_ACCOUNT bit (8192) — the flag that distinguishes real Domain Controller accounts from ordinary machine accounts. If no match is found, the function retries with an extracted base DN via _ExtractBaseDNForDCSearch. If still no match, the request is rejected outright.
A second new check — _GetObjectSID — verifies that the resolved object's SID matches the requester's SID, preventing object substitution even if an unusual lookup path is somehow exploited.
Important caveat: The feature gate is conditional. When Feature_3185813818 is disabled (for backward compatibility), the legacy vulnerable path still executes. This means that in environments where the servicing flag isn't properly enabled — or where someone intentionally disables it — the vulnerability remains exploitable even after the July update is installed. Administrators should verify that the feature gate is active on their CA servers.
::Summary: Before vs. After the Patch
| Component | Before (Vulnerable) | After (Patched) |
|---|---|---|
| Chase target | CA followed requester-supplied cdc blindly | CA validates cdc hostname against real AD directory |
| Principal lookup | Remote response treated as authoritative | Target must resolve to a legitimate DC computer object |
| Certificate identity | SID and hostname could be attacker-controlled | Resolution proceeds only after target validation + SID comparison |
| Request outcome | Forged chase response → certificate issuance | Failed validation or SID mismatch → error path, no certificate |
Red Team Perspective
::Attack Opacity — Why This Is a Blue Team Nightmare
From an offensive standpoint, CertiGhost is an exceptionally clean attack. Consider its properties:
- ▹No privileged access required: The entry point is a standard Domain Users account. No local admin, no domain admin, no delegated permissions. Just the ability to log in to the domain.
- ▹No user interaction: The attack is fully automated. No phishing clicks, no consent prompts, no "please approve this request" dialogs.
- ▹No custom tooling overhead: The PoC is a single Python script leveraging well-known libraries (impacket). A red team can integrate it into existing workflows within minutes.
- ▹Default configuration exploited: The attack works against default AD CS deployments — the default Machine template, the default machine account quota, the default chase behavior. No misconfiguration beyond "running AD CS as shipped" is needed.
- ▹Stealthy certificate path: The resulting certificate is CA-signed, not self-signed. It carries the Enterprise CA's full chain of trust, making it indistinguishable from a legitimate DC certificate in most logging and monitoring setups.
::Engagement Playbook — How Red Teams Would Weaponize This
Phase 1: Initial Access and Reconnaissance
After obtaining initial access (phishing, credential spraying, or an existing low-privileged account), the red team operator enumerates the target domain for AD CS infrastructure. Certipy's find module or custom LDAP queries reveal the Enterprise CA name, available templates, and the CA server's hostname:
The key targets are:
- ▹An Enterprise CA (standalone CAs don't support the chase path).
- ▹The Machine template or any template where
Domain Users(or the compromised account) has enrollment rights. - ▹Confirmation that
ms-DS-MachineAccountQuota > 0or access to an existing machine account.
Phase 2: Exploit Execution
The operator sets up the CertiGhost PoC on a host with network reachability to both the CA and the DC. The rogue SMB and LDAP listeners require root/Administrator privileges locally (ports 389 and 445 are privileged), but the attacker's host can be any machine — a cloud VPS, a pivot host, or even the same network segment:
The script automates the full chain: machine account creation, rogue service startup, certificate request submission, PKINIT authentication, and credential extraction. Within minutes, the operator has a .ccache file authenticating as the Domain Controller.
Phase 3: DCSync and Persistence
With DC-level TGT in hand, the operator executes DCSync to extract krbtgt and all other high-value credentials:
From there, the standard persistence playbook applies:
- ▹Golden Ticket: Forge a TGT using the
krbtgthash for long-lived, password-change-surviving domain admin access. - ▹Silver Tickets: Forge service tickets for specific services (CIFS, LDAP, HTTP) for targeted, stealthy access.
- ▹DCShadow: If the operator has already gained DC privileges, they can temporarily register a rogue DC to modify directory objects without leaving replication logs.
Phase 4: Detection Evasion Considerations
CertiGhost's certificate-based approach has significant evasion advantages over traditional credential-based attacks:
- ▹The PKINIT authentication uses a legitimate CA-signed certificate, not a password or NTLM hash. Most SIEM correlation rules don't flag PKINIT events the same way they flag unusual NTLM authentications.
- ▹The chase-related SMB and LDAP connections originate from the CA server to the attacker's host, not from the attacker to the CA. This can appear as normal CA enrollment traffic in network logs.
- ▹The rogue LDAP/SMB services are ephemeral — they run only during the ~30-second enrollment window and can be torn down immediately after certificate issuance.
- ▹The machine account used for the chase can be deleted after the attack, reducing forensic artifacts.
However, attentive defenders can spot anomalies:
- ▹CA server making outbound connections to unexpected IP addresses during enrollment (the CA connecting to the attacker's
cdctarget). - ▹Machine account creation by a low-privileged user followed immediately by certificate enrollment against the Machine template.
- ▹PKINIT authentications for DC accounts from unusual source IPs or at unusual times.
- ▹Non-DC machine accounts appearing in CA enrollment logs with
cdcattributes pointing to non-DC hosts.
::Mitigation — What Defenders Should Do Now
Immediate patching is the primary recommendation. Install the July 2026 security updates on all AD CS hosts. The patch introduces the _ValidateChaseTargetIsDC check and the SID verification gate that closes the vulnerable chase path.
For environments where immediate patching isn't possible, the researchers documented a hotfix: disable the chase fallback entirely by clearing the EDITF_ENABLECHASECLIENTDC policy flag:
Critical warnings about this hotfix:
- ▹This is a mitigation, not a permanent fix. If someone re-enables
EDITF_ENABLECHASECLIENTDCon an unpatched CA (via group policy, imaging, or administrative error), the vulnerability is exploitable again. - ▹Disabling the chase fallback breaks legitimate enrollment flows that depend on it — specifically, cross-domain enrollment scenarios. Test this on a staging CA before applying to production.
- ▹The researchers validated this mitigation only in a controlled lab, not on a production CA.
Additional hardening measures that reduce (but don't eliminate) risk:
- ▹Set
ms-DS-MachineAccountQuotato 0 to prevent arbitrary machine account creation. Note: this doesn't stop an attacker who already controls a machine account. - ▹Restrict enrollment permissions on the Machine template — remove
Domain Usersenrollment rights and limit enrollment to authorized computer accounts only. - ▹Monitor CA enrollment logs for requests containing
cdcattributes pointing to non-DC hosts. - ▹Deploy network monitoring rules that flag CA servers making outbound SMB/LDAP connections to unexpected destinations.
Conclusion
CertiGhost is a stark reminder that trust boundaries in enrollment protocols matter as much as trust boundaries in authentication protocols. The AD CS chase fallback was designed for a legitimate operational need — cross-domain certificate enrollment — but it created an implicit trust assumption: that whoever supplies the cdc attribute is pointing the CA at a real Domain Controller. That assumption was never enforced at the protocol level, and the result was a full domain compromise chain accessible to the lowest-privileged user in the forest.
The fix — validating the chase target against the real directory before following it — is exactly the right remediation. But the conditional feature gate (Feature_3185813818) means that administrators must verify the protection is actually active on their CA servers, not just assume that installing the July update is sufficient.
For red team operators, CertiGhost is a high-value addition to the AD CS attack toolkit — alongside Certifake, ESC1-ESC8, and other template-based abuses. For blue teams, it's another argument for auditing CA enrollment paths, restricting template permissions, and treating AD CS as a high-impact trust boundary that deserves the same scrutiny as Kerberos and NTLM.
::References

Red Team Consultant · Penetration Tester · Bug Bounty Hunter
Offensive security professional with 250+ vulnerabilities reported across 50+ organizations including Atlassian, Vimeo, and AT&T. Sharing research, tools, and field notes.