Summary
KeyCredential node IDs are built from the account's distinguishedName, which changes when an object is renamed or moved between OUs, so re-running the collector after such a change produces duplicate nodes instead of updating the existing ones.
Location
- File(s):
parse.go
- Line(s) / function(s):
ParseResults() at L52-L57 (keyCredentialNodeId construction)
Category
state-management
Severity
medium
Brief justification: Node identity is not stable across collections, breaking BloodHound's id-based idempotent merge; renamed/moved principals yield orphaned duplicate KeyCredential nodes on re-ingest.
Reproduction / Evidence
Verified by code analysis. keyCredentialNodeId is kc.Identifier + "." + distinguishedName (L56), or "Unknown-<n>." + distinguishedName (L54). The distinguishedName is a mutable attribute: renaming the object or moving it to another OU changes its DN. BloodHound merges on node id; with a DN-derived id, a re-collection after a move creates a second node with a new id rather than updating the first. The account's objectSid — already read at L214 for the HasKeyCredential edge — is immutable for the lifetime of the principal and is the stable identifier that should namespace the node id.
Expected Behavior
Node IDs are derived from a stable identifier (the account objectSid plus the credential Identifier) so re-collection updates existing nodes idempotently.
Actual Behavior
Node IDs embed the distinguishedName; any rename or OU move changes the id and causes duplicate nodes on the next ingest.
Root Cause
The node id is namespaced by the mutable distinguishedName instead of the immutable objectSid.
Summary
KeyCredential node IDs are built from the account's
distinguishedName, which changes when an object is renamed or moved between OUs, so re-running the collector after such a change produces duplicate nodes instead of updating the existing ones.Location
parse.goParseResults()at L52-L57 (keyCredentialNodeIdconstruction)Category
state-managementSeverity
mediumBrief justification: Node identity is not stable across collections, breaking BloodHound's id-based idempotent merge; renamed/moved principals yield orphaned duplicate KeyCredential nodes on re-ingest.
Reproduction / Evidence
Verified by code analysis.
keyCredentialNodeIdiskc.Identifier + "." + distinguishedName(L56), or"Unknown-<n>." + distinguishedName(L54). ThedistinguishedNameis a mutable attribute: renaming the object or moving it to another OU changes its DN. BloodHound merges on nodeid; with a DN-derived id, a re-collection after a move creates a second node with a new id rather than updating the first. The account'sobjectSid— already read at L214 for theHasKeyCredentialedge — is immutable for the lifetime of the principal and is the stable identifier that should namespace the node id.Expected Behavior
Node IDs are derived from a stable identifier (the account
objectSidplus the credentialIdentifier) so re-collection updates existing nodes idempotently.Actual Behavior
Node IDs embed the
distinguishedName; any rename or OU move changes the id and causes duplicate nodes on the next ingest.Root Cause
The node id is namespaced by the mutable
distinguishedNameinstead of the immutableobjectSid.