Your detection worked. I want to open with that, because it does not happen as often as it should.

Suzie Q clicked the thing on a Thursday morning. By 11:40 someone in your SOC had an alert they did not like, and by 11:52 her password was reset, her sessions were killed, her refresh tokens were revoked, and the EDR sweep was running on her laptop. That is a good hour of work. Most places take a week to get there, and a lot of places never do.

Then somebody closed the ticket, and the ticket was correct, and I was still in.

Here is the part your playbook does not have a line for. Suzie Q has a certificate. She has had one since the day her account was created, because autoenrollment gave her one off the User template, and it has quietly renewed itself every year since. Nobody asked for it. Nobody remembers it exists. It is a client authentication certificate for a real employee, issued by your own CA, and there is nothing wrong with it.

I took a copy of it and the private key while I had her session. I am not going to write down how, because that would turn this post into a tutorial, but I will tell you the part that matters: the template marks that key as non-exportable, and that did not stop me, because non-exportable in a software provider is a rule the API follows, not a property of the key. With her profile and her master key, the material comes out.

Now look at what I am holding.

It carries her SID in the extension, OID 1.3.6.1.4.1.311.25.2, because your CA is patched and does that correctly. Under Full Enforcement the mapping is strong, implicitly, exactly as designed. Your KDC has no reason to hesitate and no reason to log anything unusual. I have written before about what happens when the trust moves into a directory attribute. This is the opposite problem. The trust is in the certificate, the certificate is real, and the certificate is mine now.

So I authenticate. PKINIT, my copy of her key, and I get a Kerberos TGT for Suzie Q. Event 4768, certificate logon, Thursday afternoon, from a workstation she uses. Nothing about it looks like anything.

Then I ask your domain controller a follow-up question. The PAC that came back with that ticket carries her NT hash in it, encrypted to the session key, which is a feature: it is there so certificate logons can still do NTLM to the file server that has never heard of Kerberos. A user-to-user exchange hands it to me in plaintext form.

You changed her password at 11:52. I asked for the new one at 12:20, and your domain controller gave it to me.

Take a second with that. The reset was not just insufficient. The reset was a thing I could query the result of.

I do not need it again. That is the point I want to leave you with. The certificate is good for a year from the day it was issued, and before it expires I can renew it with the same key, and a renewal request authenticates with the certificate, not with her password. Her password is now a detail about her, not a control on me. She can change it every ninety days for the next eleven months and I will watch each one go by.

I did check whether you could take it away from me. You can. Revoke the certificate and your DCs will refuse it. But your base CRL publishes on a seven day cycle, and a domain controller honours the copy it already has until that copy expires. So the honest answer is that if someone had thought of certificates that Thursday, I would have had somewhere between a day and a week, and I would have spent it.

Nobody thought of certificates that Thursday. There is no field on the form.

Before I go, I renewed it early. Open your CA console, go to Issued Certificates, add the Request Attributes column, and sort by submission date. You are looking for a User certificate issued to Suzie Q at 3:14 on a Sunday morning. Same public key, new serial, and one attribute on the request that I typed myself.

It says Hiro.

Hiro out.


If you are a defender, here is what just happened, plainly.

There is no ESC number on this one. No template misconfiguration, no vulnerable enrollment right, no relay, no CA compromise. Every component behaved as designed. The certificate was legitimate, the enrollment was legitimate, the authentication was legitimate, and the domain controller was correct to accept it. The only failure was in the response, and the failure was an omission.

The pieces are documented. Pulling a user certificate and key out of a compromised profile is THEFT2 in Certified Pre-Owned, by Will Schroeder and Lee Christensen at SpecterOps, June 2021. Turning the resulting PKINIT ticket back into an NT hash is THEFT5 in the same paper, commonly called UnPAC the hash. The detail that makes the reset irrelevant is timing: the KDC builds the PAC_CREDENTIAL_INFO structure from the account's credentials as they stand when it issues the ticket, so what comes back is whatever the hash is now, not whatever it was when the certificate was enrolled. Renewing before expiry to keep the access is PERSIST3. None of this is new. It is five years old and it is still the quietest way to survive an incident response.

Two honest caveats.

First, this is persistence, not initial access and not privilege escalation. Hiro is on the workstation before any of it starts, and Suzie Q is nobody in particular. What the certificate buys is a credential that outlives your response on an account you have already marked as cleaned, which is worth more than it sounds, because that account will never be looked at twice.

Second, whether the key comes out at all depends on the provider. If the template requires a TPM-backed KSP or a smart card, this gets substantially harder, and in most cases it stops. If it is a software provider, the non-exportable flag is enforced by the API and not by the hardware, and it does not stop anyone with the user's context. I cannot give you a number for how many user authentication templates in the field are software KSP. The default User template is, and most organizations have never replaced it. That is a practitioner impression, not a statistic I can source.

Three things to check this week

One: add certificates to the incident response playbook. When an account is compromised, enumerate what your CAs have issued to it and revoke it. On each issuing CA that is a view of the issued certificates filtered by requester, or certutil -view -restrict against the UPN, and then revoke with reason KeyCompromise and publish a fresh CRL immediately rather than waiting for the next scheduled one. Two things worth knowing before you need them. Disabling the account stops certificate logon right away, so full containment already covers you, but a password reset alone does not, and reset-and-monitor is what most compromised user tickets actually get. And revocation is only as fast as your CRL, so find out today what your publication interval and overlap are, because that number is your real containment time.

Two: work out how long a certificate outlives an incident. Look at the validity period on every template that issues client authentication to users and workstations. The default User template issues for one year, so a certificate stolen in month one is a working credential for the next eleven. Then look at renewal, because renewal with the existing key extends that clock again and authenticates with the certificate rather than the password, so nothing tied to the account's credentials will notice. Shorter validity on user authentication templates costs you nothing except CRL size and buys you a smaller window on every incident you have not detected yet.

Three: find out whether the private keys are protected or just labelled. Check the private key flags on your client authentication templates. A software KSP with a non-exportable key is non-exportable as far as CAPI is concerned and no further. TPM KSP or a smart card is the difference between a flag and a control, and it is the single change that turns this whole story into a much shorter one. While you are in there, turn on CA auditing so that issuance and renewal are recorded at all. That is the AuditFilter on the CA plus the Object Access audit policy on the server, and it gets you 4886 and 4887 for requests and issuance. A renewal for a user account at three in the morning is not something you can go back and investigate if nothing wrote it down.

A password is a secret, and you can change a secret. A certificate is a statement your CA signed, and it stays true until you say otherwise, out loud, on a CRL that a domain controller actually reads.

Your response plan has a step for the secret. Check whether it has a step for the statement.

And while you are working out what your CAs have signed, it is worth asking which CAs your domain controllers still accept — including the one you decommissioned years ago, which does not need to exist in order to keep signing.