Series · 3 partsActive Directory Architecture & InternalsPart 3 · You are here
  1. 1Multi-Forest Active Directory Architecture: How Forests, Trusts, DNS, and Tiered Services Actually Work
  2. 2Inside Kerberos Delegation: Why Multi-Tier Windows Apps Need It (and Why It Breaks)
  3. 3Active Directory as a Directed Graph: How Security Descriptors and Object ACLs Actually WorkYou are here

Why Active Directory Behaves Like a Directed Graph Database

When an administrator opens Active Directory Users and Computers, the directory appears as a hierarchical tree of Organizational Units (OUs). However, this organizational hierarchy merely dictates where objects reside in LDAP (distinguishedName); it conveys virtually nothing about administrative control relationships.

Under the hood, Active Directory attaches a binary nTSecurityDescriptor ([MS-DTYP §2.4.6]) attribute to every directory object in ntds.dit. Within this security descriptor resides an ordered Discretionary Access Control List (DACL) consisting of Access Control Entries (ACEs). Each ACE explicitly pairs an authenticated identity (Trustee SID) with a specific set of operations (AccessMask and optional ObjectType GUID) over the target object. When directory objects are treated as nodes and non-default ACE entries as directed edges, the directory transforms into a complex directed graph.

In GOAD (Game of Active Directory), BloodHound maps an unbroken eight-hop path in sevenkingdoms.local starting from tywin.lannister (whose cleartext password is recovered from a SYSVOL script) straight to the Forest Root Domain Controller KINGSLANDING$ (192.168.56.10). Deconstructing this path reveals how Windows Security Reference Monitor (SRM) access checks translate into administrative takeover.

nTSecurityDescriptor ANATOMY · BINARY LAYOUT & ACE EVALUATION ENGINE nTSecurityDescriptor OBJECT BINARY ATTRIBUTE [MS-DTYP] Control Flags (16-bit) 0x8004 (SE_DACL_PRESENT | SELF_REL) Owner SID S-1-5-32-544 (Domain Admins) Implicit: WRITE_DAC & READ_CONTROL Group SID & SACL Primary Group SID · Audit ACE Array DACL (Ordered ACE Array) Canonical: Explicit Deny ➔ Allow AceCount: Active ACE Records Each ACE: AccessMask + ObjectType BloodHound Ingestion ➔ ACCESS_ALLOWED_OBJECT_ACE TYPE 0x05 · OBJECT-SPECIFIC ACE ACE Header (4 Bytes) Type: 0x05 · Flags: 0x00 (Explicit) AccessMask (32-bit Bitmask) 0x00000100 (ControlAccess) Extended Right Operation Filter ObjectType GUID (128-bit) 00299570-246d-11d0-a768-00aa006e0529 CN=User-Force-Change-Password Trustee SID (Grantee Account) S-1-5-21-3291916993-...-1113 Identity: tywin.lannister Holds explicit right on target SRM & GRAPH RESOLUTION SECURITY REFERENCE MONITOR Access Check Invocation Caller Token: tywin (SID 1113) Target Object: jaime (SID 1114) Evaluation Engine Token SIDs match Trustee SID? STATUS_SUCCESS (Granted) EFFECTIVE GRAPH EDGE Node: tywin.lannister ──[ForceChangePassword]──► Target: jaime.lannister Primitive: Reset unicodePwd Tool: net rpc / bloodyAD Outcome: Account Compromised
Binary architecture of the Active Directory nTSecurityDescriptor: The Security Reference Monitor (SRM) parses DACL entries (ACEs) against caller token SIDs to establish attack graph edges.

1. Anatomy of an Object-Specific ACE (ACCESS_ALLOWED_OBJECT_ACE)

Unlike NTFS filesystem permissions where access masks simply encode generic read, write, and execute flags, Active Directory objects represent rich schema entities containing dozens of distinct attributes (member, servicePrincipalName, msDS-KeyCredentialLink) and specialized operations (User-Force-Change-Password, DS-Replication-Get-Changes-All).

