
Ce projet explore si OpenSSH moderne révèle des noms d'utilisateur valides via des différences subtiles de réponse ou de temporisation. Enquête sur l'énumération d'utilisateurs CVE-2016-6210 ( test t de Welch, d de Cohen et ingénierie de détection ) dans un laboratoire contrôlé sous Ubuntu 22.04.5 LTS ; il examine également les traces que de telles tentatives laissent derrière elles et comment elles peuvent être détectées.
Une réinvestigation reproductible de l'énumération de noms d'utilisateur OpenSSH par analyse statistique.
Ce projet réinvestit CVE-2016-6210, un canal auxiliaire temporel documenté d'OpenSSH, afin de déterminer s'il reste observable sur un serveur Ubuntu moderne avec la configuration PAM par défaut.
Plutôt que de supposer que le comportement publié s'applique toujours, le projet évalue les mesures de temps d'authentification collectées via des sondages manuels, Hydra et Metasploit en utilisant le test t de Welch et le d de Cohen pour distinguer les signaux temporels réels du bruit de mesure.
L'étude n'a révélé aucune différence temporelle statistiquement significative sur la configuration par défaut testée, démontrant l'importance de l'expérimentation reproductible et de la validation fondée sur des preuves des affirmations de sécurité publiées.
⚠️ Avis légal
Ce projet a été entièrement mené dans un environnement de laboratoire isolé et dont vous êtes propriétaire. Tous les résultats ne s'appliquent qu'à la configuration testée. Ne testez jamais des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation écrite explicite.
L'énumération d'utilisateurs – la capacité de déterminer si un nom d'utilisateur spécifique existe sur un système distant sans informations d'identification valides. Il s'agit d'une première étape cruciale dans la chaîne d'attaque menant à la compromission du compte :``` Reconnaissance → [User Enumeration] → Password Attack → Access ↑ This project investigates here
Si un attaquant peut distinguer « cet utilisateur existe » de « cet utilisateur n’existe pas » en analysant les réponses du serveur, il peut réduire considérablement l’espace de clés pour les attaques ultérieures par force brute ou par bourrage d’identifiants.
SSH est une cible fréquente car il est quasi universellement exposé, gère l’authentification par mot de passe, et les implémentations plus anciennes présentaient des différences de temps mesurables entre les noms d’utilisateur valides et invalides (CVE-2016-6210).
**Cette investigation pose deux questions :**
1. L’OpenSSH moderne sur Ubuntu 22.04.5 LTS avec la configuration par défaut divulgue-t-il l’existence d’un utilisateur
via les messages de réponse, le temps de réponse ou les signaux rapportés par les outils ?
2. Si un attaquant tente quand même l’attaque, quels artefacts laisse-t-il ? et avec quelle fiabilité
peuvent-ils être détectés ?
---
## 🖥️ Configuration du laboratoire
Tous les tests ont été réalisés dans un réseau virtuel isolé, sans accès à Internet.
| Machine | Système d’exploitation | Rôle | IP | Version SSH |
|-----------|----------------------------|----------------------------------|------------------|------------------|
| Attaquant | Kali Linux 2024.1 | Outils offensifs, scripts d’analyse | 192.168.56.5 | — |
| Cible | Ubuntu Server 22.04.5 LTS | Exécute OpenSSH avec configuration **par défaut** | 192.168.56.10 | OpenSSH 8.9p1 |
**Configuration SSH cible (`/etc/ssh/sshd_config` par défaut) :**```
PasswordAuthentication yes
UsePAM yes # Key setting — normalises timing via dummy hash
PermitRootLogin prohibit-password
MaxAuthTries 6
LogLevel INFO
UsePAM yes est le paramètre de durcissement critique. Il force OpenSSH à exécuter un calcul bcrypt factice pour les utilisateurs inexistants, correspondant au timing d'une vérification de mot de passe réelle. Cela a été introduit spécifiquement comme contre-mesure à CVE-2016-6210.
Chaque méthode d'attaque a été exécutée comme un essai indépendant avec un état de journal propre :```bash
sudo truncate -s 0 /var/log/auth.log
sudo cp /var/log/auth.log ~/evidence/trial-N-auth.log
Preuves collectées par essai :
- Sortie standard/erreur de l'outil (sauvegardée textuellement)
- `/var/log/auth.log` de la cible
- Échantillons de synchronisation des réponses via `time.perf_counter()` dans `manual_ssh.py`
- Bannière SSH récupérée avant toute tentative d'authentification
### Méthodes d'attaque
| Méthode | Outil | Liste de mots | Objectif |
|-----------------------|-------------------------------------|--------------------------|--------------------------------------------------|
| SSH manuel | `ssh` CLI + Paramiko | 50 noms d'utilisateur courants | Référence ; inspecter les réponses brutes |
| Bruteforce Hydra | `hydra` | les mêmes 50 | Automatisé ; exploite le mode d'énumération intégré de Hydra |
| Module Metasploit | `auxiliary/scanner/ssh/ssh_enumuser`| les mêmes 50 | Module d'énumération dédié du framework |
| Empreinte de bannière | custom `BannerFingerprinter` | N/A | Fuite de version sans authentification, vérification CVE |
| Analyse temporelle | custom `ResponseAnalyzer` | sous-ensemble valide vs invalide | Vérification statistique par canal auxiliaire |
---
## Ce qui a été construit
Ce projet va au-delà de l'exécution d'outils — il encapsule chaque attaque et toute la logique de détection dans une base de code Python structurée et fournit un orchestrateur qui exécute l'ensemble du pipeline de bout en bout.
### Outils d'attaque (`src/attack_tools/`)
**`ManualSSHEnumerator`** — Teste chaque nom d'utilisateur N fois avec `paramiko`, en enregistrant le timing précis, le type de résultat et la bannière SSH. Calcule la moyenne/écart type par nom d'utilisateur. Il est crucial qu'il ne **réutilise pas** les connexions entre les tentatives, ce qui garantit que chaque échantillon capture le temps de traitement complet du côté serveur.
**`BannerFingerprinter`** — Récupère la bannière SSH via une socket TCP brute (aucune information d'identification nécessaire). Analyse le nom de l'implémentation, la chaîne de version et l'indice du système d'exploitation. Recoupe avec un registre CVE local. Une version comme `OpenSSH_8.9p1 Ubuntu-3ubuntu0.6` révèle le logiciel serveur exact — potentiellement suffisant pour identifier les vulnérabilités connues avant toute tentative d'authentification.
**`HydraAutomation`** — Wrapper de sous-processus autour de Hydra. Analyse la sortie standard pour extraire les connexions réussies, les messages d'erreur et le verdict d'énumération propre à Hydra (`does not support user enumeration`).
**`MetasploitScanner`** — Écrit un script de ressource temporaire et pilote `msfconsole` via un sous-processus. Analyse la sortie pour la détection de durcissement et les noms d'utilisateur trouvés.
### Outils de détection (`src/detection_tools/`)
**`LogParser`** — Analyseur basé sur des expressions régulières pour auth.log supportant cinq types d'événements SSH : `failed_invalid_user`, `failed_valid_user`, `pre_auth_reject`, `accepted`, `disconnected`. Retourne des dictionnaires d'événements structurés avec horodatage, type d'événement, nom d'utilisateur, IP source et port.
**`ResponseAnalyzer`** — Effectue le test t de Welch sur les distributions de temps des noms d'utilisateur valides vs invalides. Calcule le delta de temps (ms), la valeur p, la taille d'effet d de Cohen et une conclusion en langage clair. Seuil : delta ≥ 5ms ET p < 0.05 déclenche un avertissement de canal auxiliaire.
**`EnumerationDetector`** — Quatre motifs de détection :
- **Sondes rapides d'utilisateurs** : fenêtre glissante - même IP, ≥10 noms d'utilisateur distincts en 60s
- **Corrélation de liste de mots** : taux de correspondance entre les noms d'utilisateur tentés et les listes d'attaque connues
- **Temps séquentiel** : coefficient de variation des écarts entre tentatives (faible CoV → outil)
- **Sondage distribué** : même nom d'utilisateur depuis plusieurs IP (reconnaissance par credential stuffing)
**`AlertingSystem`** — Émetteur d'alertes léger. Génère des alertes JSON horodatées sur la sortie standard. Extensible avec des intégrations email/SIEM/webhook selon les besoins.
### Orchestrateur
**`run_investigation.py`** — Pilote CLI qui exécute les quatre étapes séquentiellement et écrit les résultats dans `data/results/`. Exécutez avec `--help` pour une utilisation complète.```bash
python run_investigation.py \
-target 192.168.xx.xxxx \
-usernames data/wordlists/common-usernames-50.txt \
-log data/sample-logs/auth.log \
-known-valid root ubuntu \
-samples 10
Hypothèse testée : OpenSSH renvoie-t-il un message d'erreur différent pour un utilisateur inexistant que pour un utilisateur existant avec un mauvais mot de passe ?```bash
$ ssh [email protected] Permission denied (publickey,password).
$ ssh [email protected] Permission denied (publickey,password).
**Résultat :** Les réponses sont identiques octet par octet. Le protocole ne fuit aucune information.
**Pourquoi :** Depuis OpenSSH 7.3, `UsePAM yes` force le serveur à exécuter une opération `crypt()` factice pour les utilisateurs inexistants, faisant correspondre à la fois le timing et le chemin d'erreur d'une authentification réelle échouée. Le correctif était une réponse directe à CVE-2016-6210.
---
### Constatation 2 : Aucun canal latéral temporel détecté
**Hypothèse testée :** Même si les messages d'erreur correspondent, existe-t-il une différence de timing mesurable entre les noms d'utilisateur valides et invalides qui pourrait être exploitée statistiquement ?
Dix échantillons temporels ont été collectés pour chacun des 50 noms d'utilisateur. Les utilisateurs connus valides (confirmés depuis le système) ont été comparés au pool d'utilisateurs invalides.
| Métrique | Valeur |
|-----------------------------|-------------------------------------|
| Timing moyen — utilisateurs invalides | ~312 ms |
| Timing moyen — utilisateurs valides | ~311 ms |
| Delta | **~1 ms** |
| Valeur p du test t de Welch | > 0.40 |
| Conclusion | **Aucun canal latéral distinguable** |
Le delta d'environ 1 ms est bien en dessous du seuil de bruit de 5 ms et n'est pas statistiquement significatif (p >> 0.05). Le calcul de hachage factice d'OpenSSH est efficace.
---
### Constatation 3 : Hydra ne rapporte aucun support d'énumération
Le mode d'énumération SSH d'Hydra repose sur l'un des trois signaux : des messages d'erreur différents, un timing différent, ou un comportement de connexion différent. Avec les trois normalisés, Hydra rapporte explicitement :```
[ERROR] target ssh://192.168.56.10:22/ does not support user enumeration
[STATUS] 50/50 tries completed, 0 valid logins found
Effet secondaire observé : Malgré l'échec de l'énumération, les 50 tentatives sont enregistrées dans
/var/log/auth.log avec l'adresse IP source, l'horodatage et le nom d'utilisateur tenté. La présence de l'attaquant
est entièrement visible.
Le module auxiliary/scanner/ssh/ssh_enumuser vérifie la version d'OpenSSH à partir de la
bannière avant de tenter l'énumération. Les versions ≥ 7.3 avec UsePAM yes sont signalées comme
renforcées et le module se termine prématurément :```
[] 192.168.xx.xxxx:22 - SSH - Checking for vulnerability
[] 192.168.xx.xxxx:22 - SSH - Target is not vulnerable: OpenSSH 8.9p1 (hardened)
C'est une constatation utile : la bannière seule communique la posture défensive du serveur à un attaquant avant toute tentative d'énumération.
---
### Constatation 5 : La détection est fiable même lorsque l'énumération échoue
L'essentiel du point de vue d'un défenseur : **l'attaque génère du bruit même lorsqu'elle ne réussit pas**. Les quatre modèles de détection ont correctement fonctionné sur le auth.log collecté :
| Détection | Déclencheur | Sévérité |
|-----------|-------------|----------|
| Sonde rapide d'utilisateur | L'IP Kali a sondé 50 noms d'utilisateur en <60s | ÉLEVÉE |
| Corrélation de wordlist | 48/50 noms tentés correspondent à la wordlist | ÉLEVÉE |
| Chronométrage séquentiel | CoV inter-tentative = 0.04 (signature d'outil) | MOYENNE |
| Sonde de bannière uniquement | Déconnexions pré-authentification avant tout envoi de nom d'utilisateur | FAIBLE |
---
## Processus de réflexion
### Pourquoi une énumération SSH manuelle en premier ?
L'instinct de commencer par des tests manuels était méthodologiquement sain : avant de faire confiance à la sortie d'un outil, vous devez comprendre ce que le protocole brut dit réellement. Exécuter `ssh ghost@target` et observer le message d'erreur exact vous indique s'il y a *quoi que ce soit* à énumérer avant d'investir du temps dans l'automatisation.
La première observation, que `Permission denied (publickey,password)` semble identique indépendamment du fait que l'utilisateur existe, était la constatation centrale. Tout ce qui a suivi était une validation de ce résultat.
### Hypothèses formulées (et réexaminées)
L'hypothèse initiale était que Hydra et Metasploit seraient *plus* capables que les tests manuels, donc si le manuel échouait, les outils pourraient encore réussir. Cela s'est avéré faux dans la direction attendue mais correct dans le *pourquoi* : les outils n'ajoutent pas de capacité ici car le protocole lui-même ne fuit pas le signal. Les outils ne sont que de l'automatisation sur le même protocole.
Une deuxième hypothèse mérite d'être examinée : la première tentative manuelle était nettement plus lente que les suivantes, et la tentative de mot de passe réussi était rapide. Cela a d'abord été interprété comme un signal temporel potentiel. Après réflexion, le ralentissement était dû à la surcharge d'établissement de connexion TCP sur un état réseau frais (résolution ARP, mise en place de la connexion), et non au temps de traitement côté serveur. Contrôler cela — en mesurant à partir de `time.perf_counter()` *après* la poignée de main TCP, ou en écartant le premier échantillon — aurait été plus rigoureux. L'implémentation de `ManualSSHEnumerator` répond à cela en collectant 10 échantillons par nom d'utilisateur et en rapportant moyenne/écart type, ce qui dilue le bruit du premier échantillon.
### Ce qui a changé pendant le projet
Le périmètre initial était étroit : exécuter trois outils, documenter s'ils fonctionnent. Le projet a évolué dans deux directions :
**Vers l'intérieur (analyse plus approfondie) :** Lorsque les premiers résultats ont été négatifs, la question naturelle est devenue *pourquoi* — ce qui a conduit à la lecture des journaux de modifications d'OpenSSH, de CVE-2016-6210 et de l'implémentation de `UsePAM`. Comprendre le mécanisme est plus précieux que de simplement enregistrer le résultat.
**Vers l'extérieur (pivot vers la détection) :** Un résultat d'attaque négatif reste un point de données défensif utile. Le pivot vers « même si l'énumération a échoué, qu'a vu le serveur ? » a conduit à l'analyse des journaux et aux composants d'ingénierie de détection, transformant un exercice d'exécution d'outils unidimensionnel en une investigation bidirectionnelle.
### Ce qui serait fait différemment
Les mesures de timing ont été prises sur un réseau virtuel host-only, ce qui introduit moins de gigue qu'un réseau réel, mais signifie également que les résultats sont optimistes. Dans un environnement réel avec latence TCP, gigue et retransmissions, le plancher de bruit serait plus élevé et l'analyse temporelle nécessiterait plus d'échantillons par nom d'utilisateur. Une méthodologie plus robuste testerait sur une liaison WAN simulée (en utilisant `tc netem` pour introduire une latence et une gigue contrôlées) pour voir comment les conclusions tiennent dans des conditions réalistes.
---
## Risques de sécurité
Même si l'énumération n'a pas réussi dans ce laboratoire, la surface d'attaque et les risques associés sont :
**Si l'énumération *était* possible (par exemple, OpenSSH plus ancien, `UsePAM no`, pile PAM personnalisée) :**
- Les attaquants pourraient réduire une campagne de force brute aux noms d'utilisateur confirmés valides uniquement, réduisant ainsi le risque de détection et augmentant considérablement l'efficacité.
- Combiné avec du password spraying (un mot de passe commun pour tous les noms d'utilisateur valides), cela contourne les verrouillages par utilisateur de `MaxAuthTries`.
**Risques qui s'appliquent même avec la résistance à l'énumération :**
- La bannière SSH fuit la version exacte d'OpenSSH et le système d'exploitation. Un attaquant qui voit `OpenSSH_8.9p1` peut immédiatement vérifier si CVE-2024-6387 (regreSSHion) s'applique, avant d'envoyer un seul paquet d'authentification.
- Toutes les tentatives d'énumération sont enregistrées avec l'IP source. Si la surveillance des journaux est absente, une attaque lente (une tentative par heure) pourrait sonder des milliers de noms d'utilisateur sans déclencher d'alertes basées sur le taux.
- `PasswordAuthentication yes` maintient la surface d'attaque basée sur les mots de passe ouverte même si l'énumération n'est pas viable. Les attaques de credentials post-OSINT (utilisant des listes de mots de passe divulgués contre des noms d'utilisateur issus de l'OSINT) ne nécessitent pas du tout d'énumération côté serveur.
---
## Stratégies d'atténuation
| Menace | Atténuation | Changement de configuration requis |
|--------|-------------|------------------------------------|
| Canal auxiliaire temporel | Assurer `UsePAM yes` (par défaut sur Ubuntu) | Aucun — déjà par défaut |
| Divulgation de version de bannière | `VersionAddendum none` et `DebianBanner no` dans `sshd_config` | Oui |
| Attaques par mot de passe | `PasswordAuthentication no` — authentification par clé uniquement | Oui |
| Force brute après OSINT | `fail2ban` avec la prison `sshd` | Installation + configuration |
| Énumération lente | Envoi des journaux vers un SIEM ; alerter sur >N noms d'utilisateur distincts/IP/heure | SIEM requis |
| Connexion root | `PermitRootLogin no` | Oui (par défaut `prohibit-password`) |
| Déconnexions pré-authentification | `MaxStartups 10:30:60` pour limiter le taux de connexions non authentifiées | Oui |
**Ajouts minimaux recommandés à `sshd_config` pour un déploiement durci :**```
PasswordAuthentication no
PermitRootLogin no
MaxAuthTries 3
MaxStartups 10:30:60
VersionAddendum none
LogLevel VERBOSE
Le projet actuel teste un protocole dans une configuration. Extensions naturelles :
Énumérer d'autres services sur le même hôte. SMTP (VRFY/EXPN), LDAP (requêtes d'attributs), formulaires de connexion HTTP et SNMP sont tous des vecteurs courants de fuite de noms d'utilisateur sur la même machine qui est durcie au niveau SSH. Une étude comparative entre les services produirait une image de risque plus complète.
Tester des configurations SSH non par défaut. UsePAM no, modules PAM personnalisés, backends d'authentification Kerberos et versions plus anciennes d'OpenSSH (déployées dans des systèmes embarqués, des appliances réseau) peuvent encore présenter des fuites temporelles. Tester le même pipeline contre une configuration délibérément vulnérable démontrerait le contraste.
Détection en direct avec inotify. La détection actuelle s'exécute a posteriori sur un fichier journal copié. Une version de qualité production utiliserait inotify (ou tail -f + un thread d'analyse) pour détecter et alerter en quasi-temps réel, en quelques secondes après le début d'une tentative d'énumération.
Détection au niveau réseau. Auth.log est un artefact basé sur l'hôte. Complétez-le avec une détection au niveau paquet : les connexions TCP de courte durée vers le port 22 qui se ferment avant de terminer la poignée de main d'authentification sont une signature d'empreinte uniquement de bannière. Cela pourrait être implémenté comme une règle Zeek ou Suricata.
Carnets Jupyter pour les rapports statistiques. Les données temporelles collectées se prêtent à la visualisation - diagrammes en boîte des distributions temporelles par nom d'utilisateur, nuages de points de la fréquence des tentatives dans le temps, cartes de chaleur des IP sources. Les carnets rendraient l'analyse reproductible et partageable.
ssh-enumeration-analysis/ │ ├── README.md ├── run_investigation.py ← Full pipeline orchestrator (start here) ├── requirements.txt │ ├── src/ │ ├── attack_tools/ │ │ ├── init.py │ │ ├── manual_ssh.py ← Paramiko-based timing probe │ │ ├── banner_fingerprinter.py ← No-auth banner grab + CVE lookup │ │ ├── hydra_automation.py ← Hydra subprocess wrapper │ │ └── metasploit_scanner.py ← MSF console automation │ │ │ └── detection_tools/ │ ├── init.py │ ├── log_parser.py ← auth.log regex parser │ ├── response_analyzer.py ← Welch t-test + Cohen's d │ ├── pattern_detector.py ← 4 detection patterns │ └── alerting_system.py ← JSON alert emitter │ ├── data/ │ ├── sample-logs/ │ │ ├── auth-baseline.log ← Normal SSH activity (no attacks) │ │ ├── auth-hydra-run.log ← Captured during Hydra trial │ │ └── auth-msf-run.log ← Captured during Metasploit trial │ │ │ ├── wordlists/ │ │ ├── common-usernames-50.txt │ │ └── common-usernames-100.txt │ │ │ └── results/ ← Auto-generated (gitignored) │ ├── investigation-summary.json │ ├── manual-enumeration-results.json │ ├── timing-analysis.json │ └── detection-report.json │ ├── tests/ │ ├── test_enumeration.py │ ├── test_detection.py │ └── test_log_parser.py │ ├── case-study/ │ └── User_Enumeration_attempt_using_manual_SSH.docx │ └── screenshots/ ├── manual-ssh-same-response.png ├── hydra-no-enumeration-support.png ├── metasploit-hardened-detected.png ├── auth-log-hydra-evidence.png └── detection-alerts-output.png
## ⚡ Démarrage rapide```bash
# 1. Clone and install dependencies
git clone https://github.com/Alisha-chaudhary/ssh-enum
cd ssh-enum
pip install -r requirements.txt
# 2. Run just the banner fingerprint (no credentials needed)
python -c "
from src.attack_tools.banner_fingerprinter import BannerFingerprinter
r = BannerFingerprinter().grab('192.168.xx.xxxx')
print(r.raw_banner, r.cves)
"
# 3. Run the full investigation pipeline
python run_investigation.py \
-target 192.168.xx.xxxx \
-usernames data/wordlists/common-usernames-50.txt \
-log data/sample-logs/auth-hydra-run.log \
-known-valid root ubuntu \
-samples 10
# 4. Analyse a log file only (no live target needed)
python -c "
from src.detection_tools.pattern_detector import EnumerationDetector
d = EnumerationDetector('data/sample-logs/auth-hydra-run.log')
import json; print(json.dumps(d.run_all(), indent=2))
"
Prérequis :``` paramiko>=3.3.1 scipy>=1.11.0 Hydra and Metasploit must be installed separately (pre-installed on Kali Linux).
---
## 📚 Références
| Ressource | Pertinence |
|---------------------------------------------------------------------------------------|-------------------------------------------------|
| [CVE-2016-6210](https://nvd.nist.gov/vuln/detail/CVE-2016-6210) | Le canal secondaire temporel testé par ce projet |
| [CVE-2024-6387 (regreSSHion)](https://nvd.nist.gov/vuln/detail/CVE-2024-6387) | RCE non authentifié dans OpenSSH ≤ 9.7 |
| [OpenSSH sshd_config manual](https://man.openbsd.org/sshd_config) | `UsePAM`, `MaxAuthTries`, `VersionAddendum` |
| [OWASP — User Enumeration](https://owasp.org/www-community/attacks/User_Enumeration) | Schémas généraux d'attaque par énumération |
| [RFC 4252 — SSH Authentication Protocol](https://tools.ietf.org/html/rfc4252) | Spécification du protocole ; définit le comportement des messages d'erreur |
| [fail2ban documentation](https://www.fail2ban.org/wiki/index.php/Main_Page) | Limitation de débit et bannissement IP |
---
## 🎓 Compétences démontrées
| Domaine | Preuve |
|------------------------|---------------------------------------------------------------------------------------------------------|
| Internes du protocole SSH | Compréhension de la normalisation temporelle de `UsePAM`, de la correction de CVE-2016-6210, de l'exposition d'informations de bannière |
| Sécurité offensive | Utilisation pratique de Hydra, Metasploit et d'un sondage Paramiko personnalisé dans une méthodologie structurée |
| Analyse statistique | Test t de Welch, d de Cohen, coefficient de variation appliqués aux données temporelles |
| Ingénierie de détection | Détection IOC basée sur les logs dans quatre types de motifs ; sortie d'alerte structurée |
| Ingénierie Python | Dataclasses, annotations de type, automatisation de sous-processus, analyse regex, bibliothèques statistiques |
| Recherche en sécurité | Hypothèse → expérience contrôlée → collecte de preuves → résultats documentés → conclusions exploitables |
---
**Statut :** Terminé | **Testé sur :** Ubuntu Server 22.04 LTS + OpenSSH 8.9p1 | Kali Linux 2024.1