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

243 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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é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 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).