To provide fine-grained delegation without exhausting bits in a 32-bit mask, Active Directory utilizes Object-Specific ACEs (ACCESS_ALLOWED_OBJECT_ACE, Type 0x05):

  1. AccessMask (ActiveDirectoryRights): Encodes the operational class (0x00000020 = ADS_RIGHT_DS_WRITE_PROP, 0x00000100 = ADS_RIGHT_DS_CONTROL_ACCESS, 0x00040000 = WRITE_DAC, 0x00080000 = WRITE_OWNER, 0x10000000 = GENERIC_ALL).
  2. ObjectType (128-bit GUID): Restricts the AccessMask to a specific attribute schema GUID or Extended Right GUID. When ObjectType is null (GUID_NULL), the permission applies unconditionally to all attributes or operations across the object.

Inspecting non-inherited ACE entries across the eight targets in sevenkingdoms.local using impacket-dacledit:

Principal (Trustee SID)                  RightName            Inherited  Target Object (SID)
---------------------------------------  -------------------  ---------  ---------------------------------------------
TYWIN.LANNISTER (...-1113)               ForceChangePassword  False      JAIME.LANNISTER (...-1114)
JAIME.LANNISTER (...-1114)               GenericWrite         False      JOFFREY.BARATHEON (...-1118)
JOFFREY.BARATHEON (...-1118)             WriteDacl            False      TYRON.LANNISTER (...-1116)
TYRON.LANNISTER (...-1116)               AddSelf              False      SMALL COUNCIL (...-1108)
SMALL COUNCIL (...-1108)                 AddMember            False      DRAGONSTONE (...-1109)
DRAGONSTONE (...-1109)                   WriteOwner           False      KINGSGUARD (...-1110)
KINGSGUARD (...-1110)                    GenericAll           False      STANNIS.BARATHEON (...-1120)
STANNIS.BARATHEON (...-1120)             GenericAll           False      KINGSLANDING.SEVENKINGDOMS.LOCAL (...-1001)
GOAD SEVENKINGDOMS.LOCAL · 8-HOP PRIVILEGE ESCALATION DIRECTED GRAPH STAGE 1 · USER ACCOUNT HOPS (EXTENDED RIGHTS & PROPERTY WRITES) [ENTRY · USER] tywin.lannister RID 1113 · SYSVOL Cleartext Password 1: FCP [USER · RESET] jaime.lannister RID 1114 · Reset net rpc password 2: GW [USER · SHADOW] joffrey.baratheon RID 1118 · PKINIT pywhisker (KeyCred) 3: WD [USER · DACL EDIT] tyron.lannister RID 1116 · FullCtrl impacket-dacledit Hop 4 AddSelf STAGE 2 · SECURITY GROUP NESTING & OBJECT OWNERSHIP REWRITES [GROUP · SELF-ENROLL] SMALL COUNCIL RID 1108 · bloodyAD add self tyron self-adds to group 5: AM [GROUP · NESTED] DRAGONSTONE RID 1109 · member attribute WriteProperty on member 6: WO [GROUP · TAKEOVER] KINGSGUARD RID 1110 · Owner Changed impacket-owneredit Hop 7 GenericAll STAGE 3 · RESOURCE-BASED CONSTRAINED DELEGATION (RBCD) & FOREST TAKEOVER [USER · CONTROLLED PRINCIPAL] stannis.baratheon RID 1120 · GenericAll Target pywhisker (Shadow Cred) ➔ TGT Hop 8: GenericAll RBCD · S4U2Proxy [TIER-0 FOREST ROOT DOMAIN CONTROLLER] KINGSLANDING.SEVENKINGDOMS.LOCAL 192.168.56.10 · RID 1001 · DC01$ (Computer) DCSync (krbtgt + DA Hashes) ➔ FULL FOREST ROOT COMPROMISE
The full 8-hop privilege escalation graph in sevenkingdoms.local: from entry point tywin.lannister through password resets, shadow credentials, DACL rewrites, group nesting, and object ownership to Tier-0 Forest Root takeover via RBCD and DCSync.

2. Extended Rights: Operations Beyond Attribute Writes

Hop 1 demonstrates why the two-part ACE exists. A user password cannot be updated by setting a cleartext string over LDAP: unicodePwd requires specialized protocol wrapping. Resetting a password without knowing the existing credential is an Extended Right (ADS_RIGHT_DS_CONTROL_ACCESS / 0x100):

$ impacket-dacledit -action read -principal tywin.lannister -target jaime.lannister \
    'sevenkingdoms.local/samwell.tarly:Heartsbane' -dc-ip 192.168.56.10
[*] ACE[0] info:
[*]   ACE Type                  : ACCESS_ALLOWED_OBJECT_ACE
[*]   AccessMask                : ControlAccess (0x00000100)
[*]   ObjectType                : User-Force-Change-Password (00299570-246d-11d0-a768-00aa006e0529)

When AccessMask contains 0x00000100, the KDC resolves ObjectType against CN=Extended-Rights,CN=Configuration:

  • 00299570-246d-11d0-a768-00aa006e0529 (User-Force-Change-Password): Permits tywin.lannister to issue RPC SamrSetInformationUser (net rpc password) or LDAPS password modifications (bloodyAD set password) against jaime.lannister.
  • 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2 (DS-Replication-Get-Changes-All): When granted on the domain partition root (DC=sevenkingdoms,DC=local), permits non-DC accounts to invoke DRSGetNCChanges (DCSync), retrieving password hashes forest-wide.

3. Property Writes: Scoped Rights vs. GenericWrite

Hops 2, 4, and 5 illustrate property write delegation:

  1. AddMember (SMALL COUNCIL to DRAGONSTONE): AccessMask = 0x20 (WriteProperty) filtered by ObjectType = bf9679c0-0de6-11d0-a285-00aa003049e2 (the schema GUID for the member attribute). Any member of SMALL COUNCIL can append arbitrary user accounts into DRAGONSTONE.
  2. AddSelf (tyron.lannister to SMALL COUNCIL): Combines ADS_RIGHT_DS_SELF (0x08) with the Self-Membership GUID (bf9679c0-0de6-11d0-a285-00aa003049e2). tyron.lannister is restricted strictly to appending his own identity to the group.
  3. GenericWrite (jaime.lannister to joffrey.baratheon): Grants WriteProperty (0x20) across all non-protected attributes without GUID filtering. Crucially, GenericWrite lacks ControlAccess (0x100), so jaime cannot directly force-reset joffrey’s password.

Instead, GenericWrite enables three modern takeover primitives:

  • Shadow Credentials (msDS-KeyCredentialLink): Injecting an X.509 certificate public key structure into msDS-KeyCredentialLink (5b47d60f-6090-40b2-9f37-2a4de88f3063) allows acquiring a Kerberos TGT and recovering the user’s NT hash via PKINIT without altering the user’s password.
  • Targeted Kerberoasting (servicePrincipalName): Writing a fake SPN to servicePrincipalName, requesting a service ticket, and cracking the password hash offline.
  • Computer RBCD (msDS-AllowedToActOnBehalfOfOtherIdentity): When the target is a computer, writing a machine account SID into msDS-AllowedToActOnBehalfOfOtherIdentity enables S4U2Proxy execution as Administrator.

4. Security Descriptor Manipulation: WriteDacl, WriteOwner, and GenericAll

Hops 3, 6, 7, and 8 target the nTSecurityDescriptor binary structure itself:

  • WriteDacl (0x00040000, joffrey to tyron): Grants permission to rewrite the target’s DACL, permitting the injection of an explicit GenericAll ACE.
  • WriteOwner (0x00080000, DRAGONSTONE to KINGSGUARD): Permits changing the object’s Owner SID. Under Windows SRM architecture ([MS-DTYP]), an object’s Owner inherently possesses implicit READ_CONTROL and WRITE_DAC (0x00040000) permissions, even if the DACL explicitly denies access. Consequently, WriteOwner is mathematically equivalent to WriteDacl.
  • GenericAll (0x10000000, stannis to KINGSLANDING$): Encompasses all standard, specific, and generic rights. Holding GenericAll on a Domain Controller computer object enables immediate takeover via RBCD or Shadow Credentials, resulting in DCSync domain dominance.

