Your WebServer template is a model citizen. Enrollee-supplied subject is off; the CA builds the subject from Active Directory. Manager approval is on. Enroll is scoped to a single security group with three service accounts in it. You reviewed it during last quarter's ESC1 sweep, closed it as clean, and moved on. If someone ran the ESC1 detection against it today, it would pass.

At 2:00 AM a service account that is a member of a group called App-Team-CertAdmins opens the template's security descriptor. It has Full Control on the object. It flips the Subject Name setting to "Supply in the request," adds Client Authentication to the EKU list, grants itself Enroll, and clears the manager-approval flag. The template is now textbook ESC1. It requests a certificate naming Administrator@corp.local in the SAN, receives it, authenticates to a domain controller by way of PKINIT, and gets a Kerberos ticket for Administrator.

Then it puts the template back. Subject Name to "Build from Active Directory." EKU restored. Enroll ACE removed. Manager approval on. By 2:00:40 AM the template is a model citizen again.

If you run the ESC1 detection at 3:00 AM, it passes. It passed yesterday, it passes today, it will pass tomorrow. The configuration was never wrong for more than forty seconds, and it was wrong on a schedule the attacker chose. The thing that was wrong the whole time was not the configuration. It was who could change it.

This is ESC4. It was published in 2021 in Will Schroeder and Lee Christensen's Certified Pre-Owned research at SpecterOps, and it is the one that changes how you have to think about everything before it. ESC1 is a template that claims to be someone else. ESC2 is a template that can do anything. ESC3 is a certificate that lets you enroll as anyone else. ESC4 is not about a template's configuration at all. It is about write access to the object that holds the configuration. An attacker with the right permissions on the template object does not need to find a misconfigured template. They author one, use it, and revert it.

This article is for the defender. It covers what ESC4 actually is, why the fix for ESC1 through ESC3 does not touch it, why every configuration-level control you have is void on a template whose ACL is open, and how to find and close it in your environment.

I'm assuming you've read the ESC1, ESC2, and ESC3 articles. The detection tooling, the nested-group expansion, the publish-only-what-you-use discipline, the scaling story are all there and I'm not going to rewrite them. Where ESC4 reuses that machinery, I point back rather than duplicating.

I won't walk through exploitation. SpecterOps covers it, Certify and Certipy document the tooling, and Certipy in particular automates the rewrite-use-revert cycle so cleanly that the whole attack is a single command. Use those when you need to show leadership the attack. Use this when you need to find it and fix it.

What ESC4 actually is

Every ESC path so far has been a property of the template's configuration: a name flag, an EKU, an issuance requirement, an enrollment agent EKU. Detecting them meant reading attributes. ESC4 is a property of the template object's access control list, and detecting it means reading the ACL and the owner. The template's configuration is irrelevant to whether it is an ESC4 target, because the whole point is that the attacker rewrites the configuration.

A template is an ESC4 finding when a principal who should not have it holds any right that lets them modify the template object or its security descriptor. In practice that means one of the following, granted to a low-privileged principal directly or through nested group membership:

Full Control (GenericAll). The complete set. Includes everything below. This is the most common ESC4 finding and the easiest to create by accident.

Write (GenericWrite). Write to the object's properties. Enough to change the name flag, the EKU list, the enrollment flag, and the RA-signature requirement, which is enough to author an ESC1 or ESC2 or ESC3 template.

Write DACL (WriteDacl). The right to rewrite the object's access control list. An attacker with only WriteDacl grants themselves Full Control and proceeds. This one is quiet, because the basic Security tab in certtmpl.msc does not have a checkbox that reads "can rewrite these permissions"; it lives in the Advanced view, and a casual reviewer never sees it.

Write Owner (WriteOwner). The right to make yourself the owner. The owner of any securable object in Windows can always read and write the DACL, regardless of what the DACL says. So WriteOwner is a two-step path to Full Control: take ownership, then rewrite the DACL. This is the quietest finding of all, because ownership is not shown anywhere in the basic Security tab and most reviewers never check it.

Write to specific dangerous properties (WriteProperty scoped to the right attributes). You do not need write access to the whole object. Write access to msPKI-Certificate-Name-Flag alone lets you turn on enrollee-supplied subject. Write access to pKIExtendedKeyUsage or msPKI-Certificate-Application-Policy lets you add Client Authentication or the Certificate Request Agent EKU. Write access to nTSecurityDescriptor is WriteDacl by another name. A scoped write to any one of these is sufficient.

And, separately from the DACL: the object's owner. The owner is not an ACE. It is a field in the security descriptor, and it carries implicit WriteDacl and ReadControl that no ACE can revoke. If a low-privileged principal owns a template object, that template is ESC4 no matter how clean its explicit ACL looks. Checking the DACL and forgetting to check the owner is the single most common way an ESC4 review misses a live finding.

In the BrkrOps lab we have a template called ESC4 that is configured to look completely benign, with a security descriptor that grants Full Control to a broad group. The screenshots below reference it.

Screenshot of the Certificate Template Properties dialog in Windows Server, showing the General tab for a template named ESC4. Display name and template name are both ESC4. Validity one year, renewal six weeks. The template looks like an ordinary, unremarkable template. Nothing on this tab, or on the Subject Name, Extensions, or Issuance Requirements tabs, is misconfigured.
Figure 1. The General tab of the ESC4 lab template. The template's configuration is clean. That is the point. ESC4 is not visible on any of the configuration tabs.
Screenshot of the Advanced Security Settings dialog for the ESC4 template, reached through Security then Advanced. The Permissions tab lists several entries. One entry, highlighted, grants Full Control to a group named App-Team-CertAdmins, applied to This object only. Authenticated Users has Read. Domain Admins and Enterprise Admins have Full Control. At the top of the dialog, the Owner field reads Domain Admins.
Figure 2. The Advanced Security Settings dialog. This is where ESC4 lives and where the basic Security tab hides it. App-Team-CertAdmins has Full Control on the object. If that group's membership resolves to low-privileged accounts, the template is ESC4 regardless of how it is configured.

Why write access defeats every control before it

Here is the part that makes ESC4 the meta-vulnerability of the series rather than the fourth entry in a list.

Every remediation in the ESC1, ESC2, and ESC3 articles was a configuration change. Scope the Enroll permission. Turn on manager approval. Turn off enrollee-supplied subject. Require an authorized signature. Remove Any Purpose. Restrict enrollment agents. Every one of those controls lives in an attribute of the template object, and every one of those attributes is writable by a principal with GenericWrite or GenericAll on that object.

So on a template with an open ACL:

  • You hardened it against ESC1 by turning off enrollee-supplied subject. The attacker turns it back on.
  • You hardened it against ESC1 by scoping Enroll to a dedicated group. The attacker adds an Enroll ACE for themselves, because the Enroll permission is stored in the same nTSecurityDescriptor that WriteDacl lets them rewrite.
  • You hardened it against ESC2 by removing Any Purpose and adding specific EKUs. The attacker rewrites the EKU list.
  • You hardened it against ESC1 and ESC2 by requiring an enrollment agent signature. The attacker sets msPKI-RA-Signature back to zero.
  • You turned on manager approval. The attacker clears CT_FLAG_PEND_ALL_REQUESTS.

The configuration controls are real, and they are the right controls, against an attacker who cannot change the configuration. Against an attacker who can, they are suggestions. This is why template permissions are the control that underwrites all the others. A perfectly ESC1-hardened template with WriteDacl granted to Domain Users is more dangerous than an unhardened one, not less, because the hardening is a fiction that makes the template pass every configuration-based check while remaining a one-command escalation.

The corollary matters for how you prioritize. When you find a template that is both misconfigured (an active ESC1) and has an open ACL (ESC4), fixing the configuration without fixing the ACL closes nothing. The attacker reintroduces the misconfiguration the moment you remove it. ESC4 has to be fixed first, or the ESC1 fix does not hold.

How the attack works

Discovery is the same enumeration as everything before it, aimed at a different attribute. Instead of reading the name flag and the EKU list to find a misconfigured template, the attacker reads the nTSecurityDescriptor and the owner of every template object to find one they can write to. Any authenticated user can read these; the directory exposes them so that enrollment clients can evaluate their own access. The attacker is looking for a template where a group they belong to, or a well-known broad principal, holds Full Control, Write, WriteDacl, WriteOwner, a dangerous scoped write, or ownership.

Having found one, the attack is three steps.

Rewrite. The attacker modifies the template into whichever shape they need. The usual choice is the ESC1 shape, because it is the shortest path to a domain admin logon: turn on enrollee-supplied subject, ensure a client authentication EKU is present, grant themselves Enroll if they do not already have it, clear manager approval. If WriteDacl or WriteOwner is the only right they hold, the first move is to use it to grant themselves GenericAll, and then everything else follows.

Use. The attacker enrolls exactly as in ESC1: request a certificate from the now-vulnerable template, naming a privileged account in the SAN, and receive it. Nothing distinguishes this enrollment from a legitimate one, because for the forty seconds the template is misconfigured, it genuinely is an ESC1 template and the request is genuinely valid against it.

Revert. The attacker restores the template to its original configuration and, if they modified the ACL or owner, restores those too. The environment returns to its previous state. A configuration snapshot taken before or after the window shows nothing. This is what makes ESC4 evade the config-based detection that catches ESC1 through ESC3: the misconfiguration is transient and controlled by the attacker. Certipy's ESC4 mode does the save, modify, request, and restore as one operation specifically so that the vulnerable window is as short as possible and the template is left exactly as it was found.

Why strong certificate mapping is not your safety net here

For ESC1 and ESC2 I could tell you that KB5014754 strong certificate mapping, in full enforcement mode, blunts the domain-authentication payoff of an arbitrary-SAN certificate. The temptation is to assume the same holds for ESC4, because the most common ESC4 payload is ESC1-shaped. It does not hold, and relying on it is a mistake, for two reasons.

