Active Directory · graph representation
Real Active Directory attack surface is principal-mediated and ACL-dense
In a real Active Directory environment, a host-to-host graph retained 0 of 321 movement edges. A representation built around users, groups, computers, ACLs, and delegation recovered 1,940 traversable edges and five of six documented attack-path families.
Many cybersecurity systems represent a network as machines connected to machines.
For some security problems, this is a useful abstraction. For Active Directory privilege escalation, it can erase the attack surface.
We tested graph representations, collection strategies, and synthetic scaling methods against live and collected Active Directory data. Several results pointed in the same direction.
- Privilege escalation was principally mediated by identities and permissions rather than direct host adjacency.
- An ordinary authenticated user could collect the directory ACL surface we needed.
- The density of abusable permissions predicted reachable exposure better than the number of principals.
- Attempts to create statistical power by randomly enlarging a real directory destroyed the path structure we were trying to measure.
We also found two collector interoperability bugs that silently removed critical edges or attributes while returning apparently valid output.
Together, these results suggest that the representation layer is not a cosmetic choice. For Active Directory, it determines whether an attack surface exists at all.
01A host-centric graph retained zero movement edges
We started with a third-party GOADv2 BloodHound collection whose documented ground truth contains full-compromise paths.
The first representation was intentionally simple. Computers were nodes. Movement between computers became edges. This is the graph shape used naturally by many network-security models:
It produced five disconnected machines. Of 321 candidate movement edges, the adapter retained zero. The crown was reported unreachable.
A model operating over this graph could produce a perfectly reasonable answer: no attack path found. That answer would be wrong because the graph had removed the mechanisms by which the attack occurs.
Active Directory escalation commonly travels through principals. A user is a member of a group. A group has an abusable ACE over another object. An account owns another object or can modify its DACL. A service principal exposes a credential attack surface. A principal has administrative control over a computer. Delegation changes what another identity can impersonate.
The path crosses machines, but the edges are often permissions and identity relationships rather than machine-to-machine links.
We therefore changed the representation. Users, groups, and computers became nodes. Abusable ACEs, MemberOf, SPN, delegation, and related control relationships became edges.
Nothing about the underlying directory had changed. Only the representation had.
This is a particularly dangerous modeling failure because it is quiet. A parser crash is visible. An empty attack graph can look like good news.
For defensive systems, false absence is often worse than explicit failure.
02Privilege was not the directory-collection bottleneck
We next asked how much privilege was required to recover this graph.
One collection ran as an ordinary domain user. Another ran as SYSTEM on a domain controller.
For the directory-side ACL surface, the result was the same. Both reached the same completeness tier. Both parsed 686 nTSecurityDescriptor ACEs.
The reason is an important property of default Active Directory visibility. Authenticated users can commonly read security descriptors through READ_CONTROL. The relationships needed to map a large part of the directory privilege-escalation surface are therefore not necessarily privileged secrets.
The data that remained unavailable was mostly host-side: information such as local administrator membership and logged-on sessions. Running as SYSTEM on the domain controller did not magically make that remote host information available either.
This changes the threat model. If an ordinary authenticated identity can map the directory's ACL structure, defenders should generally reason as though an attacker with a foothold can map it too.
There was one operational complication. az vm run-command executes as SYSTEM and could not perform the secondary logon we needed for the low-privilege measurement. We therefore launched the unprivileged collection through a scheduled task and granted SeBatchLogonRight.
This detail matters because collection privilege can otherwise be confounded with the privilege of the mechanism used to start the collector.
03ACL density predicts exposure better than directory size
Once the graph representation was correct, we asked a different question: what characteristics of a directory predict how much attack surface is reachable?
A naive answer is size. More users, groups, and computers produce more objects and therefore perhaps more opportunities for escalation.
Our generated-directory experiment did not support that view.
We created 500 directories, ranging from 500 to 10,000 principals, and computed reachable attack surface with an independent breadth-first-search oracle. The strongest relationships were structural.
Within this experiment, adding identities was not what made directories substantially more exposed. Adding exploitable relationships did.
The real GOAD-Light graph illustrates why this can happen. It contained only 212 nodes but 1,009 edges.
| relationship | count |
|---|---|
| GenericAll | 415 |
| WriteDacl | 310 |
| WriteOwner | 309 |
| GenericWrite | 237 |
| Owns | 185 |
| MemberOf | 98 |
| AdminTo | 16 |
These counts are not intended as a node-edge total decomposition; they describe the observed relationship inventory used in our analysis.
What matters is the shape. A relatively small directory can contain a dense privilege structure. Conversely, a much larger directory with tightly controlled ACLs may contain fewer useful escalation paths.
This suggests that "directory size" is an unusually weak proxy for the security property defenders actually care about.
04You cannot get a larger real directory by adding random ACLs
The next problem was statistical. Our real lab provided only 18 footholds for one attack-path ranking experiment. That leads to wide confidence intervals.
An obvious solution is to make the graph bigger. We tried. It failed.
Starting from the real graph, we generated larger versions by adding random ACL structure. Before enlargement, a choke-point exposure method achieved \(\text{AUC} = 0.99\). After random enlargement, \(\text{AUC} = 0.52\). Essentially chance.
The explanation became clear when we inspected the resulting graphs. Real escalation paths concentrate through structure: privileged groups, highly consequential ACLs, delegation relationships, and bottlenecks. Random edges add connectivity. They do not necessarily add realistic choke points.
So the synthetic graph became larger while becoming less like the object the benchmark was meant to measure.
More graph ≠ more realistic evidence.
We then tried a cleaner experiment. Instead of perturbing the real graph, we built an independent tiered generator with explicit choke points. The resulting graph had 519 nodes and 305 footholds.
This gave us far more observations. It did not give us the missing result. Every method remained near \(\text{AUC} \approx 0.55\).
The larger sample reduced one problem while changing the data-generating process. That distinction is fundamental.
Synthetic data can increase the number of examples. It cannot automatically increase the amount of evidence about a phenomenon that exists because of real organizational structure.
For this particular attack-path ranking question, our conclusion is therefore inconvenient but useful: statistical power requires a larger real directory.
05Two collector bugs silently removed the graph
Representation is only useful if the collector preserves the information required to construct it. We found two failures where it did not.
Neither initially looked like a catastrophic collector failure. That made them worse.
bloodhound-python: missing crown and AdminTo semantics
The first involved schema differences in bloodhound-python. The collected objects omitted the isdc and highvalue fields expected by our adapter and emitted the legacy LocalAdmins key.
Without a remapping step, two things happened. Every AdminTo edge disappeared. And the crown resolver could no longer identify the domain controllers correctly.
The collection itself existed. The downstream graph silently lost important semantics.
We repaired the DC identification by using the PrimaryGroupSID: domain controllers are tagged through a SID ending in -516.
The important lesson is not this particular compatibility patch. It is that graph completeness cannot be inferred from collector completion.
PowerShell SearchRequest: one string instead of two attributes
The second bug was smaller and even easier to miss.
A PowerShell SearchRequest interface accepted params string[]. The caller passed @() in a way that bound the requested fields into one space-joined element.
The intent was to request two LDAP attributes: defaultNamingContext and dnsHostName. The actual request asked Active Directory for one attribute named "defaultNamingContext dnsHostName".
Active Directory accepted the request. It returned entries. Those entries contained zero requested attributes. No LDAP error announced that the schema query was wrong.
We reproduced the behavior directly against the live domain controller: @() requested one attribute; an explicit [string[]] cast requested two.
Because nothing fails loudly, a collector built on this binding runs to completion and consistently reports no attack paths.
06Representation is part of correctness
Machine-learning work often treats representation as a modeling decision. Use one graph schema instead of another. Add node features. Collapse edge types. Compare architectures.
In this environment, the first representation decision occurred before the model had anything meaningful to learn. A host-centric graph said there were no paths. A principal-mediated graph exposed nearly two thousand traversable relationships.
No downstream algorithm can recover an edge that preprocessing has defined out of existence.
This suggests a useful ordering for attack-path systems:
- Establish the security semantics.
- Demonstrate that the representation preserves known paths.
- Validate collection completeness.
- Only then evaluate the predictive model.
Reversing that order can produce excellent metrics on the wrong graph.
07Attackers should be assumed to have the map
The collection experiment also changes how we think about information advantage.
If the escalation-relevant ACL structure is readable by an ordinary authenticated user, then keeping the graph itself out of a defensive product does not meaningfully hide the attack surface. An attacker with a valid foothold may be able to reconstruct much of it.
The defensive advantage has to come from doing more with the map: identifying consequential structure earlier, testing interventions, shrinking abusable relationships, and predicting how exposure changes.
Security through graph obscurity is not a strong assumption.
08Directory hygiene is an edge problem
The density experiment points to a related operational conclusion. Organizations do not become dangerous merely because they contain many identities. They become dangerous when those identities accumulate exploitable relationships.
GenericAll. WriteDacl. WriteOwner. Delegation. Nested group membership. Administrative control.
These edges compose. And composition is what turns an innocuous-looking permission into a path to a crown.
This is why we think ACL and delegation density are more promising exposure variables than raw directory size. The goal is not necessarily to make the directory smaller. It is to make privilege relationships sparse, deliberate, and difficult to compose.
09Limitations
The density analysis uses 500 generated directories with an independent oracle. Our detailed real-directory measurements come from one forest, so we have not yet established the same density relationship across a large population of production Active Directory environments.
The unprivileged collection experiment was performed in one domain. READ_CONTROL and related defaults can be hardened, so equal collection completeness should not be assumed in every organization.
Our negative synthetic-scaling result covers two strategies: random enlargement of a real graph and an independent tiered generator. It does not establish that realistic synthetic directory generation is impossible. It establishes that the two methods we tested did not preserve the statistical structure needed for this task.
10What's next
We want to test the density relationship against multiple independent real directories.
We are also preparing the principal-mediated graph schema and the collector remappings for publication, so the assumptions between raw directory data and attack-path analysis are explicit.
Longer term, the relevant unit of analysis is not the machine. It is the principal and the authority that principal can exercise.
A user controls a group. A group controls an object. An object changes a credential. A credential reaches a host. A host reaches the crown.
The machines still matter. But the path is made of permissions.