Telemetry and Defensive Engineering

Directory Access Control Telemetry Matrix

Event IDLog ProviderEvent NameTechnical Context
5136Directory Service ChangesA Directory Object Was ModifiedLogs attribute modifications including nTSecurityDescriptor, msDS-KeyCredentialLink, and msDS-AllowedToActOnBehalfOfOtherIdentity.
4724SecurityPassword Reset AttemptTracks execution of User-Force-Change-Password extended right.
4728 / 4732 / 4756SecurityMember Added to Security GroupLogs additions to global, domain local, or universal security groups.
4738SecurityUser Account ModifiedMonitors additions of servicePrincipalName (Targeted Kerberoasting).
// KQL Query: Detect Unauthorized Modifications to Active Directory Security Descriptors
SecurityEvent
| where EventID == 5136
| where AttributeLDAPDisplayName in~ ("nTSecurityDescriptor", "msDS-AllowedToActOnBehalfOfOtherIdentity", "msDS-KeyCredentialLink")
| project TimeGenerated, Computer, SubjectUserName, ObjectDN, AttributeLDAPDisplayName, OperationType

Remediation and Boundary Enforcement

  1. Enforce AdminSDHolder Protection: High-privilege objects (Domain Admins, Enterprise Admins) must remain protected by AdminSDHolder. The SDProp process runs every 60 minutes to overwrite unauthorized DACL alterations.
  2. Audit Tier-0 Object Ownership: Ensure object ownership (Owner SID) on Tier-0 accounts and groups is strictly assigned to Domain Admins or Enterprise Admins.
  3. Eliminate Transitive Permission Chains: Use BloodHound to audit and prune transitive permission chains linking Tier-2 or Tier-1 identities to Tier-0 assets.

Summary of ACL Primitives

Right NameBinary Representation (AccessMask + ObjectType)GOAD Exploit StepExecution PrimitiveDetection Telemetry
ForceChangePassword0x00000100 (ControlAccess) + User-Force-Change-Passwordtywin → jaimeOverwrite unicodePwd via RPC/SAMR or LDAPS.Event ID 4724; restrict helpdesk reset delegation.
GenericWrite0x00020028 (ReadControl | Self | WriteProp)jaime → joffreyShadow Credentials (msDS-KeyCredentialLink) or RBCD.Event ID 5136 on sensitive LDAP attributes.
AddSelf / AddMember0x00000008 / 0x00000020 + member GUIDtyron → SMALL COUNCIL → DRAGONSTONEInject user DN into group member attribute.Event IDs 4728, 4732, 4756.
WriteDacl0x00040000 (WRITE_DAC)joffrey → tyronInject GenericAll ACE into target DACL.Event ID 5136 on nTSecurityDescriptor.
WriteOwner0x00080000 (WRITE_OWNER)DRAGONSTONE → KINGSGUARDChange Owner SID to gain implicit WRITE_DAC.Event ID 5136; audit non-default object owners.
GenericAll0x10000000 (GENERIC_ALL)stannis → KINGSLANDING$Full object control; RBCD or Shadow Credentials on DC.Protect Tier-0 with AdminSDHolder / SDProp.

Evidence and limits

The ACE dumps, GUIDs, and eight-hop examples come from the GOAD directory used in Part 3. impacket-dacledit output was compared with the corresponding BloodHound edges and the Microsoft definitions of ACCESS_ALLOWED_OBJECT_ACE, access masks, and object-specific rights. The article therefore treats BloodHound as a path finder and the directory security descriptor as the authority.

This is not an exhaustive Windows authorization model. Inheritance, deny ACE ordering, AdminSDHolder, protected objects, validated writes, and application-specific checks can change effective access. A graph edge is a testable hypothesis until the precise ACE and target behavior are verified; the examples here were verified only in the isolated GOAD lab.

References

Sources & freshness

How current is this note?

Sources checkedNovember 3, 2026

The latest source-review, content-update, or publication date is shown.

ReviewPublic sources reviewed

Primary public records were checked. Environment-specific behavior remains outside the claim unless separately reproduced.