First, if the attacker takes the arbitrary-SAN path and your CA is patched and enforcing, strong mapping does reject that specific logon, the same way it does for ESC1. But the attacker with write access is not confined to that path. They can author an ESC2 shape instead, adding Any Purpose to mint a code-signing or mutual-TLS certificate that strong mapping does not touch, because those capabilities never go through Kerberos. They can author an ESC3 shape, adding the Certificate Request Agent EKU and enrolling on behalf of a privileged user, which produces a certificate carrying the victim's real SID that strong mapping accepts. Strong mapping constrains exactly one of the shapes an attacker with template-write can freely author. Whichever defense you have, they write the shape that goes around it, because you handed them the pen.

Second, and more fundamentally: a certificate template governs domain authentication. Write access to it is write access to a tier-zero-adjacent object. The specific payload is almost beside the point. An account that can rewrite a template's security descriptor is, in security terms, already holding a piece of your tier-zero infrastructure. ESC4 is not a certificate problem that strong mapping might paper over. It is an access-control problem on a critical object, and it has to be closed at the ACL.

Why each step works

There is no bug. Directory objects have owners and DACLs because that is how Active Directory delegates administration. Certificate templates are directory objects, so they have owners and DACLs like everything else. An administrator who wants to let a team manage its own template grants that team write access, and the directory faithfully honours it. The CA issues what the template permits at the moment of the request, with no memory of what the template permitted a minute ago. Every component is doing what it was built to do. The exposure is in granting write access to a template object to a principal broader or less trusted than the template's blast radius, which is domain authentication.

The prevention you should have had

The cleanup above is longer than the prevention. Three principles, and unlike the configuration disciplines from the earlier articles, these are about the object, not its contents.

Treat write access to a template as tier-zero delegation. The ability to modify a certificate template is the ability to modify how the domain authenticates people. It belongs in the same tier as Domain Admin, not in the same tier as "the app team manages its own stuff." Write, Full Control, WriteDacl, and WriteOwner on any template object should be held only by the PKI administrators and the built-in tier-zero groups (Domain Admins, Enterprise Admins) that already have it by default. If a delegation genuinely requires letting a team request a specific kind of certificate, that is an Enroll grant, not a Write grant, and the two are not interchangeable.

Own your template objects deliberately. The owner of every template object should be a tier-zero group, normally Domain Admins or Enterprise Admins, which is the default. Ownership is invisible in the day-to-day console and it is exactly the field that a delegation or a migration quietly changes. When someone creates a custom template, the creator can end up as the owner. When a template is created by a service account or a delegated admin, that principal can end up as the owner, carrying implicit WriteDacl that no explicit ACE reflects. Set ownership back to tier-zero after any template creation, and audit it, because it is the one field your DACL review will not catch.

Watch the container, not just the templates. Certificate template objects live under CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,<forest root>. If someone grants write access on that container with inheritance enabled, every template underneath inherits it, and you have ESC4 on all of them at once from a single ACE. This is also an ESC5 finding on the container itself, and it is the same root cause wearing two labels: the container-level grant is the ESC5, the inherited ACE on each template is the ESC4. Reviewing templates one at a time will show you a suspicious pattern of identical inherited ACEs; the fix is at the container, not on each template.

Why this is everywhere

ESC4 turns up less often than ESC1 but more often than people expect, and almost always for one of these reasons.

The "let the app team manage their own template" delegation. A team wants to tweak the validity period or the SAN handling on their template without filing a ticket every time. An administrator grants their group Full Control on the object, because Full Control is the permission that makes the "can they change it" question go away. Nobody framed it as "we are granting this team the ability to issue themselves domain admin certificates," because from the console it looked like granting the ability to edit a template. Years later the group's membership has drifted, the original requester has left, and the group resolves to a dozen accounts nobody has reviewed.

The template that was created by the wrong account. A custom template gets created during a project by a delegated admin or a service account, and that principal becomes the owner. The explicit DACL might be perfectly scoped. The owner carries implicit WriteDacl regardless. Nobody set the owner back to tier-zero because nobody looked at the owner.

The cloned template that inherited a bad ACL. Someone duplicated an existing template to make a variant. The duplicate inherited the source template's security descriptor, including whatever over-broad write ACE the source had accumulated. Now the finding exists on two objects instead of one, and fixing the original does not fix the copy.

The container ACL that got loosened. During a delegation project someone granted a group write access on the Certificate Templates container with inheritance, intending to let that group create new templates. Inheritance flowed the write ACE down onto every existing template. The intended capability was "create templates"; the delivered capability was "rewrite every template in the forest."

The migration or tool that reset ownership. A migration between CAs, a bulk template edit script, or a third-party PKI tool ran under a service account and, as a side effect, became the owner of the objects it touched. The service account is now a standing WriteDacl on those templates by virtue of ownership.

Why nobody catches it: the dangerous rights are not on the tab people look at. The basic Security tab in certtmpl.msc shows Read, Write, Enroll, Autoenroll, and Full Control as checkboxes, but a reviewer scanning it for ESC1 is looking at the Enroll column, not the Write or Full Control column, and the tab does not show the owner or the WriteDacl/WriteOwner distinction at all. Those are in Advanced, which most reviews never open. And the configuration is clean, so every configuration-based check passes, which produces false confidence: "we reviewed that template, it's fine."

Detecting ESC4 in your environment

ESC4 detection asks a different question from ESC1 through ESC3. Not "how is the template configured" but "who can change how it is configured." You are reading two things on each template object: the owner, and the DACL for dangerous rights held by principals who should not have them. As with the Enroll analysis in the ESC1 article, the DACL question only has a real answer after you expand nested groups, because a benign-looking group name can resolve to Domain Users.

All commands below are read-only. A standard domain user can read template owners and DACLs, which is exactly why an attacker can enumerate ESC4 without any special access.

certutil for a known template

certutil -dstemplate dumps configuration but not the object's security descriptor in a form that surfaces the dangerous rights clearly. For the ACL on a specific template object, dsacls against the object's distinguished name is the built-in tool that reads owner and permissions:

dsacls "CN=WebServer-Custom,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local"

The output lists the owner at the top and then every ACE. What you are looking for:

  • The Owner line. If it names anything other than a tier-zero group, that is a finding on its own.
  • ACEs granting FULL CONTROL, WRITE PROPERTY, WRITE PERMISSIONS (WriteDacl), or WRITE OWNER to any principal that is not a PKI or tier-zero administrator. WRITE PERMISSIONS and WRITE OWNER are the two that a casual reader skims past; they are the two that matter most.

dsacls is fine for spot-checking a template you already suspect. It does not expand nested groups and it is impractical across a hundred and twenty templates. For that, PowerShell.

PowerShell enumeration across the forest

This enumerates every template object, reads its owner and DACL, and flags the ones where a dangerous right is held by a broad well-known principal, or where the owner is broad. For named groups that are not well-known SIDs, it surfaces the principal so you can run the recursive expansion from the ESC1 article against it. The dangerous-rights test is the ESC4 equivalent of the ESC1 Enroll test: same recursive-expansion problem, keyed on write rights instead of the Enroll extended right.

# Find candidate ESC4 templates in the current forest.
# Reads the owner and DACL of each template object; flags dangerous write
# rights and broad owners.
# Server 2022 native. Read-only. Requires the ActiveDirectory module (RSAT).

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Import-Module ActiveDirectory

$configNC      = (Get-ADRootDSE).configurationNamingContext
$templatesPath = "CN=Certificate Templates,CN=Public Key Services,CN=Services,$configNC"

# Well-known SIDs that broadly include low-privileged users.
$broadSids = @{
    'S-1-1-0'      = 'Everyone'
    'S-1-5-11'     = 'Authenticated Users'
    'S-1-5-7'      = 'Anonymous Logon'
    'S-1-5-32-545' = 'BUILTIN\Users'
    'S-1-5-32-546' = 'BUILTIN\Guests'
}
# Domain-relative RIDs that are broad: Domain Users (513), Domain Computers (515),
# Domain Guests (514). Resolved per-domain below.
$broadRids = @{ '513' = 'Domain Users'; '514' = 'Domain Guests'; '515' = 'Domain Computers' }
$domainSid = (Get-ADDomain).DomainSID.Value

# Dangerous rights: anything that lets the holder rewrite the object or its DACL.
$dangerous = [System.DirectoryServices.ActiveDirectoryRights]'GenericAll, GenericWrite, WriteDacl, WriteOwner, WriteProperty'
$emptyGuid = [Guid]::Empty

# Retrieve the security descriptor including the Owner. The SDDL owner flag is
# needed so GetOwner() returns a value.
$templates = Get-ADObject -SearchBase $templatesPath `
    -LDAPFilter '(objectClass=pKICertificateTemplate)' `
    -Properties name, displayName, nTSecurityDescriptor

function Test-BroadSid {
    param([string]$Sid)
    if ($broadSids.ContainsKey($Sid)) { return $broadSids[$Sid] }
    if ($Sid -like "$domainSid-*") {
        $rid = $Sid.Substring($domainSid.Length + 1)
        if ($broadRids.ContainsKey($rid)) { return $broadRids[$rid] }
    }
    return $null
}

$findings = New-Object System.Collections.Generic.List[object]

