
Scoring de chemins d'attaque BloodHound sensible à la détection - trouvez l'itinéraire le plus discret vers votre objectif, calibré sur les niveaux audit/EDR/SIEM.

Score des chemins d'attaque Active Directory tenant compte de la détection. DreadHost Research | compagnon de OffsetInspect (PowerShell) et OffsetScan (Rust)
BloodHound (et PlumHound par-dessus) trouve un chemin vers l'objectif. NoiseHound ingère les mêmes données de graphe et re-classe les chemins selon le coût de détection attendu plutôt que le nombre de sauts, afin qu'un opérateur puisse demander « quel est le chemin le plus discret vers Domain Admin » plutôt que simplement « y a-t-il un chemin ».
Nouveau ici ? Commencez par la procédure pas à pas pour opérateurs — un tutoriel pratique, guidé par captures d'écran, qui vous mène de l'installation à une preuve de concept sur BloodHound CE en direct (les scores étant réécrits dans l'interface BloodHound), au moteur DeadAir et au rapport d'écarts de détection pour l'équipe bleue.
État du projet (v1.0) : stable et testé sur des données BloodHound réelles provenant de plusieurs domaines. 30 des 57 arêtes du corpus sont désormais mesurées en laboratoire sur quatre niveaux de détection (audit, Defender for Endpoint, Elastic SIEM et posture MDI) — livrées en tant que profils prêts à l'emploi dans
profiles/, avec une preuve en boucle fermée qu'elles modifient le classement des chemins (docs/VALIDATION.md). Les ~28 arêtes restantes portent encore des estimations d'experts ; le harnais de calibration (noisehound-calibrate,docs/CALIBRATION.md) est ce qui permet de les mesurer, ainsi que votre propre environnement. Traitez les classements non calibrés comme des indications raisonnées, et non comme une vérité absolue.
Réservé aux engagements autorisés. Cet outil évalue les chemins d'attaque pour la planification OPSEC contre des systèmes pour lesquels vous avez une autorisation écrite de tester.
NoiseHound est un projet communautaire indépendant. Il n'est affilié à, approuvé par, ni associé à SpecterOps ou au projet BloodHound ; il consomme le format de données ouvert de BloodHound.
.zip), d'un fichier JSON
brut ou d'un répertoire d'exportations dans un graphe interne. Un format JSON
normalisé {nodes, edges} est également accepté pour l'analyse hors ligne et
les tests. Les arêtes d'escalade AD CS ESC1-8 sont synthétisées au
chargement à partir des faits de modèles de certificats et d'AC que BloodHound
collecte (voir ci-dessous).effective_noise_score (0-100). Lorsque plusieurs droits
connectent la même paire de nœuds, le plus discret est choisi. Les types
d'arêtes absents du corpus ont par défaut un score conservateur (60), afin que
les lacunes échouent en sécurité plutôt que de sous-rapporter. Un profil
d'environnement optionnel ajuste les scores en fonction de la posture de
détection déclarée de la cible (voir ci-dessous).Le bruit d'un chemin n'est délibérément pas une simple somme. Déclencher la même détection deux fois n'est pas deux fois plus bruyant (tri SOC, pas un nombre brut d'événements). NoiseHound utilise :``` path_score = max(edge_scores) * 0.6 + mean(edge_scores) * 0.4
Ceci pondère en faveur de l’étape unique la plus bruyante (une mauvaise étape brûle souvent toute l’opération) tout en tenant compte de l’exposition cumulée. Les pondérations sont configurables (`--max-weight` / `--mean-weight`) afin de pouvoir être ajustées empiriquement une fois que de vraies données de détection sont disponibles depuis un laboratoire APT29/Caldera.
Chaque chemin rapporte également une **probabilité de détection** - la chance de déclencher une alerte corrélée - en combinant l’arête la plus bruyante avec le OU-bruyant cumulé de toutes les arêtes (ajusté via `--correlation`). Elle répond à une question différente de celle du score de bruit : un chemin court mais bruyant peut avoir une probabilité globale *plus faible* d’être détecté qu’un chemin long mais silencieux. Classez avec `--rank-by probability`.
### Moteur à deux niveaux (DeadAir)
Pour les grands graphes, la résolution est confiée à [DeadAir](https://github.com/warpedatom/DeadAir), un moteur Rust compagnon (le niveau OffsetScan-to-OffsetInspect). NoiseHound reste le frontal riche en fonctionnalités - ingestion, corpus, environnement/Sigma, contraintes, rapports - et transmet le graphe préparé au moteur qui le résout, les résultats étant donc identiques dans les deux cas.
- `--engine auto` (par défaut) : DeadAir lorsque son binaire est trouvé *et* que le graphe est grand (>= 5000 nœuds) ; sinon le solveur Python intégré.
- `--engine python` : force le solveur intégré (aucun binaire requis).
- `--engine rust` : force DeadAir (erreur si le binaire est manquant).
DeadAir est localisé via `$NOISEHOUND_DEADAIR`, puis `PATH`, puis la compilation sœur `../deadair/target/{release,debug}/`. Il est 10 à 100 fois plus rapide sur les grands graphes (un graphe de 250 000 nœuds se résout en ~2 s contre ~30 s en Python) tout en produisant des classements identiques octet pour octet. La sortie indique quel moteur a été utilisé.
### Cheminement multi-objectif et sous contraintes
Le bruit, le nombre de sauts et la probabilité de détection tirent dans des directions différentes, aussi `--pareto` renvoie la **frontière de Pareto** - chaque chemin qu’aucun autre ne surclasse sur les trois critères à la fois - au lieu d’imposer un unique gagnant. Et les opérations réelles ont des contraintes : `--avoid NODE` maintient un chemin hors d’un hôte spécifique (un serveur de rebond surveillé par EDR, un honeypot), et `--avoid-edge TYPE` refuse une technique (par ex. `--avoid-edge DCSync`). Les deux sont répétables et relancent la résolution à la volée.```bash
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
La recherche de chemin pondérée de BloodHound n'est pas nouvelle, voici donc le positionnement honnête :
La contribution de NoiseHound est la combinaison : un corpus lisible par machine reliant les arêtes de BloodHound à la télémétrie de détection, un nouveau calcul pondéré par le bruit, présenté sous l'angle de l'OPSEC de l'opérateur (« le chemin le plus silencieux vers DA »), un modèle d'environnement qui s'adapte à la posture déclarée d'une cible, et une boucle de calibration qui transforme les détections en laboratoire en scores mesurés. Le calcul de graphe est un produit de base ; le corpus et le cadrage sont le cœur du sujet. Sa valeur ne vaut que ce que vaut le corpus, c'est pourquoi la calibration et la contribution communautaire sont de première classe - voir ci-dessous.
cd NoiseHound python -m pip install -r requirements.txt # networkx>=3.0
noisehound command on PATH:python -m pip install -e .
Nécessite Python 3.10+.
## Utilisation```bash
# Text summary (default)
python -m noisehound --input export.zip --objective "Domain Admins" --source jdoe
# JSON, for downstream tooling / correlation across the DreadHost suite
python -m noisehound -i export.zip -o "Domain Admins" -s jdoe -f json --out paths.json
# Self-contained HTML report
python -m noisehound -i export.zip -o "Domain Admins" -s jdoe -f html --out report.html
Tout d'abord, vérifiez la cohérence du parser sur votre export (histogrammes + couverture du corpus, sans pathing) - le moyen le plus rapide de valider NoiseHound sur des données réelles :```bash noisehound-inspect -i export.zip
### BloodHound CE / Neo4j en direct
Au lieu d'un zip, pointez `--input` vers la base de données Neo4j que BloodHound CE remplit
et NoiseHound lit le graphe (déjà analysé) directement via Bolt :```bash
pip install 'noisehound[neo4j]'
export NEO4J_PASSWORD=bloodhoundcommunityedition # match your BHCE compose
noisehound-inspect -i bolt://localhost:7687
python -m noisehound -i bolt://localhost:7687 -s jdoe -o "Domain Admins"
Démarrez BloodHound CE (embarque Neo4j sur 7687) avec son compose officiel :
curl -L https://ghst.ly/getbhce | docker compose -f - up.
Testez-le avec les exemples fournis :```bash python -m noisehound -i samples/sample_graph.json -s jdoe -o "Domain Admins" -d CONTOSO.LOCAL python -m noisehound -i samples/sample_bloodhound_ce.zip -s jdoe -o "Domain Admins" python -m noisehound -i samples/sample_adcs_ce.zip -s jdoe -o "Domain Admins" # ADCS ESC1
python -m noisehound -i samples/sample_fullspectrum_ce.zip -s ALICE -o "Domain Admins" -d CONTOSO.LOCAL -k 3
Ce dernier exemple est la démonstration la plus claire de la thèse : le chemin le plus silencieux vers Domain
Admins est le chemin de session à 4 sauts, classé *au-dessus* du chemin RDP à 3 sauts et de l'ADCS
ESC1 à 1 saut - le plus de sauts, le moins de bruit.
L'exemple démontre la valeur essentielle : la route la plus silencieuse est un chemin de session à 4 sauts
(score 19.9), classé *au-dessus* d'un raccourci ForceChangePassword à 2 sauts (36.4).
Moins de sauts ne signifie pas plus silencieux.
### Mode des lacunes de détection pour l'équipe bleue
Le chemin le plus silencieux est celui où la détection est la plus faible, ajoutez donc `--defensive` pour inverser
la sortie pour les défenseurs : il signale les arêtes qui sont silencieuses uniquement parce que leur
télémétrie est désactivée ou absente, associe chacune au contrôle qui la détecterait, et
classe ces contrôles selon la mesure dans laquelle ils augmentent le score du chemin le plus silencieux.```bash
python -m noisehound -i export.zip -s jdoe -o "Domain Admins" --defensive
Sur l'échantillon plein spectre, il constate que le chemin le plus silencieux vers Domain Admins repose sur un accès LSASS non détecté (HasSession, 20 -> 65 si instrumenté) et recommande de déployer Sysmon Event 10 - combler cette seule lacune fait passer le chemin le plus silencieux de 19.9 à 48.4. Consultez docs/ROADMAP.md pour savoir où tout cela et le reste du modèle se dirigent.
Un score de corpus statique ne peut pas savoir si une cible donnée a l'audit d'objets 4662 activé, embarque Sysmon, ou exécute un ITDR comme MDI - pourtant ces éléments modifient énormément le bruit réel d'une arête (DCSync est quasi silencieux sans l'audit 4662 et quasi certainement détecté avec). Plutôt que de prétendre qu'un seul chiffre convient à tous les environnements, déclarez la posture de la cible dans un petit fichier JSON et NoiseHound ajuste les scores de manière transparente par rapport aux annotations de télémétrie du corpus :```json { "name": "CONTOSO.LOCAL-prod", "object_auditing_4662": true, "ds_change_auditing_5136": false, "edr": "MDI", "sysmon": true, "powershell_logging_4104": true, "adjustments": { "HasSession": 65 } }
Les ajustements ne font jamais que *hausser* un score vers un seuil de détection impliqué par la
posture déclarée. `adjustments` sont des surcharges strictes par arête - l'endroit où
enregistrer les valeurs que vous avez calibrées contre votre propre laboratoire. Ceci est fourni par l'opérateur,
non mesuré ; cela ne remplace pas la validation en direct de la phase 2, mais transforme le
corpus statique de "un nombre pour tous les environnements" en "le nombre pour
l'environnement dans lequel vous vous trouvez réellement". Sur l'échantillon fourni, en déclarant le profil
ci-dessus, l'itinéraire le plus discret passe du chemin de session de dump LSASS à un
chemin d'écriture de répertoire - ce qui est la bonne décision une fois la télémétrie hôte active.```bash
python -m noisehound -i samples/sample_graph.json -s jdoe -o "Domain Admins" \
-e samples/env_profile.example.json
Précédence du score : static -> environment-adjusted -> live (Phase 2).
Les profils d'environnement ne valent que ce que valent les chiffres que vous y mettez.
noisehound-calibrate ferme la boucle : exécutez les techniques dans un laboratoire de détection,
consignez ce qui a déclenché, et il émet un profil d'environnement calibré.
Cela a été fait. profiles/ fournit trois profils mesurés à partir d'un
parc Hyper-V Vulnerable-AD réel — audit, EDR (Defender for Endpoint) et niveaux Elastic
SIEM, 30 arêtes — produits par le harnais automatisé (lab/) et cet outil.
Utilisez-les directement, ou mesurez les vôtres :```bash
noisehound -i export.zip -s jdoe -o "Domain Admins" -e profiles/vulnad-hyperv-audit.json
noisehound-calibrate -i lab_detections.json -o env.calibrated.json noisehound -i export.zip -s jdoe -o "Domain Admins" -e env.calibrated.json
Le modèle de score est un estimateur de retrait, honnête quant à la taille de l'échantillon :```
p = detections / runs (detection probability)
lab_score = p * severity_loudness + (1 - p) * residual
w = runs / (runs + smoothing) (confidence in the lab)
calibrated = w * lab_score + (1 - w) * corpus_static
lab_score est le coût de détection attendu - la gravité SOC lorsqu'elle se déclenche,
un petit résidu lorsqu'elle ne se déclenche pas. Le poids w empêche une seule exécution de
écraser le corpus tout en laissant un résultat bien échantillonné dominer. Dans
l'exemple, HasSession passe d'un score statique de 20 à 52 (le lab a détecté le dump
LSASS 4 fois sur 5) tandis que Kerberoast chute de 60 à 34 (il ne s'est jamais déclenché). Utilisez
--merge existing.json pour superposer une nouvelle calibration sur un profil tout en conservant
ses flags de posture, et --smoothing / --residual pour régler le modèle.
Les profils d'environnement et la calibration sont autodéclarés. noisehound-sigma
évalue les détections qu'un défenseur a réellement écrites : pointez-le vers un
ensemble de règles Sigma et il détermine sur quelles arêtes du corpus chaque règle se déclencherait
(en faisant correspondre les identifiants d'événements de télémétrie de l'arête et la technique ATT&CK), puis émet un
profil d'environnement qui élève les arêtes couvertes - un remplacement direct pour --environment.```bash
noisehound-sigma -r ./sigma-rules/ -o env.sigma.json
python -m noisehound -i export.zip -s jdoe -o "Domain Admins" -e env.sigma.json --defensive
La correspondance est volontairement prudente afin de ne jamais masquer une lacune : une règle n'est
comptée que si elle fait référence à un ID d'événement que l'arête génère *et*, lorsque la règle est
étiquetée ATT&CK, sa technique concorde - ainsi une règle DCSync DS-Access (4662) n'est pas
créditée à tort de couvrir les lectures LAPS qui partagent simplement le même ID d'événement. Le rapport
liste à la fois ce que vos règles couvrent et, plus utilement, les arêtes d'attaque qu'aucune règle ne
couvre. Combiné avec `--defensive`, cela répond à la question : « compte tenu des détections que j'ai
déployées, où mon chemin d'attaque le plus discret est-il encore invisible ? »
---
## AD CS ESC1-8
Au chargement, NoiseHound effectue une version ciblée du post-traitement ADCS
de BloodHound, en synthétisant les arêtes d'escalade à partir des faits conservés
sur les modèles/AC. Un droit `Enroll` sur un modèle vulnérable devient une arête
`ADCSESCn` directe allant du principal au groupe Domain Admins du domaine (RID 512),
si bien que l'escalade par certificat est empruntable et notée comme n'importe quelle autre arête :
| Edge | Condition |
|------|-----------|
| `ADCSESC1` | Enrollee-supplies-subject + EKU client-auth, sans approbation/signatures RA |
| `ADCSESC2` | EKU Any-Purpose / SubCA, sans approbation |
| `ADCSESC3` | Modèle d'agent d'inscription + modèle d'authentification sur la même AC |
| `ADCSESC4` | Contrôle d'écriture dangereux sur un modèle publié |
| `ADCSESC5` | Contrôle de l'objet AC ou de l'ordinateur qui l'héberge |
| `ADCSESC6` | L'AC a `EDITF_ATTRIBUTESUBJECTALTNAME2` défini |
| `ADCSESC7` | `ManageCA` / `ManageCertificates` sur l'AC |
| `ADCSESC8` | Point de terminaison d'inscription web HTTP vulnérable (coercition + relais NTLM) |
Essayez : `python -m noisehound -i samples/sample_adcs_ce.zip -s jdoe -o "Domain Admins"`.
Simplifications documentées (par sécurité, elles montrent davantage de chemins) : restrictions
d'inscription au niveau de l'AC ne sont pas modélisées (l'inscription à un modèle est traitée comme
suffisante) ; l'ESC5 couvre l'objet AC et son hôte, pas tous les conteneurs PKI ;
ESC9/10/13 sont hors périmètre. Les arêtes ESC synthétiques déjà présentes dans une
exportation post-traitée sont conservées.
---
## Le corpus (`edge_mappings/`)
Le corpus de télémétrie d'arêtes est la véritable valeur de cet outil ; le code n'est, en comparaison, qu'un
calcul de graphe relativement simple par-dessus. Chaque `edge_mappings/<Edge>.json`
associe un type d'arête BloodHound à sa surface de détection :
- sources de télémétrie attendues (Windows Security Event IDs, Sysmon Event IDs,
réseau, heuristiques EDR/ITDR) avec fiabilité par source et indiquant si
l'audit pertinent est activé par défaut
- un score de bruit statique (0-100)
- technique MITRE, privilège prérequis, primitive d'abus habituelle, et notes```json
{
"edge_type": "DCSync",
"static_noise_score": 85,
"telemetry": [
{"source": "windows_security", "event_id": 4662,
"detail": "Directory Service Access - requires object auditing (default OFF)",
"reliability": "high_if_auditing_enabled", "default_enabled": false},
{"source": "edr_heuristic", "product_class": "MDI",
"detail": "Non-DC hosts issuing DRSGetNCChanges - high fidelity",
"reliability": "high"}
],
"mitre_technique": "T1003.006",
"notes": "Assumes default audit policy (mostly OFF) but high EDR/MDI coverage."
}
Étendre le corpus est là où la majeure partie de l'effort continu doit porter. Ajoutez un nouveau fichier JSON, conservez le schéma (validé au chargement), et il est pris en compte automatiquement. v0.3 inclut 43 types d'arêtes couvrant les abus ACL, Kerberos, la délégation, ADCS ESC1-8, LAPS/gMSA, GPO, les relations d'approbation et les arêtes de droits d'accès qui comptent pour la recherche de chemins.
Les scores sont initialisés à partir de la matrice de bruit DreadHost Red Team Operator Playbook (mappings d'événements Windows/Sysmon, évaluations du bruit des techniques) et de faits standard de détection AD. Ajustez-les en fonction de vos propres données de détection en laboratoire.
profiles/ (niveaux audit / EDR / Elastic) avec une validation
en boucle fermée. Restants : les ~28 autres arêtes (coercition/relais, ADCS ESC2-13, CanRDP),
le niveau d'alertes runtime MDI (la posture fonctionne ; le chemin d'alerte nécessite un DC
bare-metal/Ludus – voir docs/CALIBRATION.md), et un axe de profils d'outillage
sélectionnable (docs/TOOLING_AXIS.md) afin que les scores reflètent les tactiques
prêtes à l'emploi par rapport aux tactiques natives.live_noise_score extrait de la configuration Defender/Sysmon/audit d'une cible réelle,
en réutilisant la logique de frontière de détection d'OffsetInspect. annotate() accepte déjà
un remplacement live_scores ; l'intégration CLI arrive en Phase 2.Le corpus est extensible par la communauté et c'est là que les contributions comptent le plus. Ajouter une arête est un fichier JSON validé au chargement et dans le CI :```bash noisehound-validate # schema + consistency checks over the corpus python -m pytest tests/ -q
Voir [CONTRIBUTING.md](https://github.com/warpedatom/noisehound/blob/HEAD/CONTRIBUTING.md) pour le schéma, le guide de notation et les consignes
relatives aux PR, et [`docs/edge_schema.json`](https://github.com/warpedatom/noisehound/blob/HEAD/docs/edge_schema.json) pour le schéma
formel des edges.
## Calibrage avec un lab
30 edges sont mesurés (voir [`profiles/`](https://github.com/warpedatom/noisehound/blob/HEAD/profiles/)) ; les autres restent des estimations tant que
vous ne les avez pas mesurés, et chaque environnement diffère. [`docs/CALIBRATION.md`](https://github.com/warpedatom/noisehound/blob/HEAD/docs/CALIBRATION.md)
est un playbook complet : topologie de lab, politique d'audit Windows exacte et configuration Sysmon
pour déclencher les event IDs du corpus, un runbook d'exercice par edge, une
couche de réalisme APT29-via-Caldera, et la manière de compiler les résultats dans un profil
calibré. Commencez par `noisehound-calibrate --template -o lab_detections.json`.
Le kit [`lab/`](https://github.com/warpedatom/noisehound/blob/HEAD/lab/) automatise l'instrumentation de détection :
`Enable-Telemetry.ps1` active la politique d'audit, la journalisation des blocs de script, la SACL
DCSync et Sysmon ; `Collect-Detections.ps1` comptabilise ce qui a été déclenché dans une fenêtre. Il
s'appuie sur [GOAD](https://github.com/Orange-Cyberdefense/GOAD) ou
[Vulnerable-AD](https://github.com/WazeHell/vulnerable-AD) (ou votre lab CRTP/CRTO
) pour le domaine vulnérable lui-même plutôt que de les réimplémenter.
## Utilisation responsable
NoiseHound est destiné aux tests de sécurité autorisés, aux exercices de purple team, à l'ingénierie
de détection et à la recherche. Il lit les données BloodHound que vous avez déjà collectées et
calcule des classements ; il n'exécute rien contre une cible. Utilisez-le uniquement lorsque vous
disposez d'une autorisation écrite explicite. Les contributions ne doivent pas inclure
de données spécifiques à une cible ou issues d'engagements réels.
## Tests```bash
python -m pytest tests/ # with pytest
python tests/test_noisehound.py # dependency-light smoke run
noisehound/ engine: schema, corpus, ingest, adcs, annotate, environment, solver, report, cli, calibrate edge_mappings/ the telemetry corpus (one JSON per edge type) - the IP samples/ sample_graph.json, sample_bloodhound_ce.zip, sample_adcs_ce.zip, sample_fullspectrum_ce.zip, env_profile.example.json, lab_detections.example.json, sample_report.html docs/ WALKTHROUGH.md (start here), CYPHER.md, VALIDATION.md, CALIBRATION.md, ROADMAP.md, seed_demo_graph.cypher, seed_showcase_graph.cypher, images/ tests/ unit + end-to-end tests
| Option | Signification |
|---|
--input, -i | BloodHound .zip, .json ou répertoire d'exports |
--source, -s | Principal de départ (jdoe ou un identifiant d'objet) |
--objective, -o | Nœud cible (Domain Admins ou un identifiant d'objet) |
--paths, -k | Nombre de chemins les plus silencieux à renvoyer (défaut 5) |
--format, -f | text (défaut), json ou html |
--defensive | Vue équipe bleue : lacunes de détection sur les chemins les plus silencieux + correctifs |
--rank-by | noise (défaut) ou probability (P d'une alerte corrélée) |
--correlation | Coefficient de corrélation SOC pour P(détecté), 0..1 (défaut 0.5) |
--pareto | Renvoyer la frontière de Pareto sur bruit/sauts/P(detect) |
--engine | auto (défaut), python ou rust (le moteur DeadAir) |
--avoid NODE | Exclure un nœud de tous les chemins (répétable) |
--avoid-edge TYPE | Exclure un type d'arête de tous les chemins (répétable) |
--corpus | Remplacer le répertoire du corpus de mappage des arêtes |
--environment, -e | JSON de posture de cible déclaré par l'opérateur (ajuste les scores) |
--max-weight / --mean-weight | Pondérations de score (doivent totaliser 1.0) |
--default-noise | Score pour les types d'arêtes absents du corpus (défaut 60) |