Honeypot & SIEM : Détecter et Analyser les Attaquants en Production | Mickael Chaksoukane | Portfolio

Honeypot & SIEM : Détecter et Analyser les Attaquants en Production

7 mars 2026

Objectifs du Projet

Ce projet fait directement suite au projet DevSecOps, dans lequel le honeypot HTTP était déjà en place mais muet : il ne loggait rien, et aucune alerte ne remontait nulle part. L’objectif ici était d’aller jusqu’au bout, instrumenter chaque leurre, construire le pipeline complet vers un SIEM, et avoir une visibilité réelle sur ce qui arrive sur le serveur.

Les critères du projet :

  • Honeypot : Il y aura un leurre cowrie qui simule un port ssh ouvert et accessible, ainsi qu’une fausse page de connexion wordpress.
  • Détection active : chaque connexion suspecte déclenche une alerte dans le SIEM, avec les données utiles (IP source, credentials tentés, commandes exécutées).
  • Couverture complète : SSH, HTTP, et le reverse proxy lui-même sont tous surveillés.
  • Production-ready : tout est déployable en une commande via Ansible, réalisé sur le projet DevSecOps.

Architecture

honeypot archi

Deux réseaux Docker distincts assurent l’isolation : public_net pour les services exposés, trap_net interne uniquement pour les honeypots. Les leurres ne sont jamais joignables directement depuis Internet, seul le reverse proxy (Traefik) peut les atteindre.

L’agent Wazuh tourne directement sur le host et lit les fichiers de logs via les volumes Docker nommés. Les logs remontent au manager Wazuh (lui-même en Docker) sur localhost.


Honeypot SSH — Cowrie

Cowrie est un honeypot SSH medium-interaction : il accepte n’importe quelle combinaison identifiant/mot de passe et présente un faux shell. L’attaquant croit être connecté à un vrai serveur, pendant que chaque commande est enregistrée.

Le vrai SSH admin est déplacé sur le port 2222 (configuré par Ansible). Le port 22 public est entièrement capté par Cowrie.

cowrie:
  image: cowrie/cowrie:latest
  ports:
    - "22:2222"    # Port 22 public → Cowrie (honeypot)
  volumes:
    - cowrie_logs:/cowrie/cowrie-git/var/log/cowrie

Voici des alertes réelles remontées dans Wazuh lors des tests :

Tentative de connexion SSH (règle 100201 — level 10) : honeypot archi

Connexion réussie au leurre (règle 100202 — level 14) : honeypot archi

Commande exécutée dans le faux shell (règle 100203 — level 12) : honeypot archi


Honeypot HTTP — Flask

Le honeypot HTTP simule une interface WordPress : page de connexion wp-admin complète, avec logo, formulaire et comportement attendu par les bots. Contrairement à une réponse nginx statique, Flask capture tout ce qui est soumis.

Chaque requête est loggée en JSON avec les credentials en clair. Alerte réelle remontée dans Wazuh (règle 100102 — level 12) :

honeypot archi

Le routage Traefik dirige uniquement /wp-admin et /wp-login.php vers ce service. Tout le reste arrive sur le portfolio.

labels:
  - "traefik.http.routers.honeypot.rule=PathPrefix(`/wp-admin`) || PathPrefix(`/wp-login.php`)"
  - "traefik.http.routers.honeypot.priority=10"

La priorité élevée garantit que ces routes sont interceptées avant la règle catch-all du portfolio.


SIEM — Wazuh

Wazuh est déployé en mode single-node via Docker (manager + indexer + dashboard). L’agent sur le host surveille trois sources de logs, chacune avec ses propres règles de détection.

Pipeline complet

[Fichier log JSON]
       │
[Agent Wazuh — logcollector]
       │  (localfile, format json)
[Manager — analysisd]
       │
[Décodeur json générique]  ← toujours prioritaire sur les décodeurs custom
       │
[Règles custom — field matching]
       │
[alerts.json + Dashboard]

Un point clé : le décodeur json natif de Wazuh prend toujours la priorité sur les décodeurs personnalisés pour les logs JSON. Les règles utilisent donc <decoded_as>json</decoded_as> puis filtrent sur les champs pour identifier la source.

Règles Cowrie