foreach ($t in $templates) {
    $sd = $t.nTSecurityDescriptor

    # 1. Owner check. The owner carries implicit WriteDacl regardless of the DACL.
    try {
        $ownerSid = $sd.GetOwner([System.Security.Principal.SecurityIdentifier]).Value
        $ownerBroad = Test-BroadSid $ownerSid
        if ($ownerBroad) {
            $findings.Add([PSCustomObject]@{
                Template = $t.name; Principal = $ownerBroad; Right = 'OWNER (implicit WriteDacl)'; Scope = 'n/a'
            })
        } else {
            # Non-broad owner still worth surfacing if it is not a tier-zero group.
            $ownerName = try { $sd.GetOwner([System.Security.Principal.NTAccount]).Value } catch { $ownerSid }
            if ($ownerName -notmatch 'Domain Admins|Enterprise Admins|Administrators$') {
                $findings.Add([PSCustomObject]@{
                    Template = $t.name; Principal = $ownerName; Right = 'OWNER (review: not tier-zero)'; Scope = 'n/a'
                })
            }
        }
    } catch {
        Write-Warning "Could not read owner for $($t.name): $_"
    }

    # 2. DACL check. Flag Allow ACEs with dangerous rights.
    foreach ($ace in $sd.Access) {
        if ($ace.AccessControlType -ne 'Allow') { continue }
        if ((([int]$ace.ActiveDirectoryRights) -band ([int]$dangerous)) -eq 0) { continue }

        # WriteProperty scoped to a single attribute is only dangerous for the
        # security-relevant attributes; unscoped (empty ObjectType) hits all of them.
        $isWritePropOnly = (([int]$ace.ActiveDirectoryRights) -band
            ([int][System.DirectoryServices.ActiveDirectoryRights]'GenericAll, GenericWrite, WriteDacl, WriteOwner')) -eq 0
        $scoped = ($ace.ObjectType -ne $emptyGuid)
        if ($isWritePropOnly -and $scoped) {
            $scopeNote = "WriteProperty scoped to $($ace.ObjectType) -- review: dangerous only if a security attribute"
        } else {
            $scopeNote = if ($scoped) { "scoped $($ace.ObjectType)" } else { 'all properties' }
        }

        $sid = try { ([System.Security.Principal.NTAccount]$ace.IdentityReference).Translate(
            [System.Security.Principal.SecurityIdentifier]).Value } catch { $ace.IdentityReference.Value }
        $broad = Test-BroadSid $sid

        $findings.Add([PSCustomObject]@{
            Template  = $t.name
            Principal = if ($broad) { "*** $broad ***" } else { $ace.IdentityReference.Value }
            Right     = $ace.ActiveDirectoryRights.ToString()
            Scope     = $scopeNote
        })
    }
}

if ($findings.Count) {
    Write-Host "ESC4 candidates -- dangerous rights or broad ownership on template objects:" -ForegroundColor Yellow
    $findings | Format-Table -AutoSize -Wrap
    Write-Host ""
    Write-Host "Entries marked *** are well-known broad principals and are confirmed findings."
    Write-Host "Named groups need recursive expansion (Expand-EnrollPrincipals from the ESC1"
    Write-Host "article, keyed on the write ACE instead of Enroll) to confirm whether they"
    Write-Host "resolve to low-privileged accounts."
} else {
    Write-Host "No template objects in this forest have dangerous rights held by broad principals." -ForegroundColor Green
}

Two things to know about the output. First, a principal marked with *** is a well-known broad SID and is a confirmed finding; a named group is only a finding if its transitive membership includes low-privileged accounts, which is the recursive-expansion problem the ESC1 article covers and which does not get easier here. Second, the WriteProperty scoped to <GUID> entries need a judgment call: a scoped write to a benign attribute like the display name is not ESC4, but a scoped write to msPKI-Certificate-Name-Flag, pKIExtendedKeyUsage, msPKI-Certificate-Application-Policy, msPKI-Enrollment-Flag, msPKI-RA-Signature, or nTSecurityDescriptor is. The script surfaces the GUID rather than guessing; resolve it against the schema (Get-ADObject under CN=Schema,CN=Configuration filtering on schemaIDGUID) if you need to be certain, and treat any unresolved scoped write to a msPKI-* attribute as a finding until proven otherwise.

Check the container, and check inheritance

Run dsacls against the Certificate Templates container itself:

dsacls "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local"

A dangerous write ACE here, with inheritance, is an ESC5 finding on the container and a mass ESC4 across every template. In the per-template output above, a container-inherited ACE shows up as the same principal with the same right on many templates at once; that pattern is the tell, and the fix is at the container.

Where manual detection breaks down

The same multi-forest scaling wall from the ESC1 article applies, with an added dimension. For ESC1 you expanded Enroll principals. For ESC4 you expand write principals and you also have to read and reason about ownership on every object, resolve scoped WriteProperty GUIDs against the schema, and account for inherited ACEs that originate at the container or higher. Across six forests and two hundred and forty templates, the owner-plus-DACL-plus-inheritance analysis is a materially larger job than the Enroll analysis, and it is the same category of multi-week engineering effort to do correctly by hand.

Remediating ESC4

ESC4 is remediated by fixing the object's access control, and only by fixing the object's access control. There is no configuration change that closes it, because configuration is the thing the attacker rewrites. This is the mirror image of ESC1 through ESC3, whose remediations were all configuration and none of them ACL.

Remove the dangerous rights. For every template where a low-privileged principal holds Full Control, Write, WriteDacl, WriteOwner, or a dangerous scoped write, remove that ACE. Write access to template objects should be held only by PKI administrators and the tier-zero groups that hold it by default. If a team needs to request certificates, grant Enroll, not Write. If a team genuinely needs to edit a template's non-security properties, that is a case for a tightly scoped delegation reviewed on its merits, not a Full Control grant, and it is rare enough that it should be the exception you can name and justify.

Fix the owner. Set the owner of every template object to a tier-zero group, normally Domain Admins or Enterprise Admins. This is the step most likely to be skipped because ownership is invisible in the day-to-day console, and it is the step that closes the implicit-WriteDacl path that an explicit-ACE cleanup leaves wide open.

# Reset the owner of a template object to Domain Admins.
$dn = "CN=WebServer-Custom,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local"
$domAdmins = (Get-ADGroup 'Domain Admins').SID
$obj = [ADSI]"LDAP://$dn"
$obj.PSBase.ObjectSecurity.SetOwner($domAdmins)
$obj.PSBase.CommitChanges()

Fix the container and inheritance. If the finding is inherited from the Certificate Templates container, fix it there. Remove the over-broad write ACE from the container, and confirm the inherited ACEs clear from the templates below. This closes the mass finding at its source instead of one template at a time.

Turn on auditing so the next one is not silent. ESC4's defining trait is that the attack reverts itself, so a configuration snapshot cannot catch it. What catches it is auditing the modification. Add a SACL to the template objects (or the container, inherited down) auditing successful writes, and enable the Directory Service Changes audit subcategory on the domain controllers. Then the rewrite and the revert both generate events, and the transient window that hides from snapshot detection is fully visible in the audit log. This is a detective control, not a preventive one; it does not stop ESC4, it makes sure that if an ACL you missed gets used, you find out. Fix the ACLs first, then audit as the backstop.

Do not stop at the configuration. If a template is both an active ESC1 and an ESC4, fix the ACL and the owner before, or at the same time as, the configuration. Removing the ESC1 misconfiguration while leaving the write access open is not remediation; the attacker reintroduces it on demand. The order is not cosmetic.

What the lab ended up with

For the ESC4 lab template:

  • The App-Team-CertAdmins Full Control ACE removed. The team's actual need was to enroll, so they were granted Enroll through a dedicated scoped group and nothing more.
  • Owner reset to Domain Admins.
  • Write, WriteDacl, and WriteOwner on the object confined to Domain Admins, Enterprise Admins, and the PKI administrators group.
  • A SACL added auditing successful property and DACL writes, with Directory Service Changes auditing enabled on the domain controllers, so any future modification of the template is logged.

The template's configuration was already clean and did not change. That is the whole lesson of ESC4: the configuration was never the problem.

A real configuration: delegated template management, and where the residual sits

The cleanest way to see why write access is the control that underwrites the others is a delegation that is legitimate, reasonable, and still leaves a residual if you grant it the obvious way.

A large environment has an application team that owns a certificate template for their internal service mesh. They legitimately need to adjust it: change the validity period as their rotation policy evolves, adjust the SAN construction as services move. Filing a PKI ticket for every change is real friction, and the PKI team does not want to be a bottleneck for a change that only affects that team's own template. So the delegation request is sound. The question is how to grant it.

The obvious way is Full Control on the template object for the app team's group. It works, it makes the friction go away, and it is ESC4. Full Control includes WriteDacl, which means the app team can grant themselves Enroll and turn on enrollee-supplied subject, which means any member of that group can issue themselves a domain admin certificate. The delegation intended "manage the validity period." The delegation delivered "escalate to domain admin." Nobody chose the second thing; it came bundled with the first.

Walk the safer options. Scoped WriteProperty on only the non-security attributes the team actually changes is closer to right, but it is fiddly to express, easy to get wrong, and one careless inclusion of a security attribute reopens the path. The honest answer for most environments is that template editing is not a delegation you hand out at all. It is a tier-zero action. The friction the app team feels is real, and the correct place to solve it is process, a fast path for template change requests, not permissions, a standing write grant on a domain-authentication object. When a team asks for write access to a template, the reframe is: they do not want to edit the template, they want a certificate with certain properties, and that is an enrollment design question, not an ACL question.

Where the residual sits, if you must delegate at all: the smallest grant that meets the need, on the fewest attributes, to the smallest group, with the owner still tier-zero and auditing on. And even then, the residual is real, because any write access to a template is a lever on domain authentication, and the person holding it is inside your tier-zero boundary whether the org chart says so or not.

The reality at scale

The ESC1 article covers the multi-forest, hundreds-of-templates, continuous-program reality, and it holds here with one addition that makes ESC4 harder to keep closed than the others. ESC1 through ESC3 are configuration findings, and configuration is relatively stable; a template you hardened tends to stay hardened until someone deliberately changes it. ESC4 is an access-control finding, and access control drifts constantly through the normal operation of the directory: group memberships change, delegations get added, templates get cloned and inherit ACLs, migrations reset owners, container permissions get loosened for a project and never tightened. A forest that is clean of ESC4 today acquires it through routine administration, without anyone touching a template on purpose.

That makes ESC4 the finding most in need of continuous rather than point-in-time assessment. A quarterly manual review of template ACLs and owners across six forests, with recursive group expansion and schema-GUID resolution on every scoped write, is exactly the kind of cross-object, permission-drift analysis that does not survive being done by hand on a schedule. The publish-only-what-you-use discipline helps here too, for the same reason it helps everywhere: fewer published templates means fewer objects whose ACLs and owners you have to keep watching.

How Truvald handles this

