
Honeypot SSH autonome permettant de capturer les interactions des attaquants et de les transformer en renseignements de sécurité structurés.
SSHintel est un honeypot SSH léger construit avec Python et Paramiko. Il simule un shell Linux factice pour journaliser les tentatives d'accès non autorisées, capturer les identifiants et analyser le comportement des attaquants dans un environnement contrôlé.
ls, cd, pwd, , , , , , , , , , , , , , , , etc.)catechogrepfindtreeheadtailwcstatpsdffreeenvidwhoami--tarpit optionnel pour ralentir les attaquants avec une sortie différéeSSHintel n'est pas un shell Bash/Linux complet. Il simule un sous-ensemble crédible de commandes courantes pour recueillir la télémétrie des attaquants. Les commandes sont distribuées par un registre léger ; ajouter une commande signifie ajouter une petite fonction de gestion.
pip install -r requirements.txt
Remarque : La clé d'hôte SSH est générée automatiquement au premier lancement. Aucune génération manuelle de clé n'est nécessaire.
Exécutez le honeypot avec un port, un nom d'utilisateur et un mot de passe spécifiques :
python3 run.py serve --port 2222 --username user1 --password pass123
Le port par défaut est
2222et l'hôte est0.0.0.0.
Le honeypot génère automatiquement une clé d'hôte SSH dans static/server.key au premier lancement si elle n'existe pas. Aucune génération manuelle de clé n'est nécessaire.
Pour activer le mode tarpit :
python3 run.py serve --port 2222 --username user1 --password pass123 --tarpit
Pour désactiver la télémétrie SQLite (JSONL uniquement) :
python3 run.py serve --port 2222 --username user1 --password pass123 --no-db
SSHintel protège contre l'épuisement des ressources causé par de nombreuses connexions simultanées ou des connexions maintenues actives indéfiniment. Ces paramètres sont configurables via la CLI :
| Indicateur | Défaut | Objectif |
|---|---|---|
--max-connections | 50 | Nombre maximal de connexions actives simultanées ; les connexions supplémentaires sont rejetées et journalisées comme un événement connection_rejected |
--auth-timeout | 60 (s) | Temps autorisé pour terminer la poignée de main/authentification SSH ; les clients bloqués sont déconnectés |
--session-idle-timeout | 300 (s) | Délai d'inactivité pour un shell authentifié ; une session inactive est terminée, mais un attaquant qui tape activement n'est jamais tué |
Exemple :
python3 run.py serve --port 2222 --username user1 --password pass123 \
--max-connections 25 --auth-timeout 30 --session-idle-timeout 300
Lorsque trop de connexions sont ouvertes, la connexion supplémentaire est fermée immédiatement et un événement de sécurité connection_rejected (avec reason: connection_limit) est écrit dans le journal JSONL. Une authentification bloquée est enregistrée comme une déconnexion avec reason: auth_timeout ; un shell inactif se termine avec reason: idle_timeout.
Le mode tarpit envoie intentionnellement la sortie lentement pour maintenir l'engagement d'un attaquant, donc la boucle de bannière tarpit n'est pas soumise au délai d'inactivité — mais les sessions tarpit comptent dans la limite de connexions.
SSHintel comprend un tableau de bord web local qui visualise la télémétrie de sécurité stockée dans SQLite.
python3 run.py dashboard
Ouvrez ensuite http://localhost:5000 dans votre navigateur.
Le tableau de bord lit la base de données SQLite dans
data/sshintel.dbpar défaut. Démarrez d'abord le honeypot pour que la télémétrie soit capturée, puis lancez le tableau de bord pour le regarder se remplir.
Cliquez sur un ID de session (ou naviguez vers /session/<session_id>) pour ouvrir la vue d'investigation de session, qui reconstitue une seule attaque chronologiquement :
$ commande de tout ce que l'attaquant a tapé, avec les répertoires de travail# Terminal 1 : démarrer le honeypot
python3 run.py serve --port 2222 --username user1 --password pass123
# Terminal 2 : démarrer le tableau de bord
python3 run.py dashboard
# Terminal 3 : simuler un attaquant
ssh user1@localhost -p 2222
# (exécuter quelques commandes, puis quitter)
Ouvrez ensuite http://localhost:5000 pour inspecter l'activité capturée.
Le tableau de bord prend en charge la télémétrie en direct — il interroge le honeypot toutes les 2 secondes et se met à jour automatiquement. Les nouvelles sessions, commandes et événements apparaissent en temps réel sans actualiser la page. Un indicateur en direct (● Live) montre l'état de la connexion.
Ouvrez un second terminal et essayez de vous connecter :
ssh user1@localhost -p 2222
Si les identifiants correspondent, vous serez placé dans le shell émulé.
Pour supprimer les empreintes SSH obsolètes :
notepad "%USERPROFILE%\.ssh\known_hosts"
Supprimez la ligne pertinente contenant
localhostou l'adresse IP du honeypot.
creds_loggerfunnel_loggerlog_files/events.jsonl au format JSON Lines (JSONL) — un objet JSON valide par ligneChaque événement JSONL comprend un timestamp UTC ISO-8601, un event_type, un session_id unique et l'source_ip. Les connexions, les tentatives/résultats d'authentification, l'exécution des commandes, l'activation du tarpit et les déconnexions sont tous enregistrés comme événements structurés.
Chaque connexion SSH entrante est suivie comme une session indépendante avec son propre session_id. Une session enregistre l'adresse IP source, les heures de connexion/déconnexion, le résultat de l'authentification et la durée de la connexion, et chaque événement généré dans cette connexion porte le même session_id (afin que les tentatives d'authentification, les commandes et les déconnexions puissent être reliées à une seule connexion). Les sessions sont isolées par connexion — aucun état n'est partagé entre les clients simultanés.
Chaque session reçoit également son propre système de fichiers factice isolé en mémoire — le système de fichiers simulé est créé fraîchement pour chaque connexion et nettoyé lorsque la connexion se termine. Les fichiers, répertoires et le répertoire de travail créés ou modifiés par un attaquant ne sont jamais visibles par un autre attaquant connecté au même moment. L'ensemble du système de fichiers est simulé dans la mémoire Python et ne touche jamais le système de fichiers réel de l'hôte.
Valeurs actuelles de event_type : connect, auth_attempt, auth_success, auth_failure, command, disconnect, connection_rejected, tarpit.
SSHintel/
├── honeypot/ # Logique principale du honeypot
│ ├── __init__.py
│ ├── main.py # Boucle d'acceptation + limitation des connexions
│ ├── handlers.py # Configuration du transport SSH + shell émulé
│ ├── server.py # Interface serveur Paramiko (authentification)
│ ├── session.py # Suivi de session par connexion
│ ├── fs.py # Système de fichiers factice en mémoire (isolé par session)
│ ├── shell.py # Shell factice : registre de commandes + répartiteur
│ ├── limits.py # Limitation des connexions simultanées thread-safe
│ ├── logger.py # Journalisation des événements JSONL + pont SQLite
│ └── telemetry_store.py # Stockage de télémétrie SQLite + couche de requêtes
│
├── dashboard/ # Tableau de bord web local
│ ├── app.py # Application Flask + routes API
│ ├── templates/
│ │ ├── index.html # Modèle principal du tableau de bord
│ │ └── session.html # Modèle d'investigation de session
│ └── static/
│ ├── style.css # Styles du tableau de bord
│ ├── dashboard.js # JS principal du tableau de bord (mises à jour en direct)
│ └── session.js # JS d'investigation de session
│
├── log_files/ # Journaux d'exécution (ignorés par git)
│ ├── creds_audits.log # Tentatives d'identifiants
│ ├── cmd_audits.log # Piste d'audit des commandes
│ └── events.jsonl # Événements de sécurité JSONL structurés
│
├── data/ # Base de données SQLite (ignorée par git)
│ └── sshintel.db
│
├── static/ # Clé d'hôte SSH (générée automatiquement)
│ └── server.key
│
├── .github/workflows/ # Configuration CI
│ └── tests.yml
│
├── Dockerfile
├── README.md
├── requirements.txt # Dépendances d'exécution (paramiko, flask)
├── requirements-dev.txt # Dépendances de test (pytest, pytest-cov)
└── run.py # Point d'entrée CLI
Si vous préférez exécuter le honeypot dans un environnement conteneurisé, vous pouvez utiliser le Dockerfile inclus.
docker build -t sshintel .
Cela crée une image Docker nommée
sshintel.
docker run -p 2222:2222 sshintel
Cela va :
static/server.key (si elle n'existe pas déjà)2222 avec les identifiants par défaut :username: user1, password: pass123Ouvrez un second terminal et connectez-vous via SSH :
ssh user1@localhost -p 2222
Vous serez placé dans le shell simulé si les identifiants correspondent.
Pour arrêter le conteneur :
docker ps # Trouver l'ID du conteneur
docker stop <container_id>
Pour supprimer l'image :
docker rmi sshintel
Vous pouvez également exporter l'image avec
docker save -o sshintel.tar sshintelet la charger plus tard avecdocker load -i sshintel.tar.
Ce projet est sous licence MIT License.