Fonctionnement de BloodHound
Dans ce chapitre, nous allons voir quels sont les principaux éléments et technologies qui font tourner BloodHound. Il est toujours intéressant de savoir comment nos outils fonctionnement, même dans les grandes lignes. Cela pour pouvoir les adapter à nos besoins, modifier leur configuration ou même les réparer.
I. L'architecture de BloodHound
Pour fonctionner, BloodHound utilise différentes briques technologiques :
- Collecteurs de données : SharpHound et AzureHound. Ils sont écrits en C# et en PowerShell et sont chargés de récolter les données de l'Active Directory ou du tenant Azure qui seront ensuite importées dans la base Neo4j. Il existe aussi un collecteur non officiel en Python (bloodhound-python).
- Back-end Neo4j : il s'agit de la graph database, c'est dans cette base de données que seront importées les données de notre Active Directory.
- Back-end postgreSQL : cette base de données plus classique est utilisée par BloodHound pour la gestion des utilisateurs, des permissions, des métadonnées concernant les imports et de la configuration de l’application BloodHound elle-même.
- Front-end BloodHound : écrits en Go et utilise Sigma.js, un framework JavaScript porté sur la représentation graphique des relations/graphes, encore une fois. Elle fournit une représentation visuelle des informations de l'Active Directory collectées et permet d'interagir avec ces informations.
Voici une tentative de représentation graphique de ces différents éléments :
Le tout consomme assez peu de ressources et peut très facilement tourner sur un poste utilisateur standard. Attention pour les "gros" Active Directory et les requêtes générant des milliers de résultats, il peut y avoir quelques coups de chaud. Mais, rien d'insurmontable et des garde-fous sont intégrés à l'application, qui demande validation de l'affichage visuel des données lorsqu'une requête va retourner trop de nodes et de edges.
II. Les principales interconnexions
Sur le schéma ci-dessus, nous pouvons voir les principales interconnexions.
BloodHound va communiquer avec deux bases de données : PostgreSQL et Neo4J. Pour une utilisation avancée, il arrivera aussi que l'utilisateur (vous) accède à la console de navigation Neo4J, elle permet parfois d'avoir une représentation différente des données comme sous forme de liste, en export CSV, etc.
Également, vous remarquerez qu'il n'y a jamais d'interaction entre BloodHound et l'Active Directory ou Azure (Entra ID). Les échanges de données (de l'AD/Entra ID vers BloodHound) passeront toujours par un poste Windows sur lequel sera exécuté un collecteur. On parle d'analyse à froid, dans le sens où une fois que les données sont importées dans BloodHound puis analysées, nous travaillons sur des données gelées qui représentent l'état de l'Active Directory au moment de l'export. Ne vous attendez pas à voir vos chemins d'attaque disparaitre de BloodHound dès qu'un correctif aura été appliqué (suppression de l'appartenance d'un utilisateur à un groupe par exemple).
Pour voir l'effet de nos modifications sur les objets de l'Active Directory, il faudra refaire un export, puis un ré-import dans BloodHound.
Il faut également savoir qu'en fonction des options de collecte que vous utiliserez, SharpHound peut aller à se connecter sur tous les postes du domaine afin de récupérer les sessions des utilisateurs. Il s'agit d'une opération sans risque et parfois longue qui lui permettra de vous indiquer, par exemple, que tel administrateur ou utilisateur sensible a une session sur ce poste particulièrement exposé, ou déjà compromis.
En dehors de ces exports/imports, les principaux flux qui seront utilisés sont des flux web (HTTP/HTTPS) entre le poste de l'analyste et le service web de BloodHound : l'interface utilisateur.
III. Le langage Cypher
Également, il faut savoir que Neo4j utilise non pas le langage SQL, mais le Cypher. Son fonctionnement natif est déjà très orienté sur les relations/chemins entre objets, les graphes et la représentation visuelle des données. Le langage en soi est assez différent des langages de base de données standards, ce qui peut freiner son apprentissage, par exemple :
MATCH p=shortestPath((n)-[*1..]->(m:Computer {unconstraineddelegation: true})) WHERE NOT n=m AND n.owned = true RETURN p
Ce n'est pas très explicite, mais en apprenant à manier BloodHound et en comprenant son fonctionnement, ces requêtes deviendront plus claires. Nous apprendrons notamment à modifier des requêtes existantes pour les adapter à nos besoins.