# 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=30–31 | 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 :** 0–100 (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égatifs** — `view_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 4. **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. 5. **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. 6. **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). 7. **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 8. **"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. 9. **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. 10. **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 48–57` → 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) - 1420–1452 → 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** : ~5–9 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 : **5–9s** (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.2–0.35 (pas 0–1 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).