Files
dashboard/RAPPORT_FINAL.md
SOC Analyst 9de59f5681 docs: mise à jour README + RAPPORT_FINAL v2.0.0
- README: version 2.0.0, TCP fingerprinting, clustering multi-métriques,
  structure fichiers mise à jour, endpoints API complets, workflows
  d'investigation, services techniques, tables ClickHouse, thème

- RAPPORT_FINAL: section v2.0.0 avec détails TCP fingerprinting
  (20 signatures, scoring multi-signal, résultats production),
  clustering (21 features, K-means++, PCA, résultats 289 bots),
  redesign visualisation (tableau de bord + graphe), points d'attention

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-19 08:54:06 +01:00

13 KiB
Raw Blame History

Rapport Final — SOC Bot Detector Dashboard

Date : 2026-03-16
Commits : 8032eba (corrections bugs), d4c3512 (améliorations)


1. Corrections de bugs (commit 8032eba)

Bug Cause Correction
Brute Force > Attaquants : IPs affichées en ::ffff:x.x.x.x Pas de normalisation IPv6 dans la requête SQL replaceRegexpAll(toString(src_ip), '^::ffff:', '') ajouté
Brute Force > Cibles : lien "Voir détails" → page inexistante Navigation vers /investigation/{host} (hostname) au lieu d'une IP Remplacement par composant TargetRow avec expansion inline des attaquants par host
Header Fingerprint : tableau de détail toujours vide Frontend lisait data.ips au lieu de data.items Correction de la clé
Heatmap Temporelle : "Top hosts ciblés" vide Frontend lisait data.hosts + erreur de type TypeScript { hosts: TopHost[] } Correction clé data.items + type annotation
Botnets Distribués : clic sur ligne n'affiche rien Frontend lisait data.countries au lieu de data.items Correction de la clé
Rotation & Persistance : IPs en ::ffff: + historique toujours vide Pas de normalisation + frontend lisait data.history au lieu de data.ja4_history Normalisation SQL + correction de la clé
TCP Spoofing : spoofings détectés sans corrélation TTL Filtre Python-side sur données déjà filtrées TTL=3031 Filtre SQL spoof_only déplacé côté ClickHouse

2. Améliorations implémentées (commit d4c3512)

J — Synthèse IP multi-sources

  • Endpoint : GET /api/investigation/{ip}/summary
  • Widget : IPActivitySummary en haut de toute page d'investigation IP
  • Données : ML + bruteforce + TCP spoofing + JA4 rotation + persistance + timeline 24h
  • Score de risque : 0100 (jauge SVG colorée)
  • Résultat : Contexte immédiat en un coup d'œil, sans naviguer entre 6 pages

I — Comparaison baseline 24h/hier

  • Endpoint : GET /api/metrics/baseline
  • Widget : 3 cartes (Détections 24h, IPs uniques, CRITICAL) avec variation ▲▼ en %
  • Impact : Détecte immédiatement les pics anormaux (ex: +246% détections observé)

M-4 — Score de sophistication adversaire

  • Endpoint : GET /api/rotation/sophistication
  • Calcul : JOIN 3 tables (rotation JA4 × 10 + récurrence × 20 + log(bruteforce+1) × 5)
  • Tiers : APT-like / Advanced / Automated / Basic
  • Résultat : Prioritisation des enquêtes les plus urgentes

M-7 — Chasse proactive (low-and-slow)

  • Endpoint : GET /api/rotation/proactive-hunt
  • Logique : IPs récurrentes avec abs(anomaly_score) < 0.5 — volent sous le radar ML
  • Évaluation : "Évadeur potentiel" (ratio récurrence/score > 10) ou "Persistant modéré"
  • Impact : Détecte les botnets slow-and-low que le modèle ML sous-score

M-2 — Badge réputation ASN inline

  • Modification : LEFT JOIN asn_reputation dans la requête des détections
  • Badge : Rouge (malicious/bot/scanner), orange (proxy/vpn), vert (human)
  • Limitation : La table asn_reputation contient 36 ASN français (ISPs légitimes) — les ASNs malveillants connus ne sont pas encore catalogués

3. Tests exhaustifs Playwright

Page Résultat Notes
Dashboard principal Baseline ▲ +246.5% détections, ▲ +11.6% IPs, = CRITICAL
Détections Badge ASN affiché (null pour ASNs hors table reputation)
Investigation IP (162.55.94.175) Score 38, TCP Spoof TTL 59, JA4 Rotation 9 sig
Rotation > Sophistication APT-like: 162.55.94.175 (score 100), 46.4.81.149 (score 100)
Rotation > Chasse proactive IPs avec scores négatifs sous le radar ML
Brute Force > Attaquants IPs propres (sans ::ffff:)
Brute Force > Cibles Expansion inline des attaquants par host
Header Fingerprint Tableau détail rempli au clic
Heatmap Temporelle Top hosts ciblés affiché
Botnets Distribués Détail pays au clic
TCP Spoofing Filtre spoof_only fonctionnel

