
Detection-aware BloodHound attack-path scoring - the quietest route to your objective, calibrated across five detection tiers (audit/EDR/Elastic/MDI/WDAC) plus Azure.

Detection-aware Active Directory attack-path scoring. DreadHost Research | companion to OffsetInspect (PowerShell) and OffsetScan (Rust)
BloodHound (and PlumHound on top of it) finds a path to the objective. NoiseHound ingests the same graph data and re-ranks paths by expected detection cost instead of hop count, so an operator can ask "what is the quietest way to Domain Admin" instead of just "what is a way".
New here? The Operator Walkthrough is the fastest way to see what this does - a hands-on, screenshot-driven tour from install to a live BloodHound CE proof of concept (scores written back into the UI), the DeadAir engine, and the blue-team detection-gap report.
Project status (v1.2.0): stable and tested on real BloodHound data across multiple domains. 37 of the 77 corpus edges are lab-measured across five on-prem detection tiers (Windows audit, Defender for Endpoint, Elastic SIEM, Defender for Identity runtime alerts, and WDAC audit) plus a measured Azure/Entra tier - shipped as six drop-in profiles in
profiles/, with closed-loop proof they change path rankings (docs/VALIDATION.md). The corpus includes 13 Azure/Entra edges (docs/AZURE.md), ingestible straight from AzureHound output. Un-measured on-prem and all Azure edges carry expert estimates; the calibration harness (noisehound-calibrate) is how they, and your own environment, get measured. Treat uncalibrated rankings as well-reasoned guidance, not ground truth.
For authorized engagements only. This tool scores attack paths for OPSEC planning against systems you have written permission to test.
NoiseHound is an independent community project. It is not affiliated with, endorsed by, or associated with SpecterOps or the BloodHound project; it consumes BloodHound's open data format.
.zip), a raw JSON file, or a directory
of exports into an internal graph. A normalised {nodes, edges} JSON format
is also accepted for offline analysis and tests. AD CS ESC1-8 escalation
edges are synthesised at load time from the certificate-template and CA facts
BloodHound collects (see below).effective_noise_score (0-100). Where several rights connect the same pair
of nodes, the quietest is chosen. Edge types absent from the corpus default
to a conservative score (60) so gaps fail safe rather than under-reporting.
An optional environment profile adjusts scores for the target's declared
detection posture (see below).Path noise is deliberately not a simple sum. Tripping the same detection twice is not twice as loud (SOC triage, not raw event count). NoiseHound uses:
path_score = max(edge_scores) * 0.6 + mean(edge_scores) * 0.4
This weights toward the loudest single step (one bad step often burns the whole
op) while still accounting for cumulative exposure. The weights are configurable
(--max-weight / --mean-weight) so they can be tuned empirically once real
detection data is available from an APT29/Caldera lab.
Every path also reports a detection probability - the chance it trips a
correlated alert - blending the loudest edge with the cumulative noisy-OR of all
edges (tuned by --correlation). It answers a different question than the noise
score: a short but loud path can have a lower overall probability of being
caught than a long but quiet one. Rank by it with --rank-by probability.
For large graphs the solve is dispatched to DeadAir, a companion Rust engine (the OffsetScan-to-OffsetInspect tier). NoiseHound stays the feature-rich frontend - ingestion, corpus, environment/Sigma, constraints, reporting - and hands the prepared graph to whichever engine solves it, so results are identical either way.
--engine auto (default): DeadAir when its binary is found and the graph is
large (>= 5000 nodes); the built-in Python solver otherwise.--engine python: force the built-in solver (no binary needed).--engine rust: force DeadAir (errors if the binary is missing).DeadAir is found via $NOISEHOUND_DEADAIR, then PATH, then the sibling
../deadair/target/{release,debug}/ build. It is 10-100x faster on large graphs
(a 250k-node graph solves in ~2s vs ~30s in Python) while producing byte-identical
rankings. The output records which engine ran.
Noise, hop count, and detection probability pull in different directions, so
--pareto returns the Pareto frontier - every path that no other beats on
all three at once - instead of forcing a single winner. And real operations
have constraints: --avoid NODE keeps a path off a specific host (an
EDR-monitored jump box, a honeypot), and --avoid-edge TYPE refuses a technique
(e.g. --avoid-edge DCSync). Both are repeatable and re-solve on the fly.
python -m noisehound -i export.zip -s jdoe -o "Domain Admins" --pareto
python -m noisehound -i export.zip -s jdoe -o "Domain Admins" --avoid FILESERVER01 --avoid-edge HasSession
Weighted BloodHound pathfinding is not new, so here is the honest positioning: