Series · 4 partsInternal Network TriagePart 4 · You are here
- 1Kerberoasting Triage: Most Service Tickets Are a Waste of Your Time
- 2SMB Signing Is On. That Closed One Edge, Not the Graph.
- 3Delegation Triage: You Were Taught the Rare One
- 4BloodHound Path Triage: The Shortest Path Is Usually the One That Expires FirstYou are here
BloodHound path triage in 60 seconds
BloodHound turns Active Directory relationships into a graph. Users, computers, groups, sessions, and permissions become nodes and edges; path queries then show how one controlled identity might reach a privileged target. The graph is a map of collected facts, not proof that every route still works.
Everyone runs the same query first. Mark something owned, ask for the shortest path to Domain Admins, screenshot the graph. It is a good query. It is also the one most likely to hand you a path that does not work, and the reason is not bad data.
BloodHound draws every relationship as an identical arrow. The facts behind those arrows have wildly different shelf lives.
Three lifetimes, one picture
ACL edges — GenericWrite, WriteDacl, Owns, AddMember, ForceChangePassword.
These come from object security descriptors. They were set during some project years ago and
nobody has touched them since. If the edge was there when you collected, it is there now,
and it will be there next week.
Membership and rights edges — MemberOf, AdminTo, CanRDP, CanPSRemote. Stable
over an engagement, but they do change. A group gets cleaned up, a machine gets rebuilt.
Session edges — HasSession. A user was logged on to that host at the moment of
collection. That is all it means. They may have logged off ninety seconds later. On a
workstation fleet, a session graph collected in the morning is substantially fiction by the
afternoon.
All three render as the same arrow. Shortest-path then does what shortest-path does: it
finds the fewest hops. Session edges are shortcuts — a single HasSession can jump you
straight from a workstation to a domain admin’s credentials — so the algorithm reaches for
them preferentially. The shortest path is systematically biased toward the most perishable
facts in the dataset.
That is why the first path so often fails on contact, and why the fifth one you try — longer, duller, entirely made of ACLs — is the one that works every time you run it.
Ask for the durable path instead
The fix is not a better tool. It is constraining the query to edge types that do not decay.
// Deterministic path: ACL edges only. Longer, and it works tomorrow too.
MATCH p = shortestPath(
(n {owned: true})-[:Owns|GenericAll|GenericWrite|WriteDacl|WriteOwner|
AddMember|ForceChangePassword|AllExtendedRights*1..]->
(g:Group {name: 'DOMAIN [email protected]'})
)
RETURN p
Then ask the opposite question — not “how do I reach DA” but “what does this one thing I already hold actually reach”:
MATCH (u:User {name: '[email protected]'})
MATCH p = (u)-[:MemberOf|AdminTo|GenericAll|GenericWrite|WriteDacl*1..3]->(c:Computer)
RETURN DISTINCT c.name, length(p) AS hops
ORDER BY hops
And make the perishable edges explicit rather than silently trusting them:
// Which of my paths depend on someone still being logged in?
MATCH p = shortestPath((n {owned: true})-[*1..]->(g:Group {name: 'DOMAIN [email protected]'}))
WHERE any(r IN relationships(p) WHERE type(r) = 'HasSession')
RETURN p
If every path you have runs through a session, you do not have a reliable finding yet. You have a screenshot.
The edges worth more than they look
Two relationship types are consistently under-weighted because they are one hop from something boring:
ReadLAPSPassword reads as an information disclosure. It is local administrator on that
machine, permanently, for anyone holding it — and LAPS deployments hand the read right to
broader groups than people remember approving.
ReadGMSAPassword is the same shape. Worth noting because moving service accounts to gMSA
is the correct remediation for Kerberoasting — but it relocates the problem into a DACL
rather than deleting it. If the read right is over-granted, the gMSA is a service account
with a 240-byte machine-generated password that a helpdesk group can simply retrieve.
Both are ACL-class edges. They are permanent, and they do not appear in the shortest path because they usually need one more hop.
What the graph cannot see
Worth stating plainly in a report, because the picture invites the opposite conclusion: the absence of a path is not evidence that none exists.
BloodHound models Active Directory. It does not model a shared local administrator password that was never in AD, credentials sitting in a deployment script on a share, an application that authenticates to a database with a domain account, or trust that lives in a CI system. Those are real edges. They are simply not the kind of fact the collector reads.
The corollary matters on the offensive side too: when the graph shows nothing, the answer is usually that the next edge is outside AD, not that there is no next edge.
The same graph approach, pointed at cloud trust instead of directory trust, is the cloud IAM note — the deliverable is the path there too, and the edges are trust relationships rather than ACLs.
Evidence matrix
| Signal | What it proves | Negative control | Defender verification |
|---|---|---|---|
| ACL or ownership edge survives two collections | The path is durable enough to treat as a standing control failure | Remove session edges from the graph and confirm the route still exists | Re-collect object DACLs and calculate effective rights through nested groups |
| Session edge has a recent timestamp and reachable host | A transient credential opportunity existed at collection time | Log the user off and collect again; the edge should disappear | Correlate the edge with logon telemetry and the collection window |
| Group membership connects a low-tier principal to a durable right | The route depends on identity governance, not an endpoint accident | Remove the test membership and recompute the path | Review nested membership, group owner, purpose, and expiry |
| The named target is reachable from the tester’s network position | The graph path is operational rather than merely theoretical | Block the management path and verify the final hop fails | Validate firewall, tiering, and administrative logon restrictions independently of AD data |
The pattern I keep seeing
The ACL edges that end up mattering are almost never granted to an individual. They arrive as a group that was given broad rights over an OU for a project, and kept them afterwards because revoking access is nobody’s deliverable. The nesting then does the rest: the group gets added to another group for an unrelated reason, and a permission scoped to one team quietly becomes reachable by three.
Which is why the finding people write — “path from user X to Domain Admin exists” — closes badly. Remove one membership and that specific path disappears while the permission that made it possible stays exactly where it was.
What to hand the defenders
Report the edge, not the path. The path is a demonstration. The finding is the specific permission on the specific object, and who holds it through what nesting. That is the thing that can be revoked.
Separate durable from perishable. If a path depended on a live session, say so. It is still worth reporting — it shows a credential exposure — but it is a different remediation from a DACL, and conflating them gets both handled badly.
Give them the reverse query. Defenders get more value from “what can this group reach” than from any attack path, because it maps to something they can act on: a group they own, and a list of what it currently touches.
The graph is not the finding. It is the argument for the finding.
How current is this note?
The latest source-review, content-update, or publication date is shown.
The author completed a technical review. This does not, by itself, claim lab reproduction.
