Repository navigation
fix(ad-tombstone): collect ownership/DACL reanimation paths, also fix queries import #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "All Tombstones", | ||
| "description": "Every deleted object GhostHound enumerated from CN=Deleted Objects. Click a node for its Recycle Bin state (is_recycled, group_membership_recoverable), its pre-deletion location (lastknownparent), and its owner (ownersid).", | ||
| "query": "MATCH (t) WHERE t:GhostHound_TombstoneUser OR t:GhostHound_TombstoneComputer OR t:GhostHound_TombstoneGroup RETURN t" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "Find All Recoverable Tombstones (Deleted State)", | ||
| "description": "Finds all AD tombstones that are in the full-fidelity 'Deleted' state (Recycle Bin) and can be recovered with their previous group memberships.", | ||
| "query": "MATCH (n) WHERE n.group_membership_recoverable = true AND (n:GhostHound_TombstoneUser OR n:GhostHound_TombstoneComputer OR n:GhostHound_TombstoneGroup) RETURN n" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "Tombstones Deleted From a Sensitive Location", | ||
| "description": "Tombstones whose lastknownparent was a sensitive container (Domain Controllers, Tier 0, ADCS), or that privilege_zones.cypher already tagged highvalue. Reanimating one of these restores an identity that used to live in a privileged part of the directory.", | ||
| "query": "MATCH (t) WHERE (t:GhostHound_TombstoneUser OR t:GhostHound_TombstoneComputer OR t:GhostHound_TombstoneGroup) AND (t.highvalue = true OR t.lastknownparent CONTAINS 'OU=Domain Controllers' OR t.lastknownparent CONTAINS 'OU=Tier 0' OR t.lastknownparent CONTAINS 'OU=ADCS') RETURN t" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "Who Can Reanimate Which Tombstone", | ||
| "description": "Every reanimation capability GhostHound found, as a graph. Click an edge to read `source` (the strongest mechanism: reanimate_right, owner, write_dac or write_owner) and `sources` (all of them). The reanimator endpoint is a GhostHound placeholder node until bridge_shadow_nodes.cypher is run -- see docs/adr/0006.", | ||
| "query": "MATCH p=()-[:GhostHound_CanReanimate]->(t) WHERE t:GhostHound_TombstoneUser OR t:GhostHound_TombstoneComputer OR t:GhostHound_TombstoneGroup RETURN p" | ||
| } |
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The queries present all four mechanisms as operationally equivalent forms of CanReanimate. If the object-only lab cases fail, the relationship must be split into separately named capabilities and these descriptions and queries must be updated accordingly. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "Reanimation Paths That Need an ACL Rewrite First", | ||
| "description": "Principals that can reanimate a tombstone only by first rewriting its security descriptor -- they own it, or hold WRITE_DAC/WRITE_OWNER on it, but do not hold the Reanimate-Tombstones right. Operationally distinct from a formal grant: the ACL/owner rewrite is an extra, auditable step. This is the class of path ADR-0007 added.", | ||
| "query": "MATCH p=()-[r:GhostHound_CanReanimate]->(t) WHERE NOT 'reanimate_right' IN r.sources RETURN p" | ||
| } |
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The queries present all four mechanisms as operationally equivalent forms of CanReanimate. If the object-only lab cases fail, the relationship must be split into separately named capabilities and these descriptions and queries must be updated accordingly. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "Reanimation Paths Already Formally Granted", | ||
| "description": "Principals holding the Reanimate-Tombstones control access right (domain-wide from the naming-context root, or via an ACE on the tombstone naming that GUID explicitly). No ACL rewrite needed -- usable as-is.", | ||
| "query": "MATCH p=()-[r:GhostHound_CanReanimate]->(t) WHERE 'reanimate_right' IN r.sources RETURN p" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "Reanimation Capability Held by Non-Tier-Zero Principals", | ||
| "description": "The interesting subset: reanimation capability held by principals that are NOT already Tier Zero. A Domain Admin being able to reanimate a tombstone is expected; a regular user being able to is an escalation path. Requires bridge_shadow_nodes.cypher -- the bridge is what resolves GhostHound's placeholders to the real AD principals, which is where the Tier Zero tag lives.", | ||
| "query": "MATCH p=(real)<-[:GhostHound_SameAs]-(shadow)-[:GhostHound_CanReanimate]->(t) WHERE NOT real:Tag_Tier_Zero RETURN p" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "Reanimation Paths for a Specific Principal (edit the name)", | ||
| "description": "Everything one principal can reanimate, starting from its real AD node and crossing the GhostHound bridge. Edit 'JOHN@' to the principal you're operating as -- BloodHound names are uppercase SAMACCOUNTNAME@DOMAIN.TLD. Requires bridge_shadow_nodes.cypher.", | ||
| "query": "MATCH p=(real)<-[:GhostHound_SameAs]-(shadow)-[:GhostHound_CanReanimate]->(t) WHERE real.name STARTS WITH 'JOHN@' RETURN p" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "Path to Domain Admins via Reanimation", | ||
| "description": "Finds the full attack path from a principal with the Reanimate-Tombstones right, through the tombstone, into the real Domain Admins node: GhostHound_CanReanimate (reanimator -> tombstone), GhostHound_WasMemberOf (tombstone -> placeholder), and the GhostHound_SameAs bridge (placeholder -> the real Group node). Requires bridge_shadow_nodes.cypher to have been run against Neo4j after import (see docs/adr/0006) -- without it, GhostHound_WasMemberOf's target is an unbridged placeholder and this query returns nothing.", | ||
| "query": "MATCH p=(reanimator)-[:GhostHound_CanReanimate]->(n:GhostHound_TombstoneUser)-[:GhostHound_WasMemberOf]->(shadow)-[:GhostHound_SameAs]->(g:Group) WHERE g.objectid ENDS WITH '-512' RETURN p" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "Reanimation Into Any Tier Zero Group", | ||
| "description": "Generalizes the Domain Admins query to every Tier Zero principal: tombstones whose preserved memberOf leads into one, so restoring the tombstone hands that membership back. Only meaningful while group_membership_recoverable = true on the tombstone (the AD Recycle Bin 'Deleted' state, ADR-0004). Requires bridge_shadow_nodes.cypher.", | ||
| "query": "MATCH p=(t)-[:GhostHound_WasMemberOf]->(shadow)-[:GhostHound_SameAs]->(g:Tag_Tier_Zero) RETURN p" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "Shortest Path Into Tier Zero That Goes Through a Tombstone", | ||
| "description": "Pathfinding across the whole graph, constrained to routes that actually traverse a reanimation edge -- escalations that exist only because of a tombstone. Mixes GhostHound edges with BloodHound's own AD edges, which is the point of registering the kinds as traversable (ADR-0004). Requires bridge_shadow_nodes.cypher; can be slow on a large graph.", | ||
| "query": "MATCH p=shortestPath((u:User)-[*1..8]->(g:Tag_Tier_Zero)) WHERE NOT u:Tag_Tier_Zero AND ANY(r IN relationships(p) WHERE type(r) = 'GhostHound_CanReanimate') RETURN p" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "Tombstones With No Reanimation Path Found", | ||
| "description": "Tombstones nobody was found able to reanimate. Read this as a possible collection gap before a clean bill of health: if ownersid is also null, the object's nTSecurityDescriptor was unreadable and its real owner/DACL paths are simply unknown.", | ||
| "query": "MATCH (t) WHERE (t:GhostHound_TombstoneUser OR t:GhostHound_TombstoneComputer OR t:GhostHound_TombstoneGroup) AND NOT ()-[:GhostHound_CanReanimate]->(t) RETURN t" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "Tombstones Whose Security Descriptor Was Unreadable", | ||
| "description": "Tombstones whose nTSecurityDescriptor came back empty, so no owner- or DACL-based reanimation path could be derived. Usually means no READ_CONTROL for the collecting account; re-run GhostHound as a more privileged principal to close the gap. The collector also reports this count on stderr during the run.", | ||
| "query": "MATCH (t) WHERE (t:GhostHound_TombstoneUser OR t:GhostHound_TombstoneComputer OR t:GhostHound_TombstoneGroup) AND t.ownersid IS NULL RETURN t" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "name": "Unbridged GhostHound Placeholder Nodes", | ||
| "description": "Placeholder nodes that GhostHound edges point at but which aren't linked to a real AD node yet, so any path through them dead-ends. Expected immediately after an import: run bridge_shadow_nodes.cypher via cypher-shell and re-check. Anything still listed afterwards is a principal BloodHound has no node for at all -- a SID from a trusted domain, or an orphaned SID left in the DACL.", | ||
| "query": "MATCH (s:GhostHound) WHERE NOT s:GhostHound_TombstoneUser AND NOT s:GhostHound_TombstoneComputer AND NOT s:GhostHound_TombstoneGroup AND NOT (s)-[:GhostHound_SameAs]->() RETURN s" | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Readme present all four mechanisms as operationally equivalent forms of CanReanimate. If the object-only lab cases fail, the relationship must be split into separately named capabilities and these descriptions and queries must be updated accordingly.