<rule id="100200" level="6">
  <decoded_as>json</decoded_as>
  <field name="eventid">^cowrie\.</field>
  <description>Cowrie: événement honeypot SSH</description>
</rule>

<rule id="100201" level="10">
  <if_sid>100200</if_sid>
  <field name="eventid">^cowrie\.login\.failed$</field>
  <description>Cowrie: tentative de brute-force SSH</description>
  <group>authentication_failed,</group>
</rule>

<rule id="100202" level="14">
  <if_sid>100200</if_sid>
  <field name="eventid">^cowrie\.login\.success$</field>
  <description>Cowrie: attaquant authentifié sur le leurre SSH</description>
  <group>authentication_success,</group>
</rule>

Règles HTTP Honeypot

<rule id="100100" level="5">
  <decoded_as>json</decoded_as>
  <location>honeypot-access</location>
  <description>HTTP Honeypot: requête reçue sur surface d'attaque</description>
</rule>

<rule id="100102" level="12">
  <if_sid>100100</if_sid>
  <field name="http_method">^POST$</field>
  <description>HTTP Honeypot: credentials soumis au leurre</description>
  <group>authentication_failed,</group>
</rule>

Règles Traefik

<rule id="100110" level="3">
  <decoded_as>json</decoded_as>
  <field name="RouterName">.</field>
  <description>Traefik: requête HTTP reçue</description>
</rule>

<rule id="100111" level="6">
  <if_sid>100110</if_sid>
  <field name="DownstreamStatus">^404$</field>
  <description>Traefik: ressource inexistante (404) — scan potentiel</description>
  <group>recon,</group>
</rule>

Résumé des niveaux d’alerte

IDLevelSourceDescription
1001005HTTP HoneypotToute requête sur le leurre
1001016HTTP HoneypotInterface sensible visitée (GET)
10010212HTTP HoneypotCredentials soumis au leurre (POST)
1001103TraefikRequête HTTP reçue
1001116TraefikRessource inexistante (404) — scan potentiel
1001137TraefikErreur serveur (5xx)
1002006CowrieÉvénement honeypot SSH générique
10020110CowrieTentative de brute-force SSH
10020214CowrieAttaquant authentifié sur le leurre SSH
10020312CowrieCommande exécutée dans le leurre SSH

Le level 14 sur cowrie.login.success est intentionnel : si un attaquant passe l’authentification du honeypot SSH, c’est la situation la plus intéressante à analyser.


Déploiement Ansible

Tout est idempotent et se déploie via une seule commande. Les étapes clés liées à ce projet dans le playbook :

- name: Créer les volumes Docker partagés (honeypots → Wazuh)
  community.docker.docker_volume:
    name: "{{ item }}"
    state: present
  loop:
    - honeypot_logs
    - cowrie_logs
    - traefik_logs

- name: Configurer la surveillance des logs honeypot HTTP
  ansible.builtin.blockinfile:
    path: /var/ossec/etc/ossec.conf
    insertbefore: '</ossec_config>'
    block: |
      <localfile>
        <log_format>json</log_format>
        <location>/var/lib/docker/volumes/honeypot_logs/_data/honeypot-access.log</location>
      </localfile>      

- name: Déployer les règles Cowrie dans le volume Wazuh
  ansible.builtin.copy:
    src: ../wazuh/cowrie_rules.xml
    dest: /var/lib/docker/volumes/single-node_wazuh_etc/_data/rules/cowrie_rules.xml

Résultats

Le pipeline fonctionne de bout en bout. Voici une alerte Traefik réelle, accès au portfolio (règle 100110) suivi d’un 404 sur un path inexistant (règle 100111) :

Alerte Wazuh — credentials WordPress capturés (règle 100102)

Alerte Wazuh — 404 traefik

Côté HTTP, les credentials WordPress soumis via POST apparaissent en clair dans data.credentials sur le Discover Wazuh. Voici un exemple de logs reçu :

Alerte Wazuh — credentials WordPress capturés (règle 100102)


Analyse du Trafic

Analyse en cours, le serveur vient d’être déployé en production. Cette section sera mise à jour avec les données réelles : top IPs attaquantes, credentials les plus tentés, répartition géographique des scans SSH, commandes exécutées dans Cowrie, heatmap horaire des tentatives d’intrusion.