Elementrica
03Case Studies04References05Company06News07Contact
PLENDE

Certighost (CVE-2026-54121): the CA took the attacker's word for it

ElementricaElementrica7 min
Certighost (CVE-2026-54121): the CA took the attacker's word for it

There is a moment on an internal test when you stop hunting for vulnerabilities and start hunting for trust. Who believes whom without checking. Certighost is the textbook answer to that question: a certificate authority asked a foreign host who the domain controller was, and issued a certificate based on what it heard.

On July 24, 2026, researchers H0j3n and Aniq Fakhrul published an analysis together with a working PoC. Microsoft patched the bug on July 14 and rated it 8.8 as an authorization flaw. So far there are no public reports of exploitation in the wild. There is, though, a script that walks the whole way from a plain domain user account to the krbtgt secret.

The industry has again given the vulnerability a name and a logo. I have no complaint, “Certighost” is easier to remember than CVE-2026-54121, and most of us will file it under “the chase one” anyway.

What the bug was

AD CS issues certificates, and a certificate in a domain works like a signed proof of identity. You can present it to the KDC through PKINIT and get a Kerberos ticket as whoever is named in it.

While processing a request, the CA can ask another domain controller about a directory object. That path is called chase, and two request attributes drive it. The cdc attribute points at the host the CA should connect to. The rmd attribute points at the object it should look for there. Both values come from whoever submits the request.

Before the July patch, the CA connected to the given address over SMB and LDAP, asked about the named object, and built the certificate from the answer. It did not first check whether a domain controller actually stood on the other side.

The rest is mechanical. The attacker stands up their own LDAP and LSA services, points cdc at them, puts the real domain controller's DNS name in rmd, and sends the CA its objectSid and dNSHostName. Authentication along the way succeeds, because the attacker creates a machine account under ms-DS-MachineAccountQuota, and their stand-in services relay the CA's challenge to the real domain controller. From the CA's point of view a valid domain principal answers on the other side, so the certificate is issued with the domain controller's identity.

From there it is a straight line: PKINIT with that certificate, a Kerberos ticket as the domain controller, replication rights, DCSync, the krbtgt secret. In practice that means a double krbtgt reset, a response team up in the middle of the night, and a question from the board about how long this had been going on. That question usually has no answer, because nobody collected the logs that would give one.

The July update adds validation. Before the CA follows the cdc value, it checks in the directory whether the given name matches a computer object with the SERVER_TRUST_ACCOUNT flag (8192), rejects literal IP addresses and characters that could tamper with the LDAP filter, and after resolving the object it also compares the SID.

Our observations

This is not another ESC. The last few years taught us to look at AD CS through numbers: ESC1, ESC4, ESC8, and so on into the teens. They share one trait. They are configuration errors, so you fix them with configuration: a template, a flag, a permission, turning off web enrollment.

Here there was nothing to fix. The researchers worked on the default Machine template, with the default access list and the default machine account quota. This is not a misconfiguration. It is a bug in the certpdef.dll code.

The consequence is awkward for everyone who audits AD CS from a checklist. On this environment that audit would say “clean”.

The patch is installed, the path is not necessarily closed. In the July build the new validation sits behind a servicing flag (Feature_3185813818), and the old branch is still in the code and runs when the flag is not active.

I have no hard data on which environments have that flag on by default, and I am not going to guess. The practical takeaway is simpler: verify it on your own configuration instead of ticking off a KB in a WSUS report. The difference between “the update is installed” and “the path is actually closed” can be a whole chapter of the report.

The CA server is almost never in scope. We get an internal network and a list of critical systems. The box with the CA role is rarely on that list, because it processes no personal data, has no pretty interface, and nobody has it in a KPI. Usually someone stood it up for one project, a few years ago, and the machine has just been there ever since.

That machine issues identities for the whole domain. Segmentation, EDR everywhere, MFA on the VPN, quarterly access reviews. And one PKI service that asks a foreign host who the domain controller is…

If your CA server is not under the same patching and monitoring regime as the domain controllers, you have a trust assumption in the architecture that nobody ever wrote down.

ms-DS-MachineAccountQuota again. The same parameter comes back in escalation path after escalation path: RBCD, noPac, now Certighost. The default of 10 means any domain user can create a valid principal for themselves.

Setting the quota to zero does not fix Certighost, because the script can use an existing machine account. What it does is take away a free building block the attacker reaches for in most of the chains we see on internal tests. Move machine joins to a delegated group, the way it should have been from the start.

Detection is cheap, nobody just collects it. A CA server making an outbound connection to ports 389 and 445 toward a host that is not a domain controller is an anomaly visible in netflow or on the firewall, if anyone is watching.

On top of that come events you already have: 4741 for a machine account created by a user account, and 4886 and 4887 for certificate requests and issued certificates on the CA. On their own they mean nothing. Lined up over time they form one very readable pattern.

What to do this week

  • Install the July updates on every server with the CA role, not only on domain controllers.
  • If the patch has to wait, the researchers give a workaround: clear the chase flag with certutil -setreg policy\EditFlags -EDITF_ENABLECHASECLIENTDC and restart the CertSvc service. Check first that you do not rely on cross-domain enrollment, because that is the feature you are turning off.
  • Move the CA server out of the “application server” bucket and into the same class as domain controllers: the same patch cycle, the same monitoring, the same list of administrative access.
  • Set ms-DS-MachineAccountQuota to zero and hand machine joins to a delegated group.
  • Turn on certificate request logging on the CA and send those events somewhere a person actually watches them.

One question to end on

One thing makes me curious. How many of you knew that chase even existed before this CVE showed up?

That is also the best argument for running an internal test, not just a scan. A scanner checks the version and moves on. A person asks whether the path is actually closed in your configuration.

Is your CA server in scope for an internal test?

Book a free consultation: 30 minutes, straight to the point, no obligation. We will show how an internal test traces where a single ordinary account in the domain really leads, and whether paths like chase are actually closed in your setup.

Next
LegacyHive: a standard user mounts the admin's hive, and there is no patch