Du 2D à la profondeur

Un log, c’est du 2D, un simple bout de texte parfois un peu enrichi. Alors que nous passons le plus clair de notre temps à lire des affreuses lignes de logs dans nos terminaux, nos SIEM et autres outils, les attaquants progressent toujours et de plus en plus vite… Pour nous aider dans cette chasse aux vilains, voici un petit billet de blog sur la représentation en graphe de nos données.

Attention, il s’agit d’un essai pour conduire à de nouvelles idées et manières de travailler. C’est le premier article d’une série où je présenterai des exemples des concepts posés ici. J’espère que cet article vous plaira !

Résultat obtenu à partir de logs Zeek

Résultat obtenu à partir de logs Zeek

C’est quoi un graphe d’abord

Un graphe, c’est une structure de données composée de nœuds et d’arêtes qui les relient.

Exemple d’un petit graphe représentant une requête DNS

Exemple d’un petit graphe représentant une requête DNS

Les graphes sont très utiles dans beaucoup de problèmes en informatique : cartographie, jeux vidéo, réseaux sociaux, etc. Tout ce qui doit être représenté par des relations peut être mis en forme de graphe.

Quelques définitions utiles pour la suite de l’article :

  • Relation orientée : une relation avec un sens. Exemple : USER_A -> ADMIN_TO -> SERVER_01.
  • Relation non orientée : une relation sans sens particulier. Exemple : deux machines connectées au même réseau.
  • Propriété : une information attachée à un nœud ou à une relation. Exemple : name, ip, timestamp, port, protocol.
  • Chemin : une suite de nœuds et de relations permettant d’aller d’un point A à un point B.
  • Voisin : un nœud directement connecté à un autre.
  • Degré : nombre de relations connectées à un nœud. Un nœud avec beaucoup de relations est souvent appelé un hub.

Pour en apprendre plus sur le fonctionnement des graphes, je vous renvoie à cette introduction de GeeksforGeeks.

Résoudre des problèmes avec les graphes

En plus d’apporter une manière pratique de représenter les données, les graphes apportent une nouvelle manière de résoudre certains problèmes. Comme par exemple trouver les chemins les plus courts, avec l’algorithme de Dijkstra ou l’algorithme A* :

Représentation d’une résolution de l’algorithme de Dijkstra

Exemple de résolution d’un chemin le plus court avec l’algorithme Dijkstra

Il existe plusieurs bases de données connues pour pouvoir stocker de la donnée au format graphe, celles-ci implémentent un moyen de stockage efficace pour ces objets et proposent certains algorithmes pré-implémentés pour faciliter leurs usages. Dans ces bases de données, on peut citer notamment :

  • ArangoDB : graphes + documents, très polyvalente.
  • Neo4j : référence graphe, utilisée par des outils comme BloodHound.
  • JanusGraph : graphe distribué, pensé pour gros volumes et backends type Cassandra/HBase.

Ces bases de données implémentent une manière efficace de chercher à travers les relations, ce que ne permettent pas des bases de données comme PostgreSQL ou MongoDB. Elles proposent également des algorithmes de graphes pré-implémentés, pour faciliter la recherche dans les bases de données.

Utilisation de la fonction shortestPath de Neo4j

Démonstration de la fonction shortestPath de Neo4j

On peut également citer les algorithmes de PageRank, qui permettent automatiquement de trouver les nœuds les plus importants de notre graphe. Ceux-ci, initialement conçus pour indexer des pages web, nous permettent d’identifier les nœuds centraux et critiques dans un graphe. De même, cet algorithme peut être utilisé pour déterminer les nœuds qui sont très peu référencés et peut-être détecter des anomalies.

Les graphes en cyber

En cyber, tout élément est relationnel et un nœud peut représenter à peu près n’importe quoi : une machine, un utilisateur, une adresse IP, un ticket Kerberos, un processus… Et une arête représente une relation entre deux de ces éléments : une connexion réseau, une authentification, une exécution de processus, un accès fichier.

Si on prend un compte utilisateur par exemple, il a des droits donc des relations avec d’autres objets, il appartient également à des groupes. Ce même compte peut être connecté à certaines machines, et d’autres groupes peuvent avoir des droits sur lui. C’est toutes ces relations qu’on peut représenter en graphe.

Ce qui rend les graphes particulièrement puissants, c’est qu’ils permettent de raisonner sur les chemins. Pas seulement sur ce qu’est un élément, mais sur comment on peut passer de l’un à l’autre ; et c’est précisément ce dont on a besoin pour modéliser le déplacement d’un attaquant dans un SI.

Il existe plusieurs types de graphes, mais ceux qui vont nous intéresser le plus sont les graphes orientés et pondérés. Chaque arête a un sens (une flèche) et porte un poids (une valeur).

Voyons comment appliquer cette structure à la vision de l’attaquant.

Le graphe pour l’attaquant