Truvald reads the owner and the full DACL of every certificate template object across every forest it has access to, expands write rights and ownership through nested groups and forest trusts the same way it expands Enroll for ESC1, resolves scoped WriteProperty ACEs against the schema to tell a dangerous attribute write from a benign one, and reports the templates a low-privileged principal can rewrite. It distinguishes explicit ACEs from container-inherited ones, so a mass finding is reported as one root cause at the container rather than a hundred and twenty separate lines you have to correlate. It runs the same analysis for ESC1, ESC2, ESC3, and ESC5 through ESC16, alongside the broader set of CA configuration, PKI object ACL, and GPO checks.

For ESC4 specifically, Truvald reports the finding that snapshot tooling cannot: a template that is configured cleanly right now but is writable by someone who should not be able to write it, with the specific principal, the specific right, the ownership status, and whether the exposure is direct or inherited. It also flags the dangerous combination the article warns about, a template that passes every configuration check while carrying an open ACL, which is precisely the one a configuration-only review closes as clean.

Output is a written assessment report. Word format. Every finding, severity, specific template, specific dangerous right or ownership, recommended remediation, residual risk. Suitable for the technical team and for the exec summary the technical team has to write.

Truvald runs inside the customer's network. No cloud, no telemetry, no data leaves. The template configurations, object ACLs, and ownership being assessed are among the most sensitive material in an enterprise environment. Truvald is built so BrkrOps never sees any of it.

Free 30-day evaluation at truvald.ca. For organizations that want the work delivered as an engagement, with BrkrOps running it in your environment, producing the report, and walking your team through the remediation roadmap, the Truvald + Assessment package combines the license with three days of consulting.

References

  • Will Schroeder and Lee Christensen. Certified Pre-Owned: Abusing Active Directory Certificate Services. SpecterOps, June 2021.
  • GhostPack Certify wiki, escalation techniques, ESC4.
  • Certipy documentation, template attack (template command, save and restore of vulnerable configuration).
  • Microsoft [MS-CRTD]: Certificate Templates Structure. The attribute definitions an attacker rewrites and a defender protects.
  • Microsoft [MS-ADTS]: Active Directory Technical Specification (object ownership, implicit WriteDacl/ReadControl for owners, and the DACL evaluation model).
  • Microsoft KB5014754: Certificate-based authentication changes on Windows domain controllers. Why strong mapping constrains one ESC4 payload shape and not the others.
  • The ESC1 defender's guide (companion article). Detection tooling, nested-group expansion, scaling.
  • The ESC2 defender's guide (companion article). Publish-only-what-you-use, the authorized-signatures requirement.
  • The ESC3 defender's guide (companion article). Enrollment agents, on-behalf-of enrollment, why a hardening in one place is an exposure in another.

Next in this series: ESC5, when the dangerous ACL is not on the template but on the objects around it. The CA object, the CA's AD computer account, the Certificate Templates and Enrollment Services containers, and NTAuthCertificates itself. Where write access to the PKI's own directory objects hands an attacker the ability to add a rogue CA, republish a template, or trust a certificate they minted, and why ESC4 was only the template-shaped corner of a larger access-control problem.

Patrick Mercier writes Truvald™ for BrkrOps™ Inc. out of Edmonton, Alberta. Truvald™ runs entirely inside your environment, no telemetry, no cloud component. A free Evaluation is available at truvald.ca/download.

Questions, corrections, or want to talk PKI? admin@brkrops.ca

Votre modèle WebServer est un citoyen modèle. « Fournir dans la demande » est désactivé; l'autorité de certification construit l'objet à partir d'Active Directory. L'approbation du gestionnaire est activée. L'inscription est restreinte à un seul groupe de sécurité comptant trois comptes de service. Vous l'avez révisé lors du dernier balayage ESC1, vous l'avez classé propre, et vous êtes passé à autre chose. Si quelqu'un exécutait la détection ESC1 dessus aujourd'hui, il passerait le test.

À 2 h du matin, un compte de service membre d'un groupe nommé App-Team-CertAdmins ouvre le descripteur de sécurité du modèle. Il possède le Contrôle total sur l'objet. Il bascule le paramètre Nom de l'objet vers « Fournir dans la demande », ajoute Authentification du client à la liste d'EKU, s'accorde l'inscription et efface l'indicateur d'approbation du gestionnaire. Le modèle est maintenant un ESC1 de manuel. Il demande un certificat en nommant Administrator@corp.local dans le SAN, le reçoit, s'authentifie auprès d'un contrôleur de domaine par PKINIT, et obtient un ticket Kerberos pour Administrator.

Puis il remet le modèle en place. Nom de l'objet à « Générer à partir d'Active Directory ». EKU restaurée. ACE d'inscription retirée. Approbation du gestionnaire réactivée. À 2 h 0 min 40 s, le modèle est de nouveau un citoyen modèle.

Si vous exécutez la détection ESC1 à 3 h, elle passe. Elle passait hier, elle passe aujourd'hui, elle passera demain. La configuration n'a jamais été mauvaise plus de quarante secondes, et elle l'a été selon un horaire choisi par l'attaquant. Ce qui était mauvais tout ce temps, ce n'était pas la configuration. C'était qui pouvait la modifier.

Voici ESC4. Il a été publié en 2021 dans la recherche Certified Pre-Owned de Will Schroeder et Lee Christensen chez SpecterOps, et c'est celui qui change votre façon de penser à tout ce qui précède. ESC1, c'est un modèle qui prétend être quelqu'un d'autre. ESC2, c'est un modèle qui peut tout faire. ESC3, c'est un certificat qui vous permet de vous inscrire pour n'importe qui d'autre. ESC4 ne porte pas du tout sur la configuration d'un modèle. Il porte sur l'accès en écriture à l'objet qui contient la configuration. Un attaquant disposant des bonnes permissions sur l'objet modèle n'a pas besoin de trouver un modèle mal configuré. Il en fabrique un, s'en sert, et le remet comme il était.

Cet article s'adresse au défenseur. Il couvre ce qu'est réellement ESC4, pourquoi le correctif d'ESC1 à ESC3 n'y touche pas, pourquoi chacun de vos contrôles de niveau configuration est nul sur un modèle dont la liste de contrôle d'accès est ouverte, et comment trouver et fermer le problème dans votre environnement.

Je présume que vous avez lu les articles ESC1, ESC2 et ESC3. L'outillage de détection, l'expansion des groupes imbriqués, la discipline « ne publier que ce que vous utilisez », le récit de la mise à l'échelle : tout est là et je ne vais pas le réécrire. Là où ESC4 réutilise cette machinerie, je renvoie plutôt que de dupliquer.

Je ne vais pas dérouler l'exploitation. SpecterOps la couvre, Certify et Certipy documentent l'outillage, et Certipy en particulier automatise si proprement le cycle réécrire-utiliser-restaurer que toute l'attaque tient dans une seule commande. Servez-vous-en quand vous devez démontrer l'attaque à votre direction. Servez-vous de ceci quand vous devez la trouver et la corriger.

Ce qu'est réellement ESC4

Chaque chemin ESC jusqu'ici était une propriété de la configuration du modèle : un indicateur de nom, une EKU, une condition d'émission, une EKU d'agent d'inscription. Les détecter, c'était lire des attributs. ESC4 est une propriété de la liste de contrôle d'accès de l'objet modèle, et le détecter, c'est lire la liste de contrôle d'accès et le propriétaire. La configuration du modèle n'a aucune importance pour déterminer s'il est une cible ESC4, parce que tout l'enjeu est que l'attaquant réécrit la configuration.

Un modèle constitue une découverte ESC4 lorsqu'un principal qui ne devrait pas l'avoir détient un droit qui lui permet de modifier l'objet modèle ou son descripteur de sécurité. En pratique, cela veut dire l'un des droits suivants, accordé à un principal peu privilégié directement ou par appartenance à un groupe imbriqué :

Contrôle total (GenericAll). L'ensemble complet. Inclut tout ce qui suit. C'est la découverte ESC4 la plus courante et la plus facile à créer par accident.

Écriture (GenericWrite). L'écriture sur les propriétés de l'objet. Suffisant pour changer l'indicateur de nom, la liste d'EKU, l'indicateur d'inscription et l'exigence de signature RA, ce qui suffit à fabriquer un modèle ESC1, ESC2 ou ESC3.

Écriture DACL (WriteDacl). Le droit de réécrire la liste de contrôle d'accès de l'objet. Un attaquant qui ne détient que WriteDacl s'accorde le Contrôle total et poursuit. Celui-ci est discret, parce que l'onglet Sécurité de base dans certtmpl.msc n'a aucune case à cocher qui dit « peut réécrire ces permissions »; ça vit dans la vue Avancé, et un réviseur pressé ne le voit jamais.

Écriture Propriétaire (WriteOwner). Le droit de devenir vous-même le propriétaire. Le propriétaire de tout objet sécurisable dans Windows peut toujours lire et écrire la DACL, peu importe ce que dit la DACL. WriteOwner est donc un chemin en deux temps vers le Contrôle total : prendre la propriété, puis réécrire la DACL. C'est la découverte la plus discrète de toutes, parce que la propriété n'apparaît nulle part dans l'onglet Sécurité de base et que la plupart des réviseurs ne la vérifient jamais.

Écriture sur des propriétés dangereuses précises (WriteProperty restreint aux bons attributs). Vous n'avez pas besoin d'un accès en écriture à l'objet entier. L'accès en écriture à msPKI-Certificate-Name-Flag seul permet d'activer « Fournir dans la demande ». L'accès en écriture à pKIExtendedKeyUsage ou à msPKI-Certificate-Application-Policy permet d'ajouter Authentification du client ou l'EKU Agent de demande de certificat. L'accès en écriture à nTSecurityDescriptor, c'est WriteDacl sous un autre nom. Une écriture restreinte à n'importe lequel de ces attributs suffit.