4. Points problématiques et axes d'amélioration

🔴 Critiques

  1. Table asn_reputation incomplète — 36 entrées uniquement (ISPs français). Pour être utile, elle devrait contenir les ASNs des datacenters, VPS, proxies connus (OVH, DigitalOcean, AWS, Linode, etc.). Source suggérée : AbuseIPDB ASN database, IPInfo, Maxmind.

  2. Chasse proactive — scores négatifsview_ip_recurrence.worst_score stocke le score brut (peut être négatif). La condition abs(score) < 0.5 capture des IPs HIGH avec score -0.18 qui sont déjà détectées par ML. Il faudrait filtrer par niveau de menace (worst_threat_level NOT IN ('HIGH', 'CRITICAL')) pour vraiment identifier les cas sous le radar.

  3. Pas de persistance des classifications SOC — Les classifications manuelles (/api/analysis/classify) ne persistent que pendant la session si la table classifications n'est pas créée. Un script d'init DB serait utile.

🟡 Moyens

  1. Score de sophistication biaised — Les IPs avec forte rotation JA4 mais recurrence=0 dans view_ip_recurrence (non présentes) atteignent quand même score 100. Les données des deux vues ne sont pas toujours cohérentes sur la même période temporelle.

  2. Timeline 24h dans la synthèse IP — Utilise window_start >= now() - INTERVAL 24 HOUR sur agg_host_ip_ja4_1h. Si les données ont moins de 24h d'historique, le graphique sera partiel/vide. Adapter la fenêtre dynamiquement selon les données disponibles.

  3. Heatmap Temporelle — Les données de agg_host_ip_ja4_1h ne sont agrégées que pour les dernières 24h dans l'endpoint. Un sélecteur de plage temporelle (7j, 30j) permettrait de détecter les patterns de vagues cycliques (botnets hebdomadaires).

  4. Pas d'export des résultats — Les analystes SOC ne peuvent pas exporter les listes d'IPs malveillantes (CSV, STIX). Un endpoint GET /api/rotation/sophistication?format=csv serait utile pour l'IOC sharing.

🟢 Mineurs

  1. "Investiguer" dans le RotationView ne transmet pas le contexte — Un clic sur "Investiguer" depuis l'onglet Sophistication navigue vers /investigation/{ip} sans pré-charger le contexte de l'onglet source. Un ?source=sophistication&score=100 dans l'URL permettrait d'afficher un bandeau contextuel.

  2. Onglets non présents dans la sidebar — Les 7 dashboards d'analyse avancée ne sont pas organisés en sous-menus. Avec l'ajout des onglets Sophistication et Chasse proactive dans Rotation, la sidebar commence à être longue.

  3. Badge ASN ne trie pas les détections — Il n'y a pas encore de filtre "Afficher seulement les ASNs malveillants" dans les détections.


5. Architecture — points de vigilance

  • Le SPA catch-all (/{full_path:path}) doit rester le dernier router dans main.py
  • L'endpoint /api/investigation/{ip}/summary utilise le préfixe /api/investigation — compatible avec la route SPA /investigation/:ip (distinct)
  • Les scores négatifs dans anomaly_score et worst_score sont normaux — toujours utiliser abs() pour l'affichage
  • Les IPv6-mapped (::ffff:x.x.x.x) sont présentes dans toutes les vues agrégées — systématiquement utiliser replaceRegexpAll(toString(src_ip), '^::ffff:', '')

Rapport — v2.0.0 : TCP Fingerprinting Multi-Signal + Clustering IPs

Date : 2026-03-19
Commit : e2db8ca


1. TCP Fingerprinting OS amélioré

Problème initial

L'ancien tcp_spoofing.py utilisait uniquement le TTL avec 3 plages grossières (≤64 = Linux, ≤128 = Windows, sinon = Network). Résultat : faux positifs, aucune détection de bots scanners.

Solution implémentée

backend/services/tcp_fingerprint.py — 20 signatures OS, scoring multi-signal :

Signal Poids Source ClickHouse
TTL initial (estimé) 40% tcp_ttl_raw
MSS 30% tcp_mss_raw
Fenêtre TCP 20% tcp_win_raw
Scale factor 10% tcp_scale_raw

Détections validées en production :

  • Masscan : win=5808, mss=1452, scale=4, TTL 4857 → confiance 97%
  • Googlebot : stack Windows détecté avec UA Android → spoof confirmé
  • Bot-tool : risk_score += 30 (vs +15 pour spoof simple)

MSS → chemin réseau :

  • 1460 → Ethernet standard
  • 1452 → PPPoE / DSL (Masscan pattern)
  • 14201452 → VPN probable
  • < 1420 → Tunnel / double-encapsulation

Fichiers modifiés :

  • backend/services/tcp_fingerprint.py (nouveau)
  • backend/routes/tcp_spoofing.py (réécriture complète — queries agg_host_ip_ja4_1h)
  • backend/routes/investigation_summary.py (utilise le service tcp_fingerprint)
  • frontend/src/components/TcpSpoofingView.tsx (nouvelles colonnes MSS/scale/confiance, graphique distribution MSS)

