- 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>
243 lines
13 KiB
Markdown
243 lines
13 KiB
Markdown
# 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).
|