Et, distinct de la DACL : le propriétaire de l'objet. Le propriétaire n'est pas une ACE. C'est un champ du descripteur de sécurité, et il porte un WriteDacl et un ReadControl implicites qu'aucune ACE ne peut révoquer. Si un principal peu privilégié possède un objet modèle, ce modèle est ESC4 quelle que soit la propreté de sa liste de contrôle d'accès explicite. Vérifier la DACL et oublier de vérifier le propriétaire est la façon la plus courante pour une révision ESC4 de manquer une découverte réelle.

Dans le laboratoire BrkrOps, nous avons un modèle nommé ESC4 configuré pour paraître parfaitement bénin, avec un descripteur de sécurité qui accorde le Contrôle total à un groupe large. Les captures d'écran ci-dessous y font référence.

Capture d'écran de la boîte de dialogue Propriétés du modèle de certificat sous Windows Server, montrant l'onglet Général pour un modèle nommé ESC4. Le nom d'affichage et le nom du modèle sont tous deux ESC4. Validité d'un an, renouvellement de six semaines. Le modèle a l'air d'un modèle ordinaire et sans particularité. Rien sur cet onglet, ni sur les onglets Nom de l'objet, Extensions ou Conditions d'émission, n'est mal configuré.
Figure 1. L'onglet Général du modèle de laboratoire ESC4. La configuration du modèle est propre. C'est justement le but. ESC4 n'est visible sur aucun des onglets de configuration.
Capture d'écran de la boîte de dialogue Paramètres de sécurité avancés du modèle ESC4, atteinte par Sécurité puis Avancé. L'onglet Autorisations liste plusieurs entrées. Une entrée, surlignée, accorde le Contrôle total à un groupe nommé App-Team-CertAdmins, appliqué à Cet objet uniquement. Utilisateurs authentifiés a Lecture. Admins du domaine et Administrateurs de l'entreprise ont le Contrôle total. En haut de la boîte de dialogue, le champ Propriétaire affiche Admins du domaine.
Figure 2. La boîte de dialogue Paramètres de sécurité avancés. C'est là que vit ESC4 et là où l'onglet Sécurité de base le cache. App-Team-CertAdmins a le Contrôle total sur l'objet. Si l'appartenance de ce groupe se résout en comptes peu privilégiés, le modèle est ESC4 quelle que soit sa configuration.

Pourquoi l'accès en écriture met en échec tous les contrôles qui le précèdent

Voici ce qui fait d'ESC4 la métavulnérabilité de la série plutôt que la quatrième entrée d'une liste.

Chaque remédiation des articles ESC1, ESC2 et ESC3 était un changement de configuration. Restreindre la permission d'inscription. Activer l'approbation du gestionnaire. Désactiver « Fournir dans la demande ». Exiger une signature autorisée. Retirer Any Purpose. Restreindre les agents d'inscription. Chacun de ces contrôles vit dans un attribut de l'objet modèle, et chacun de ces attributs est modifiable par un principal disposant de GenericWrite ou de GenericAll sur cet objet.

Donc, sur un modèle dont la liste de contrôle d'accès est ouverte :

  • Vous l'avez durci contre ESC1 en désactivant « Fournir dans la demande ». L'attaquant le réactive.
  • Vous l'avez durci contre ESC1 en restreignant l'inscription à un groupe dédié. L'attaquant ajoute une ACE d'inscription pour lui-même, parce que la permission d'inscription est stockée dans le même nTSecurityDescriptor que WriteDacl lui permet de réécrire.
  • Vous l'avez durci contre ESC2 en retirant Any Purpose et en ajoutant des EKU précises. L'attaquant réécrit la liste d'EKU.
  • Vous l'avez durci contre ESC1 et ESC2 en exigeant une signature d'agent d'inscription. L'attaquant remet msPKI-RA-Signature à zéro.
  • Vous avez activé l'approbation du gestionnaire. L'attaquant efface CT_FLAG_PEND_ALL_REQUESTS.

Les contrôles de configuration sont réels, et ce sont les bons contrôles, contre un attaquant qui ne peut pas changer la configuration. Contre un attaquant qui le peut, ce sont des suggestions. Voilà pourquoi les permissions de modèle sont le contrôle qui sous-tend tous les autres. Un modèle parfaitement durci contre ESC1 mais dont WriteDacl est accordé à Utilisateurs du domaine est plus dangereux qu'un modèle non durci, pas moins, parce que le durcissement est une fiction qui fait passer au modèle chaque vérification de configuration tout en le laissant à une seule commande de l'escalade.

Le corollaire compte pour la façon dont vous priorisez. Quand vous trouvez un modèle qui est à la fois mal configuré (un ESC1 actif) et doté d'une liste de contrôle d'accès ouverte (ESC4), corriger la configuration sans corriger la liste de contrôle d'accès ne ferme rien. L'attaquant réintroduit la mauvaise configuration au moment où vous la retirez. ESC4 doit être corrigé en premier, sinon le correctif ESC1 ne tient pas.

Comment fonctionne l'attaque

La découverte est la même énumération que tout ce qui précède, dirigée vers un attribut différent. Au lieu de lire l'indicateur de nom et la liste d'EKU pour trouver un modèle mal configuré, l'attaquant lit le nTSecurityDescriptor et le propriétaire de chaque objet modèle pour en trouver un dans lequel il peut écrire. N'importe quel utilisateur authentifié peut les lire; l'annuaire les expose pour que les clients d'inscription puissent évaluer leur propre accès. L'attaquant cherche un modèle où un groupe auquel il appartient, ou un principal large bien connu, détient le Contrôle total, l'Écriture, WriteDacl, WriteOwner, une écriture restreinte dangereuse, ou la propriété.

Une fois qu'il en a trouvé un, l'attaque tient en trois étapes.

Réécrire. L'attaquant transforme le modèle dans la forme dont il a besoin. Le choix habituel est la forme ESC1, parce que c'est le chemin le plus court vers une ouverture de session d'administrateur de domaine : activer « Fournir dans la demande », s'assurer qu'une EKU d'authentification du client est présente, s'accorder l'inscription s'il ne l'a pas déjà, effacer l'approbation du gestionnaire. Si WriteDacl ou WriteOwner est le seul droit qu'il détient, le premier geste est de s'en servir pour s'accorder GenericAll, et tout le reste suit.

Utiliser. L'attaquant s'inscrit exactement comme dans ESC1 : demander un certificat au modèle désormais vulnérable, en nommant un compte privilégié dans le SAN, et le recevoir. Rien ne distingue cette inscription d'une légitime, parce que pendant les quarante secondes où le modèle est mal configuré, c'est réellement un modèle ESC1 et la demande est réellement valide contre lui.

Restaurer. L'attaquant remet le modèle dans sa configuration d'origine et, s'il a modifié la DACL ou le propriétaire, les restaure aussi. L'environnement revient à son état précédent. Un instantané de configuration pris avant ou après la fenêtre ne montre rien. Voilà ce qui fait qu'ESC4 échappe à la détection par configuration qui attrape ESC1 à ESC3 : la mauvaise configuration est transitoire et contrôlée par l'attaquant. Le mode ESC4 de Certipy fait la sauvegarde, la modification, la demande et la restauration en une seule opération, précisément pour que la fenêtre de vulnérabilité soit la plus courte possible et que le modèle soit laissé exactement comme il a été trouvé.

Pourquoi le mappage de certificat fort n'est pas votre filet de sécurité ici

Pour ESC1 et ESC2, je pouvais vous dire que le mappage de certificat fort de KB5014754, en mode d'application complet, atténue le gain d'authentification de domaine d'un certificat à SAN arbitraire. La tentation est de présumer que la même chose vaut pour ESC4, parce que la charge utile ESC4 la plus courante a la forme d'ESC1. Ce n'est pas le cas, et s'y fier est une erreur, pour deux raisons.

D'abord, si l'attaquant emprunte le chemin du SAN arbitraire et que votre autorité de certification est corrigée et en mode d'application, le mappage fort rejette bel et bien cette ouverture de session précise, comme il le fait pour ESC1. Mais l'attaquant disposant d'un accès en écriture n'est pas confiné à ce chemin. Il peut plutôt fabriquer une forme ESC2, en ajoutant Any Purpose pour émettre un certificat de signature de code ou de TLS mutuel que le mappage fort ne touche pas, parce que ces capacités ne passent jamais par Kerberos. Il peut fabriquer une forme ESC3, en ajoutant l'EKU Agent de demande de certificat et en s'inscrivant au nom d'un utilisateur privilégié, ce qui produit un certificat portant le vrai SID de la victime, que le mappage fort accepte. Le mappage fort contraint exactement une des formes qu'un attaquant disposant de l'écriture sur le modèle peut librement fabriquer. Quelle que soit la défense que vous avez, il écrit la forme qui la contourne, parce que vous lui avez remis le crayon.

Ensuite, et plus fondamentalement : un modèle de certificat régit l'authentification de domaine. L'accès en écriture à ce modèle, c'est l'accès en écriture à un objet adjacent au niveau zéro. La charge utile précise est presque hors de propos. Un compte qui peut réécrire le descripteur de sécurité d'un modèle détient déjà, en termes de sécurité, un morceau de votre infrastructure de niveau zéro. ESC4 n'est pas un problème de certificat que le mappage fort pourrait masquer. C'est un problème de contrôle d'accès sur un objet critique, et il doit être fermé au niveau de la liste de contrôle d'accès.

Pourquoi chaque étape fonctionne

Il n'y a pas de bogue. Les objets d'annuaire ont des propriétaires et des DACL parce que c'est ainsi qu'Active Directory délègue l'administration. Les modèles de certificat sont des objets d'annuaire, ils ont donc des propriétaires et des DACL comme tout le reste. Un administrateur qui veut permettre à une équipe de gérer son propre modèle accorde à cette équipe un accès en écriture, et l'annuaire l'honore fidèlement. L'autorité de certification émet ce que le modèle permet au moment de la demande, sans aucun souvenir de ce que le modèle permettait une minute plus tôt. Chaque composant fait ce pour quoi il a été conçu. L'exposition tient à l'octroi d'un accès en écriture à un objet modèle à un principal plus large ou moins fiable que le rayon d'impact du modèle, qui est l'authentification de domaine.