2. Clustering IPs multi-métriques

Problème initial

La première version du clustering utilisait uniquement des règles sur les propriétés TCP. L'utilisateur a demandé d'utiliser l'ensemble des métriques disponibles.

Solution implémentée

backend/services/clustering_engine.py — K-means++ pur Python (sans dépendances ML) :

21 features normalisées [0,1] :

Catégorie Features
Stack TCP (4) TTL initial, MSS, scale, fenêtre
Anomalie ML (6) score, vélocité, fuzzing, headless, POST ratio, IP-ID zéro
TLS/Protocole (5) ALPN mismatch, ALPN absent, efficacité H2, ordre headers, UA-CH mismatch
Navigateur (1) score navigateur moderne (normalisé /50)
Temporel (3) entropie, diversité JA4 (log1p), UA rotatif
Comportement (2) ratio assets, ratio accès direct

Algorithme :

K-means++   : init O(k·n), n_init=3, meilleure inertie retenue
Power iter  : X^T(Xv) trick, O(n·d) par iter — pas de matrice n×n
Déflation   : Hotelling pour PC2 après extraction PC1

Stratégie d'échantillonnage : ORDER BY avg(abs(anomaly_score)) DESC → les bots (score élevé) sont inclus en priorité, même si leurs hits individuels sont faibles (cas Masscan).

Résultats en production (k=14, 3000 IPs) :

  • 289 bots confirmés : clusters UA rotatif + UA-CH mismatch (cloud providers : Microsoft, Google, Akamai)
  • 655 IPs suspects : anomalie ML modérée ou UA-CH incohérent
  • ASN dominants : MICROSOFT-CORP-MSN-AS-BLOCK, GOOGLE-CLOUD-PLATFORM, OVH, AMAZON
  • Temps de calcul : ~59 secondes (Python pur, 3000 points × 21 features)

3. Visualisation clustering redesignée

Problème initial

La première version utilisait des bulles ReactFlow positionnées par PCA. L'utilisateur a signalé : "l'affichage du graphe est illisible".

Solution implémentée

Deux vues distinctes, accessibles par onglets :

⊞ Tableau de bord (défaut — toujours lisible)

  • Grille de cartes groupées par niveau de risque
  • Bots & Menaces confirmées (rouge) → Suspects (orange) → Légitimes (vert)
  • Chaque carte : label + IP count + hits + badge CRITIQUE/ÉLEVÉ/MODÉRÉ/SAIN + 4 mini-barres + stack TCP + pays + ASN

⬡ Graphe de relations

  • Nœuds-cartes ReactFlow (220px — texte entièrement lisible)
  • Colonnes par niveau de menace (disposition déterministe, pas PCA)
  • Arêtes colorées : orange=similaire, gris=distant, animé=très fort
  • Légende intégrée, minimap, contrôles zoom

Sidebar de détail

  • RadarChart comportemental (10 axes)
  • Toutes les métriques avec barres de progression
  • Liste des IPs avec badges menace/pays
  • Export Copier IPs + ⬇ CSV
  • Intégrée dans le flux flex (ne bloque plus la barre de contrôle)

Fichiers modifiés :

  • backend/routes/clustering.py (réécriture complète)
  • backend/services/clustering_engine.py (nouveau — seuils calibrés sur données réelles)
  • frontend/src/components/ClusteringView.tsx (réécriture complète)
  • frontend/src/App.tsx (route /clustering + nav "🔬 Clustering IPs")

4. Points d'attention

Performances

  • K-means++ sur 3000 × 21 : 59s (acceptable — pas de cache implémenté)
  • Le cache mémoire du drill-down (_cache["cluster_ips"]) est volatile : rechargement = recalcul
  • Pour améliorer : cache Redis ou TTL 5 min avec functools.lru_cache

Calibration des seuils

Les seuils de name_cluster() et risk_score_from_centroid() sont calibrés sur les données observées :

  • anomaly_score en production : plage 0.20.35 (pas 01 comme attendu)
  • Score normalisé affiché : min(1, score / 0.5) pour étirer la plage utile
  • UA-CH mismatch = 1.0 sur les clusters bot = signal très fort (cloud providers simulant un navigateur)

Données manquantes dans le LEFT JOIN

Certaines IPs n'apparaissent pas dans ml_detected_anomalies (score=0, fuzz=0). Ce sont les IPs légitimes non détectées par le modèle ML. Elles forment naturellement les clusters "Trafic Légitime".

Fuzzing_index = 100% dans beaucoup de clusters

Après analyse : le fuzzing_index log-normalisé dépasse souvent le seuil de 100% car les valeurs brutes sont très variables (0 à 229+). Ce n'est pas un bug — c'est la nature du trafic web moderne (beaucoup de requêtes avec des paths variés).