Alors que nous, défenseurs, réfléchissons en surface d’attaque, en topologie réseau et en vulnérabilités, un attaquant pense graphe (même s’il ne le sait pas).

Pour lui, une étape pourrait peut-être le mener à une deuxième ou à une troisième, jusqu’à l’atteinte de son objectif final.

Prenons un groupe qui s’adonnerait au cybercrime et qui en serait au tout début de son attaque. Chaque nœud représente une action qui pourrait être réalisée par GM (soit Grand Méchant).

Exemple d’un phishing

Les nœuds en rouge représentent les étapes déjà réalisées par l’attaquant.

Graphe initial d’une attaque après phishing

Ci-dessus, GM (Grand Méchant) vient de compromettre le poste de Louise-Marie, comptable dans une entreprise de développement logiciel.

Certaines de ces étapes échoueront probablement, et d’autres lui dévoileront d’autres possibilités au fur et à mesure.

Si GM décide d’avancer un peu en réalisant un scan réseau de son entourage, on pourrait imaginer la progression suivante :

Graphe d’attaque après un scan réseau

Ainsi, en ayant effectué un scan réseau, GM s’ouvre de nouvelles possibilités qui le mèneront plus ou moins rapidement au déploiement de son ransomware.

L’exemple de BloodHound

BloodHound est l’exemple parfait de la pensée graphe appliquée à l’attaque. Cet outil est conçu pour trouver les chemins les plus courts dans un environnement Windows jusqu’à un objectif.

Représentation d’un graphe BloodHound

Ici, la recherche de chemins les plus courts s’applique très bien.

Représentation d’un graphe BloodHound

La pensée en graphe du défenseur

Ainsi que le présentait Microsoft dans cet article, un système à défendre peut se modéliser en graphe. En effet, être capable de modéliser en graphe les possibilités de progression d’un attaquant dans son réseau est intéressant à plus d’un titre :

  • identifier les points sensibles à renforcer ;
  • mettre en place du monitoring aux emplacements stratégiques ;
  • réaliser des campagnes de threat-hunting basées sur le graphe ;
  • comprendre et contenir l’attaquant lors d’une réponse à incident.

Imaginons un graphe capable de représenter toutes les actions redoutées dans notre SI et leurs transitions :

Graphe défensif avec goulots d’étranglement

Dans le graphe imaginé ci-dessus, on a pu identifier deux goulots d’étranglement en vert. Ceux-ci seront des points d’attention particuliers dans notre SI où nous pourrons redoubler de vigilance sur le monitoring et la sécurité.

Application à l’investigation numérique

En DFIR, il s’agit souvent de partir d’un incident cyber et de pivoter à travers toutes les données que nous avons à notre disposition pour trouver où est-ce que l’attaquant a pu aller et ce qu’il a pu faire.

Ce qu’on appelle un pivot n’est rien d’autre que l’exploration des relations d’un événement. En explorant ses voisins, on cherche du contexte, de la compréhension, voire même identifier d’autres actions malveillantes.

Voici un exemple de pivot sur un événement TLS :

Pivot d’investigation entre plusieurs marqueurs

Ci-dessus, durant son investigation, l’analyste identifie un nouveau marqueur : le JA3S correspondant au serveur malveillant. En pivotant sur ce nouveau marqueur, il pourrait identifier de nouveaux serveurs malveillants encore inconnus.


Dans un SIEM classique

Pour démontrer nos propos, on utilise des logs Zeek, solution open-source qui extrait de la donnée utile du réseau, voir mon article sur l’investigation avec Zeek.

Dans ELK, ça donnerait quelque chose comme ça :

Étape 1 - on fait notre recherche sur le client compromis :

FROM logs-zeek.ssl-*
| WHERE destination.ip == "24.234.1.1"
| KEEP source.ip, destination.ip, tls.server.ja3s, @timestamp
| SORT @timestamp DESC

On récupère le JA3S : d41d8cd98f00b204e9800998ecf8427e. On pivote.

Étape 2 - On cherche toutes les IPs qui partagent ce fingerprint TLS, hors de notre C2 déjà connu :

FROM logs-zeek.ssl-*
| WHERE tls.server.ja3s == "d41d8cd98f00b204e9800998ecf8427e"
    AND destination.ip != "24.234.1.1"
| STATS connexions = COUNT(*),
        premiers_contacts = MIN(@timestamp),
        sources = VALUES(source.ip)
    BY destination.ip
| SORT connexions DESC

Dans un outil graphe

Et voilà à quoi ressemblerait un pivot dans un outil de graphe :

Pivot sur un JA3 dans un outil graphe

Ici, un pivot sur le JA3 465396b6226909fc5c37f2a3d0e6dcf4 serait réalisé par un simple clic droit de l’analyste. La coloration des nœuds identifiés comme suspects pourrait également se faire avec un autre clic droit.

Toutes ces relations

Avec la possibilité d’étendre toutes les relations d’un nœud, l’analyste gagne une vitesse extraordinaire. Si nous ne considérions que des logs Zeek, on pourrait :

  • pivoter sur tous les voisins d’une IP ;
  • pivoter sur un nom de domaine pour identifier qui d’autre le requête ;
  • faire le lien graphiquement entre des tickets Kerberos, l’IP source et le service de destination pour identifier les anomalies ;
  • trouver les chemins les plus courts entre une machine compromise et des serveurs à défendre ;
  • tracer toutes les relations en même temps d’un nœud pour étendre la compréhension qu’on a de lui.

Tout cela est assez difficile dans un SIEM classique, mais rendu beaucoup plus évident par le travail en graphe.

Vers une analyse multidimensionnelle

Si nous étions capables de faire entrer toutes les données dans une base de données de graphes, on pourrait faciliter grandement l’analyse de bien des manières.

Nous passerions de logs en deux dimensions à l’exploration, en un clic, de toutes ses relations.

Faciliter le pivot entre la donnée CTI et d’investigation

Il est souvent difficile de faire le lien entre ses données d’investigation et de la donnée OSINT. Aujourd’hui, un analyste qui tombe sur une IP suspecte va ouvrir VirusTotal dans un onglet, Shodan dans un autre, et croiser manuellement avec ses logs dans un troisième. C’est laborieux et ça casse le fil de l’investigation.

Si tout était représenté dans un même graphe, avec pour nœuds les observables communs : IP, domaine, hash, JA3S…, le pivot entre donnée CTI et donnée d’investigation deviendrait naturel. On verrait immédiatement qu’une IP identifiée dans nos logs est également connue dans un rapport de threat intelligence, et on pourrait remonter le fil dans les deux directions sans changer d’outil.

Détection d’anomalies grâce au machine learning

Les algorithmes de machine learning classiques travaillent sur des vecteurs de features : des tableaux de valeurs. Mais certains patterns suspects n’ont pas de sens dans un tableau, ils n’ont de sens que dans une structure relationnelle.

Un compte qui rebondit de machine en machine en dehors de ses habitudes, un hôte qui commence à scanner son voisinage, un processus qui initie des connexions que seul un autre binaire faisait d’habitude… Ces comportements sont invisibles dans une table de logs, mais ils dessinent des formes reconnaissables dans un graphe.

Des algorithmes adaptés aux structures de graphe, les GNN (Graph Neural Networks), permettraient de détecter automatiquement ces patterns sans avoir à les écrire manuellement sous forme de règles. On passerait d’une détection par liste noire à une détection par anomalie structurelle.

Une corrélation système-réseau

Dans les exemples précédents, nous avons surtout parlé de données réseau. Mais une connexion réseau sans contexte système, c’est une moitié d’information.

Imaginons qu’on vienne enrichir notre graphe avec les Event ID 4624 (authentifications Windows) et les Event ID 3 Sysmon (connexions réseau initiées par un processus). On serait alors capables de répondre à des questions autrement impossibles : quel processus, sur quelle machine, a initié cette connexion vers ce C2 ? Ce compte s’est-il authentifié juste avant ce mouvement latéral ? La corrélation qui prenait des heures dans un SIEM deviendrait une traversée de graphe.

Et ce n’est qu’un début ; les usages qu’il reste à imaginer sont probablement les plus intéressants.

En pratique

Plusieurs outils existent pour exploiter des données dans un graphe :

  • Les bases de données orientées graphes pour le stockage (Neo4j, et les autres dont on a parlé précédemment) ;
  • Maltego, outil de visualisation de graphes orienté cyber qui supporte de nombreux inputs différents ;
  • Neo4j Bloom, outil pour visualiser une base Neo4j, très complet mais payant.

Afin d’explorer le sujet de la transformation de données réseau en graphe, je travaille actuellement sur un projet nommé JACG (Just a Cyber Graph), basé sur Neo4j, dont vous avez eu des extraits au long de l’article.

Je développerai plus sur l’implémentation et l’utilisation pratique de cet outil dans les prochains articles.

Conclusion

Les graphes proposent un changement de paradigme, nous sommes habitués à penser en logs, que nous avons parfois du mal à relier ensemble. Les graphes nous permettent de passer à une pensée relationnelle, où chaque élément est connecté, et où les attaques deviennent des chemins.

Concrètement, cela signifie :

  • Détecter des mouvements latéraux invisibles dans un SIEM classique.
  • Visualiser en temps réel les relations entre une IP suspecte, un utilisateur, et un serveur critique.
  • Automatiser la chasse aux menaces avec des algorithmes comme PageRank ou les GNN.

Et demain ? Imaginez un graphe unifié combinant réseau, endpoint, cloud, et CTI, analysé par des modèles d’IA pour détecter des anomalies structurelles.

Rendez-vous dans le prochain article pour le guide pratique : de Zeek à Neo4j.


Références