La prévention que vous auriez dû avoir

Le nettoyage ci-dessus est plus long que la prévention. Trois principes, et contrairement aux disciplines de configuration des articles précédents, ils portent sur l'objet, pas sur son contenu.

Traitez l'accès en écriture à un modèle comme une délégation de niveau zéro. La capacité de modifier un modèle de certificat, c'est la capacité de modifier la façon dont le domaine authentifie les gens. Elle appartient au même palier qu'Admin du domaine, pas au même palier que « l'équipe applicative gère ses propres affaires ». Écriture, Contrôle total, WriteDacl et WriteOwner sur tout objet modèle ne devraient être détenus que par les administrateurs de l'ICP et les groupes intégrés de niveau zéro (Admins du domaine, Administrateurs de l'entreprise) qui les détiennent déjà par défaut. Si une délégation exige réellement de laisser une équipe demander un certain type de certificat, c'est un octroi d'inscription, pas un octroi d'écriture, et les deux ne sont pas interchangeables.

Assumez la propriété de vos objets modèles délibérément. Le propriétaire de chaque objet modèle devrait être un groupe de niveau zéro, normalement Admins du domaine ou Administrateurs de l'entreprise, ce qui est le défaut. La propriété est invisible dans la console au quotidien, et c'est justement le champ qu'une délégation ou une migration change discrètement. Quand quelqu'un crée un modèle personnalisé, le créateur peut se retrouver propriétaire. Quand un modèle est créé par un compte de service ou un administrateur délégué, ce principal peut se retrouver propriétaire, portant un WriteDacl implicite qu'aucune ACE explicite ne reflète. Remettez la propriété au niveau zéro après toute création de modèle, et auditez-la, parce que c'est le seul champ que votre révision de DACL ne détectera pas.

Surveillez le conteneur, pas seulement les modèles. Les objets modèles de certificat vivent sous CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,<racine de forêt>. Si quelqu'un accorde un accès en écriture sur ce conteneur avec héritage activé, chaque modèle en dessous en hérite, et vous avez ESC4 sur chacun d'eux d'un coup, à partir d'une seule ACE. C'est aussi une découverte ESC5 sur le conteneur lui-même, et c'est la même cause racine portant deux étiquettes : l'octroi au niveau du conteneur est l'ESC5, l'ACE héritée sur chaque modèle est l'ESC4. Réviser les modèles un à un vous montrera un motif suspect d'ACE héritées identiques; le correctif est au conteneur, pas sur chaque modèle.

Pourquoi c'est partout

ESC4 apparaît moins souvent qu'ESC1, mais plus souvent qu'on ne le pense, et presque toujours pour l'une de ces raisons.

La délégation « laissons l'équipe applicative gérer son propre modèle ». Une équipe veut ajuster la période de validité ou la gestion du SAN sur son modèle sans ouvrir un billet chaque fois. Un administrateur accorde à leur groupe le Contrôle total sur l'objet, parce que le Contrôle total est la permission qui fait disparaître la question « peuvent-ils le changer ». Personne n'a présenté ça comme « nous accordons à cette équipe la capacité de s'émettre des certificats d'administrateur de domaine », parce que, depuis la console, ça ressemblait à accorder la capacité de modifier un modèle. Des années plus tard, l'appartenance du groupe a dérivé, le demandeur d'origine est parti, et le groupe se résout en une douzaine de comptes que personne n'a révisés.

Le modèle créé par le mauvais compte. Un modèle personnalisé est créé pendant un projet par un administrateur délégué ou un compte de service, et ce principal devient le propriétaire. La DACL explicite pourrait être parfaitement restreinte. Le propriétaire porte un WriteDacl implicite quoi qu'il arrive. Personne n'a remis le propriétaire au niveau zéro parce que personne n'a regardé le propriétaire.

Le modèle cloné qui a hérité d'une mauvaise DACL. Quelqu'un a dupliqué un modèle existant pour en faire une variante. Le doublon a hérité du descripteur de sécurité du modèle source, y compris toute ACE d'écriture trop large que la source avait accumulée. Maintenant, la découverte existe sur deux objets au lieu d'un, et corriger l'original ne corrige pas la copie.

La DACL de conteneur qui s'est desserrée. Lors d'un projet de délégation, quelqu'un a accordé à un groupe un accès en écriture sur le conteneur Certificate Templates avec héritage, dans l'intention de permettre à ce groupe de créer de nouveaux modèles. L'héritage a fait descendre l'ACE d'écriture sur chaque modèle existant. La capacité voulue était « créer des modèles »; la capacité livrée était « réécrire chaque modèle de la forêt ».

La migration ou l'outil qui a réinitialisé la propriété. Une migration entre autorités de certification, un script de modification de modèles en lot, ou un outil ICP tiers s'est exécuté sous un compte de service et, comme effet secondaire, est devenu le propriétaire des objets qu'il a touchés. Le compte de service est maintenant un WriteDacl permanent sur ces modèles en vertu de la propriété.

Pourquoi personne ne l'attrape : les droits dangereux ne sont pas sur l'onglet que les gens regardent. L'onglet Sécurité de base dans certtmpl.msc montre Lecture, Écriture, Inscription, Inscription automatique et Contrôle total sous forme de cases à cocher, mais un réviseur qui le balaie pour ESC1 regarde la colonne Inscription, pas la colonne Écriture ou Contrôle total, et l'onglet ne montre ni le propriétaire ni la distinction WriteDacl/WriteOwner. Ceux-là sont dans Avancé, que la plupart des révisions n'ouvrent jamais. Et la configuration est propre, alors chaque vérification par configuration passe, ce qui produit une fausse confiance : « on a révisé ce modèle, il est correct ».

Détecter ESC4 dans votre environnement

La détection d'ESC4 pose une question différente d'ESC1 à ESC3. Non pas « comment le modèle est-il configuré », mais « qui peut changer sa configuration ». Vous lisez deux choses sur chaque objet modèle : le propriétaire, et la DACL, à la recherche de droits dangereux détenus par des principaux qui ne devraient pas les avoir. Comme pour l'analyse d'inscription de l'article ESC1, la question de la DACL n'a de réponse réelle qu'après avoir développé les groupes imbriqués, parce qu'un nom de groupe d'apparence bénigne peut se résoudre en Utilisateurs du domaine.

Toutes les commandes ci-dessous sont en lecture seule. Un utilisateur de domaine standard peut lire les propriétaires et les DACL des modèles, ce qui est exactement la raison pour laquelle un attaquant peut énumérer ESC4 sans aucun accès spécial.

certutil pour un modèle connu

certutil -dstemplate déverse la configuration, mais pas le descripteur de sécurité de l'objet sous une forme qui fait ressortir clairement les droits dangereux. Pour la DACL sur un objet modèle précis, dsacls contre le nom unique de l'objet est l'outil intégré qui lit le propriétaire et les permissions :

dsacls "CN=WebServer-Custom,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local"

La sortie liste le propriétaire en haut, puis chaque ACE. Ce que vous cherchez :

  • La ligne Propriétaire. Si elle nomme autre chose qu'un groupe de niveau zéro, c'est une découverte en soi.
  • Les ACE accordant FULL CONTROL, WRITE PROPERTY, WRITE PERMISSIONS (WriteDacl) ou WRITE OWNER à tout principal qui n'est pas un administrateur d'ICP ou de niveau zéro. WRITE PERMISSIONS et WRITE OWNER sont les deux qu'un lecteur pressé survole; ce sont les deux qui comptent le plus.

dsacls convient pour vérifier ponctuellement un modèle que vous soupçonnez déjà. Il ne développe pas les groupes imbriqués et il est impraticable sur cent vingt modèles. Pour ça, PowerShell.

Énumération PowerShell à l'échelle de la forêt

Ceci énumère chaque objet modèle, lit son propriétaire et sa DACL, et signale ceux où un droit dangereux est détenu par un principal large bien connu, ou dont le propriétaire est large. Pour les groupes nommés qui ne sont pas des SID bien connus, il fait remonter le principal pour que vous puissiez exécuter l'expansion récursive de l'article ESC1 contre lui. Le test de droits dangereux est l'équivalent ESC4 du test d'inscription d'ESC1 : même problème d'expansion récursive, axé sur les droits d'écriture au lieu du droit étendu d'inscription.

# Trouve les modeles candidats ESC4 dans la foret courante.
# Lit le proprietaire et la DACL de chaque objet modele; signale les droits
# d'ecriture dangereux et les proprietaires larges.
# Natif Server 2022. Lecture seule. Requiert le module ActiveDirectory (RSAT).

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Import-Module ActiveDirectory

$configNC      = (Get-ADRootDSE).configurationNamingContext
$templatesPath = "CN=Certificate Templates,CN=Public Key Services,CN=Services,$configNC"

# SID bien connus qui incluent largement des utilisateurs peu privilegies.
$broadSids = @{
    'S-1-1-0'      = 'Everyone'
    'S-1-5-11'     = 'Authenticated Users'
    'S-1-5-7'      = 'Anonymous Logon'
    'S-1-5-32-545' = 'BUILTIN\Users'
    'S-1-5-32-546' = 'BUILTIN\Guests'
}
# RID relatifs au domaine qui sont larges : Domain Users (513), Domain Computers
# (515), Domain Guests (514). Resolus par domaine ci-dessous.
$broadRids = @{ '513' = 'Domain Users'; '514' = 'Domain Guests'; '515' = 'Domain Computers' }
$domainSid = (Get-ADDomain).DomainSID.Value

# Droits dangereux : tout ce qui permet au detenteur de reecrire l'objet ou sa DACL.
$dangerous = [System.DirectoryServices.ActiveDirectoryRights]'GenericAll, GenericWrite, WriteDacl, WriteOwner, WriteProperty'
$emptyGuid = [Guid]::Empty

# Recupere le descripteur de securite avec le proprietaire. L'indicateur owner de
# la SDDL est requis pour que GetOwner() retourne une valeur.
$templates = Get-ADObject -SearchBase $templatesPath `
    -LDAPFilter '(objectClass=pKICertificateTemplate)' `
    -Properties name, displayName, nTSecurityDescriptor

function Test-BroadSid {
    param([string]$Sid)
    if ($broadSids.ContainsKey($Sid)) { return $broadSids[$Sid] }
    if ($Sid -like "$domainSid-*") {
        $rid = $Sid.Substring($domainSid.Length + 1)
        if ($broadRids.ContainsKey($rid)) { return $broadRids[$rid] }
    }
    return $null
}

$findings = New-Object System.Collections.Generic.List[object]

foreach ($t in $templates) {
    $sd = $t.nTSecurityDescriptor

    # 1. Verification du proprietaire. Le proprietaire porte un WriteDacl implicite
    #    quelle que soit la DACL.
    try {
        $ownerSid = $sd.GetOwner([System.Security.Principal.SecurityIdentifier]).Value
        $ownerBroad = Test-BroadSid $ownerSid
        if ($ownerBroad) {
            $findings.Add([PSCustomObject]@{
                Template = $t.name; Principal = $ownerBroad; Right = 'OWNER (WriteDacl implicite)'; Scope = 's.o.'
            })
        } else {
            # Un proprietaire non large vaut quand meme d'etre signale s'il n'est
            # pas un groupe de niveau zero.
            $ownerName = try { $sd.GetOwner([System.Security.Principal.NTAccount]).Value } catch { $ownerSid }
            if ($ownerName -notmatch 'Domain Admins|Enterprise Admins|Administrators$|Admins du domaine|Administrateurs de l.entreprise') {
                $findings.Add([PSCustomObject]@{
                    Template = $t.name; Principal = $ownerName; Right = 'OWNER (a revoir : pas niveau zero)'; Scope = 's.o.'
                })
            }
        }
    } catch {
        Write-Warning "Impossible de lire le proprietaire de $($t.name) : $_"
    }

    # 2. Verification de la DACL. Signale les ACE Allow avec droits dangereux.
    foreach ($ace in $sd.Access) {
        if ($ace.AccessControlType -ne 'Allow') { continue }
        if ((([int]$ace.ActiveDirectoryRights) -band ([int]$dangerous)) -eq 0) { continue }

        # Un WriteProperty restreint a un seul attribut n'est dangereux que pour
        # les attributs de securite; non restreint (ObjectType vide) il les touche tous.
        $isWritePropOnly = (([int]$ace.ActiveDirectoryRights) -band
            ([int][System.DirectoryServices.ActiveDirectoryRights]'GenericAll, GenericWrite, WriteDacl, WriteOwner')) -eq 0
        $scoped = ($ace.ObjectType -ne $emptyGuid)
        if ($isWritePropOnly -and $scoped) {
            $scopeNote = "WriteProperty restreint a $($ace.ObjectType) -- a revoir : dangereux seulement si attribut de securite"
        } else {
            $scopeNote = if ($scoped) { "restreint $($ace.ObjectType)" } else { 'toutes proprietes' }
        }

        $sid = try { ([System.Security.Principal.NTAccount]$ace.IdentityReference).Translate(
            [System.Security.Principal.SecurityIdentifier]).Value } catch { $ace.IdentityReference.Value }
        $broad = Test-BroadSid $sid

        $findings.Add([PSCustomObject]@{
            Template  = $t.name
            Principal = if ($broad) { "*** $broad ***" } else { $ace.IdentityReference.Value }
            Right     = $ace.ActiveDirectoryRights.ToString()
            Scope     = $scopeNote
        })
    }
}

if ($findings.Count) {
    Write-Host "Candidats ESC4 -- droits dangereux ou propriete large sur des objets modeles :" -ForegroundColor Yellow
    $findings | Format-Table -AutoSize -Wrap
    Write-Host ""
    Write-Host "Les entrees marquees *** sont des principaux larges bien connus et sont des decouvertes confirmees."
    Write-Host "Les groupes nommes exigent une expansion recursive (Expand-EnrollPrincipals de"
    Write-Host "l'article ESC1, axee sur l'ACE d'ecriture plutot que sur l'inscription) pour"
    Write-Host "confirmer s'ils se resolvent en comptes peu privilegies."
} else {
    Write-Host "Aucun objet modele de cette foret n'a de droits dangereux detenus par des principaux larges." -ForegroundColor Green
}

Deux choses à savoir sur la sortie. D'abord, un principal marqué *** est un SID large bien connu et constitue une découverte confirmée; un groupe nommé n'est une découverte que si son appartenance transitive inclut des comptes peu privilégiés, ce qui est le problème d'expansion récursive que couvre l'article ESC1 et qui ne devient pas plus facile ici. Ensuite, les entrées WriteProperty restreint à <GUID> exigent un jugement : une écriture restreinte à un attribut bénin comme le nom d'affichage n'est pas de l'ESC4, mais une écriture restreinte à msPKI-Certificate-Name-Flag, pKIExtendedKeyUsage, msPKI-Certificate-Application-Policy, msPKI-Enrollment-Flag, msPKI-RA-Signature ou nTSecurityDescriptor en est. Le script fait remonter le GUID plutôt que de deviner; résolvez-le contre le schéma (Get-ADObject sous CN=Schema,CN=Configuration en filtrant sur schemaIDGUID) si vous devez en avoir le cœur net, et traitez toute écriture restreinte non résolue vers un attribut msPKI-* comme une découverte jusqu'à preuve du contraire.

Vérifiez le conteneur, et vérifiez l'héritage

Exécutez dsacls contre le conteneur Certificate Templates lui-même :

dsacls "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local"

Une ACE d'écriture dangereuse ici, avec héritage, est une découverte ESC5 sur le conteneur et un ESC4 de masse sur chaque modèle. Dans la sortie par modèle ci-dessus, une ACE héritée du conteneur apparaît comme le même principal avec le même droit sur plusieurs modèles à la fois; ce motif est le signe révélateur, et le correctif est au conteneur.

Là où la détection manuelle s'effondre

Le même mur de mise à l'échelle multiforêt de l'article ESC1 s'applique, avec une dimension de plus. Pour ESC1, vous développiez les principaux d'inscription. Pour ESC4, vous développez les principaux d'écriture et vous devez aussi lire et raisonner sur la propriété de chaque objet, résoudre les GUID de WriteProperty restreints contre le schéma, et tenir compte des ACE héritées qui prennent leur origine au conteneur ou plus haut. Sur six forêts et deux cent quarante modèles, l'analyse propriétaire plus DACL plus héritage est un travail nettement plus lourd que l'analyse d'inscription, et c'est la même catégorie d'effort d'ingénierie de plusieurs semaines à faire correctement à la main.

Remédier à ESC4

ESC4 se corrige en corrigeant le contrôle d'accès de l'objet, et seulement en corrigeant le contrôle d'accès de l'objet. Aucun changement de configuration ne le ferme, parce que la configuration est justement ce que l'attaquant réécrit. C'est l'image miroir d'ESC1 à ESC3, dont les remédiations étaient toutes de la configuration et aucune de la liste de contrôle d'accès.

Retirez les droits dangereux. Pour chaque modèle où un principal peu privilégié détient le Contrôle total, l'Écriture, WriteDacl, WriteOwner ou une écriture restreinte dangereuse, retirez cette ACE. L'accès en écriture aux objets modèles ne devrait être détenu que par les administrateurs d'ICP et les groupes de niveau zéro qui le détiennent par défaut. Si une équipe a besoin de demander des certificats, accordez l'inscription, pas l'écriture. Si une équipe a réellement besoin de modifier les propriétés non liées à la sécurité d'un modèle, c'est un cas de délégation étroitement restreinte à évaluer au mérite, pas un octroi de Contrôle total, et c'est assez rare pour être l'exception que vous pouvez nommer et justifier.

Corrigez le propriétaire. Réglez le propriétaire de chaque objet modèle à un groupe de niveau zéro, normalement Admins du domaine ou Administrateurs de l'entreprise. C'est l'étape la plus susceptible d'être sautée parce que la propriété est invisible dans la console au quotidien, et c'est l'étape qui ferme le chemin du WriteDacl implicite qu'un nettoyage d'ACE explicites laisse grand ouvert.

# Reinitialise le proprietaire d'un objet modele a Admins du domaine.
$dn = "CN=WebServer-Custom,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local"
$domAdmins = (Get-ADGroup 'Domain Admins').SID
$obj = [ADSI]"LDAP://$dn"
$obj.PSBase.ObjectSecurity.SetOwner($domAdmins)
$obj.PSBase.CommitChanges()

Corrigez le conteneur et l'héritage. Si la découverte est héritée du conteneur Certificate Templates, corrigez-la là. Retirez l'ACE d'écriture trop large du conteneur, et confirmez que les ACE héritées disparaissent des modèles en dessous. Ça ferme la découverte de masse à sa source plutôt qu'un modèle à la fois.

Activez l'audit pour que le prochain ne soit pas silencieux. Le trait qui définit ESC4 est que l'attaque se restaure elle-même, alors un instantané de configuration ne peut pas l'attraper. Ce qui l'attrape, c'est l'audit de la modification. Ajoutez une SACL aux objets modèles (ou au conteneur, hérité vers le bas) auditant les écritures réussies, et activez la sous-catégorie d'audit des modifications de service d'annuaire sur les contrôleurs de domaine. Alors la réécriture et la restauration génèrent toutes deux des événements, et la fenêtre transitoire qui se cache de la détection par instantané devient pleinement visible dans le journal d'audit. C'est un contrôle de détection, pas de prévention; il n'arrête pas ESC4, il s'assure que si une liste de contrôle d'accès que vous avez manquée est utilisée, vous l'apprenez. Corrigez d'abord les listes de contrôle d'accès, puis auditez en filet de sécurité.

Ne vous arrêtez pas à la configuration. Si un modèle est à la fois un ESC1 actif et un ESC4, corrigez la liste de contrôle d'accès et le propriétaire avant, ou en même temps que, la configuration. Retirer la mauvaise configuration ESC1 tout en laissant l'accès en écriture ouvert n'est pas une remédiation; l'attaquant la réintroduit à la demande. L'ordre n'est pas cosmétique.

Ce que le laboratoire a fini par avoir

Pour le modèle de laboratoire ESC4 :

  • L'ACE de Contrôle total d'App-Team-CertAdmins retirée. Le besoin réel de l'équipe était de s'inscrire, alors elle a reçu l'inscription par un groupe dédié et restreint, et rien de plus.
  • Propriétaire réinitialisé à Admins du domaine.
  • Écriture, WriteDacl et WriteOwner sur l'objet confinés à Admins du domaine, Administrateurs de l'entreprise et le groupe des administrateurs d'ICP.
  • Une SACL ajoutée auditant les écritures de propriétés et de DACL réussies, avec l'audit des modifications de service d'annuaire activé sur les contrôleurs de domaine, de sorte que toute modification future du modèle est journalisée.

La configuration du modèle était déjà propre et n'a pas changé. C'est toute la leçon d'ESC4 : la configuration n'a jamais été le problème.

Une configuration réelle : la gestion déléguée de modèles, et où se situe le résidu

La façon la plus nette de voir pourquoi l'accès en écriture est le contrôle qui sous-tend les autres est une délégation qui est légitime, raisonnable, et qui laisse quand même un résidu si vous l'accordez de la façon évidente.

Un grand environnement a une équipe applicative qui possède un modèle de certificat pour son maillage de services interne. Elle a légitimement besoin de l'ajuster : changer la période de validité à mesure que sa politique de rotation évolue, ajuster la construction du SAN à mesure que les services bougent. Ouvrir un billet d'ICP pour chaque changement est une friction réelle, et l'équipe d'ICP ne veut pas être un goulot d'étranglement pour un changement qui ne touche que le modèle de cette équipe. La demande de délégation est donc saine. La question, c'est comment l'accorder.

La façon évidente est le Contrôle total sur l'objet modèle pour le groupe de l'équipe applicative. Ça fonctionne, ça fait disparaître la friction, et c'est ESC4. Le Contrôle total inclut WriteDacl, ce qui veut dire que l'équipe applicative peut s'accorder l'inscription et activer « Fournir dans la demande », ce qui veut dire que tout membre de ce groupe peut s'émettre un certificat d'administrateur de domaine. La délégation voulait dire « gérer la période de validité ». La délégation a livré « escalader jusqu'à administrateur de domaine ». Personne n'a choisi la deuxième chose; elle est venue groupée avec la première.

Passez en revue les options plus sûres. Un WriteProperty restreint aux seuls attributs non liés à la sécurité que l'équipe change réellement est plus proche du bon, mais c'est délicat à exprimer, facile à mal faire, et une inclusion négligente d'un attribut de sécurité rouvre le chemin. La réponse honnête pour la plupart des environnements est que la modification de modèle n'est pas une délégation que vous distribuez du tout. C'est une action de niveau zéro. La friction que ressent l'équipe applicative est réelle, et le bon endroit pour la régler est le processus, une voie rapide pour les demandes de changement de modèle, pas les permissions, un octroi d'écriture permanent sur un objet d'authentification de domaine. Quand une équipe demande un accès en écriture à un modèle, la reformulation est : elle ne veut pas modifier le modèle, elle veut un certificat avec certaines propriétés, et c'est une question de conception d'inscription, pas une question de liste de contrôle d'accès.

Où se situe le résidu, si vous devez déléguer malgré tout : le plus petit octroi qui répond au besoin, sur le moins d'attributs, au plus petit groupe, avec le propriétaire toujours au niveau zéro et l'audit activé. Et même là, le résidu est réel, parce que tout accès en écriture à un modèle est un levier sur l'authentification de domaine, et la personne qui le détient est à l'intérieur de votre frontière de niveau zéro, que l'organigramme le dise ou non.

La réalité à grande échelle

L'article ESC1 couvre la réalité multiforêt, des centaines de modèles, programme continu, et elle tient ici avec un ajout qui rend ESC4 plus difficile à garder fermé que les autres. ESC1 à ESC3 sont des découvertes de configuration, et la configuration est relativement stable; un modèle que vous avez durci tend à rester durci jusqu'à ce que quelqu'un le change délibérément. ESC4 est une découverte de contrôle d'accès, et le contrôle d'accès dérive constamment par le fonctionnement normal de l'annuaire : les appartenances de groupes changent, des délégations s'ajoutent, des modèles sont clonés et héritent de DACL, des migrations réinitialisent des propriétaires, des permissions de conteneur se desserrent pour un projet et ne se resserrent jamais. Une forêt qui est propre d'ESC4 aujourd'hui l'acquiert par l'administration de routine, sans que personne ne touche à un modèle exprès.

Ça fait d'ESC4 la découverte qui a le plus besoin d'une évaluation continue plutôt que ponctuelle. Une révision manuelle trimestrielle des DACL et des propriétaires de modèles sur six forêts, avec expansion récursive des groupes et résolution des GUID de schéma sur chaque écriture restreinte, est exactement le genre d'analyse interobjet, de dérive de permissions, qui ne survit pas au fait d'être faite à la main selon un calendrier. La discipline « ne publier que ce que vous utilisez » aide ici aussi, pour la même raison qu'elle aide partout : moins de modèles publiés veut dire moins d'objets dont vous devez surveiller les DACL et les propriétaires en continu.

Comment Truvald gère ça

Truvald lit le propriétaire et la DACL complète de chaque objet modèle de certificat sur chaque forêt à laquelle il a accès, développe les droits d'écriture et la propriété à travers les groupes imbriqués et les approbations de forêt de la même façon qu'il développe l'inscription pour ESC1, résout les ACE WriteProperty restreintes contre le schéma pour distinguer une écriture d'attribut dangereuse d'une bénigne, et signale les modèles qu'un principal peu privilégié peut réécrire. Il distingue les ACE explicites de celles héritées du conteneur, de sorte qu'une découverte de masse est signalée comme une seule cause racine au conteneur plutôt que cent vingt lignes distinctes à corréler. Il exécute la même analyse pour ESC1, ESC2, ESC3 et ESC5 à ESC16, aux côtés de l'ensemble plus large de vérifications de configuration d'autorité de certification, de listes de contrôle d'accès d'objets ICP et de GPO.

Pour ESC4 en particulier, Truvald signale la découverte que l'outillage par instantané ne peut pas trouver : un modèle configuré proprement en ce moment mais modifiable par quelqu'un qui ne devrait pas pouvoir le modifier, avec le principal précis, le droit précis, l'état de la propriété, et si l'exposition est directe ou héritée. Il signale aussi la combinaison dangereuse dont l'article avertit, un modèle qui passe chaque vérification de configuration tout en portant une liste de contrôle d'accès ouverte, qui est précisément celui qu'une révision par configuration seule classe propre.

La sortie est un rapport d'évaluation écrit. Format Word. Chaque découverte, gravité, modèle précis, droit dangereux ou propriété précis, remédiation recommandée, risque résiduel. Convient à l'équipe technique et au sommaire exécutif que l'équipe technique doit rédiger.

Truvald s'exécute à l'intérieur du réseau du client. Aucun nuage, aucune télémétrie, aucune donnée ne sort. Les configurations de modèles, les listes de contrôle d'accès d'objets et la propriété évaluées comptent parmi les éléments les plus sensibles d'un environnement d'entreprise. Truvald est bâti pour que BrkrOps n'en voie jamais rien.

Évaluation gratuite de 30 jours à truvald.ca. Pour les organisations qui veulent le travail livré comme mandat, avec BrkrOps qui l'exécute dans votre environnement, produit le rapport et accompagne votre équipe dans la feuille de route de remédiation, le forfait Truvald + Évaluation combine la licence à trois jours de consultation.

Références

  • Will Schroeder et Lee Christensen. Certified Pre-Owned: Abusing Active Directory Certificate Services. SpecterOps, juin 2021.
  • Wiki GhostPack Certify, techniques d'escalade, ESC4.
  • Documentation Certipy, attaque de modèle (commande template, sauvegarde et restauration de la configuration vulnérable).
  • Microsoft [MS-CRTD] : Certificate Templates Structure. Les définitions d'attributs qu'un attaquant réécrit et qu'un défenseur protège.
  • Microsoft [MS-ADTS] : Active Directory Technical Specification (propriété d'objet, WriteDacl/ReadControl implicites pour les propriétaires, et le modèle d'évaluation de DACL).
  • Microsoft KB5014754 : Certificate-based authentication changes on Windows domain controllers. Pourquoi le mappage fort contraint une seule forme de charge utile ESC4 et pas les autres.
  • Le guide du défenseur ESC1 (article compagnon). Outillage de détection, expansion des groupes imbriqués, mise à l'échelle.
  • Le guide du défenseur ESC2 (article compagnon). Ne publier que ce que vous utilisez, l'exigence des signatures autorisées.
  • Le guide du défenseur ESC3 (article compagnon). Agents d'inscription, inscription au nom d'autrui, pourquoi un durcissement à un endroit est une exposition à un autre.

À venir dans cette série : ESC5, quand la liste de contrôle d'accès dangereuse n'est pas sur le modèle mais sur les objets autour de lui. L'objet de l'autorité de certification, le compte d'ordinateur AD de l'autorité de certification, les conteneurs Certificate Templates et Enrollment Services, et NTAuthCertificates lui-même. Là où l'accès en écriture aux propres objets d'annuaire de l'ICP donne à un attaquant la capacité d'ajouter une autorité de certification malveillante, de republier un modèle, ou de faire confiance à un certificat qu'il a émis, et pourquoi ESC4 n'était que le coin en forme de modèle d'un problème de contrôle d'accès plus large.

Patrick Mercier écrit Truvald™ pour BrkrOps™ Inc. depuis Edmonton, Alberta. Truvald™ s'exécute entièrement à l'intérieur de votre environnement, aucune télémétrie, aucune composante infonuagique. Une Évaluation gratuite est disponible à truvald.ca/download.

Questions, corrections, ou envie de parler ICP ? admin@brkrops.ca