ASLR ohne Info-Leak umgehen: Distributions-Härtung, PIE und fork-Ausnutzung
ASLR ohne Info-Leak umgehen: Distributions-Härtung, PIE und fork-Ausnutzung
<time datetime="2026-10-23">Geprüft am 23. Oktober 2026</time>
Address Space Layout Randomization (ASLR) ist eine der wirksamsten generischen Mitigationen gegen Speicherkorruption. Dennoch zeigen Analysen zu CVE-2026-109619, dass PHP2Shell-Varianten ohne interaktives Informationsleck auskommen. Der Grund liegt nicht in einem gebrochenen ASLR, sondern in der Kombination aus vorhersagbarer Adressraumnutzung, fork()-Vererbung und der Möglichkeit, Fehlversuche kostengünstig zu wiederholen. Dieser Artikel beschreibt die Mechanismen und was Distributionen tatsächlich dagegen tun.
1. Physik des Leak-freien Hijacks
1.1 Seitenoffets bleiben konstant
Auf 64-Bit-Linux sind Benutzeradressen auf etwa 47 Bit begrenzt. ASLR randomisiert die oberen Bits, die unteren zwölf Bits (Seitenoffset) bleiben unverändert. Ein Angreifer, der ein Objekt mit bekannter Struktur anvisiert, kann daher einen Teil der Adresse vorhersagen und den Rest durch Wiederholung treffen.
1.2 Fehlendes oder partielles PIE
Einige Pakete bauen php-fpm ohne vollständiges PIE, oder nur das Hauptprogramm ist PIE, während Erweiterungen an festen Adressen geladen werden. Dann sind interne Funktionen adressierbar, ohne dass ein Leck nötig wäre.
checksec --file=/usr/sbin/php-fpm8.3
readelf -h /usr/sbin/php-fpm8.3 | grep Type
readelf -lW /usr/sbin/php-fpm8.3 | grep GNU_RELRO
Type: EXEC bedeutet kein PIE, Type: DYN bedeutet PIE. Das Ergebnis variiert je nach Distribution, Compiler und Build-Flags. Verlassen Sie sich nicht auf Annahmen, sondern messen Sie auf dem tatsächlich eingesetzten Image.
1.3 fork()-Vererbung
Der FPM-Master startet seine Worker per fork(). Kinder erben den Adressraum des Elternprozesses; damit teilen alle Worker dieselben Bibliotheks-Basisadressen. Ein erfolgreicher Adressrückschluss in einem Worker gilt für den gesamten Pool.
ps -eo pid,ppid,user,cmd | grep -E 'php-fpm' | head -20
php-fpm: master process (/etc/php/8.3/fpm/php-fpm.conf) PID 1200
├─ php-fpm: pool www PID 1201
├─ php-fpm: pool www PID 1202
└─ php-fpm: pool www PID 1203
Diese Architektur ist performant, reduziert aber die effektive ASLR-Entropie auf einen einzigen Startzeitpunkt des Masters. Wer den Master lange laufen lässt, vergrößert das Zeitfenster für einen Angriff.
2. Distributionsmatrix
| Distribution | php-fpm PIE | Full RELRO | Stack Canary | libc | Einordnung |
|---|---|---|---|---|---|
| Ubuntu 22.04 LTS | meist DYN | teilweise | ja | 2.35 | mittel – Backport prüfen |
| Ubuntu 24.04 LTS | DYN | ja | ja | 2.39 | mittel – 8.3.36+ empfohlen |
| Debian 12 | DYN | ja | ja | 2.36 | mittel – 8.2.35+ empfohlen |
| RHEL 9 / Rocky 9 | DYN | ja | ja | 2.34 | mittel – Vendor-Patch prüfen |
| Alpine 3.20 (musl) | DYN | ja | ja | musl 1.2 | andersartig – musl-Layout |
| Container-Images | imageabhängig | imageabhängig | imageabhängig | imageabhängig | oft hoch – Basisimage veraltet |
Entscheidend ist nicht die PHP-Version allein, sondern ob das Distributionspaket die CVE-Korrektur enthält. Ein Paket kann eine ältere Upstream-Version tragen und trotzdem gepatcht sein.
# Debian/Ubuntu
apt changelog php8.1-fpm 2>/dev/null | grep -i 'CVE-2026-10961'
zgrep -E 'CVE-2026-10961[89]' /usr/share/doc/php8.1-fpm/changelog.Debian.gz 2>/dev/null
# RHEL-Familie
rpm -q --changelog php-fpm | grep -i 'CVE-2026-10961'
# Container
docker run --rm php:8.3-fpm php -v
docker inspect php:8.3-fpm --format '{{json .Config.Labels}}'
3. Ausnutzung des Master-Worker-Modells
Ein Angreifer kann die fork-Vererbung in drei Schritten nutzen.
- Layout lernen: Ein harmloser Request erzeugt eine Fehlermeldung oder ein Timing-Muster, das Rückschlüsse auf Basisadressen erlaubt.
- Fehlversuche wiederholen: Ein Absturz beendet nur den Worker. Der Master startet sofort einen neuen, der dieselbe Layoutbasis erbt.
- Erfolgswahrscheinlichkeit kumulieren: Bei mehreren hundert Requests pro Sekunde steigt die Trefferwahrscheinlichkeit schnell.
journalctl -u php8.3-fpm --since "30 min ago" | \
grep -E 'WARNING|seems busy|SIGSEGV|SIGABRT|terminated'
journalctl -u php8.3-fpm --since "1 hour ago" --output=json | \
jq -r 'select(.MESSAGE | test("exited|terminated")) | .__REALTIME_TIMESTAMP' | wc -l
4. Gegenmaßnahmen
4.1 Crash-Throttling im FPM
; /etc/php/8.3/fpm/pool.d/www.conf
emergency_restart_threshold = 5
emergency_restart_interval = 1m
process_control_timeout = 10s
pm.max_requests = 300
Überschreitet die Zahl der Worker-Beendigungen den Schwellwert, stoppt FPM den automatischen Neustart und verlangt Eingriff. Das ist ein bewusster Kompromiss zwischen Verfügbarkeit und Sicherheit.
4.2 ASLR-Status und Diensthärtung
cat /proc/sys/kernel/randomize_va_space
sudo systemctl edit php8.3-fpm
[Service]
LimitCORE=0
NoNewPrivileges=yes
ProtectSystem=strict
PrivateTmp=yes
LimitCORE=0 verhindert, dass Core-Dumps Adressinformationen und Speicherinhalte preisgeben. Für die Forensik kann das kontraproduktiv sein; in Hochsicherheitsumgebungen ist die Abwägung bewusst zu treffen und zu dokumentieren.
4.3 Binärhärtung prüfen
#!/usr/bin/env bash
set -euo pipefail
BIN="${1:-/usr/sbin/php-fpm8.3}"
echo "[*] $BIN"
readelf -h "$BIN" | awk -F: '/Type/{print " ELF Type:" $2}'
readelf -lW "$BIN" | grep -q GNU_RELRO && echo " RELRO: yes" || echo " RELRO: no"
readelf -dW "$BIN" | grep -q BIND_NOW && echo " BIND_NOW: yes" || echo " BIND_NOW: no"
readelf -sW "$BIN" | grep -q __stack_chk_fail && echo " Canary: yes" || echo " Canary: no"
Führen Sie dieses Skript in Ihrer CI aus und schlagen Sie fehl, wenn Härtungsmerkmale fehlen. So verhindern Sie, dass ein neues Basisimage stillschweigend schwächer konfiguriert ist als das vorherige.
5. Timing-Seitenkanäle und Container-Layouts
Neben klassischen Informationslecks existieren subtilere Kanäle. Ein Angreifer kann die Laufzeit eines Requests messen, um zu erkennen, ob eine geratene Adresse gültig war. Ein Treffer führt häufig zu einem längeren Pfad, weil die VM weitere Handler aufruft, bevor sie abstürzt. Diese Differenz liegt im Bereich von Mikrosekunden bis Millisekunden und ist über das Netzwerk schwer zuverlässig zu messen – in Umgebungen mit geringer Latenz und vielen Wiederholungen jedoch durchaus verwertbar. Für Verteidiger bedeutet das: Gleichmäßige Antwortzeiten und konsequentes Crash-Throttling erschweren die Auswertung.
Container verschärfen das Problem auf eine andere Weise. Das Basisimage bestimmt libc, Loader und PHP-Build. Viele Teams verwenden dasselbe Image über Monate, ohne die darin enthaltene PHP-Version zu aktualisieren. Ein docker pull aktualisiert nur das Tag, nicht die laufende Instanz, und ein latest-Tag ist keine Versionsangabe. Prüfen Sie deshalb im laufenden Container:
docker exec <container> php -v
docker exec <container> php -r 'echo PHP_BINARY, PHP_EOL;'
docker exec <container> ldd "$(docker exec <container> which php)" | head
Kubernetes ergänzt das Problem um horizontale Skalierung: Viele kurzlebige Pods mit identischem Image teilen zwar nicht denselben Adressraum, wohl aber dieselben Offsets. Ein Angreifer, der die Image-Version kennt, kann seine Tabelle einmal erstellen und gegen alle Replikate verwenden. Deshalb ist die Image-Digest-Pinning-Strategie nicht nur eine Frage der Reproduzierbarkeit, sondern auch der Angriffskosten.
Ein letzter Aspekt ist die Rolle von JIT und OPcache. Beide verändern die Speicherlayout-Dynamik, beseitigen die Grundprobleme aber nicht. Wer opcache.jit aktiviert, sollte die zusätzliche Komplexität bei der Fehlersuche einplanen; wer JIT deaktiviert, gewinnt etwas Vorhersagbarkeit, verliert aber Performance. Die Entscheidung gehört in ein dokumentiertes Härtungskonzept und nicht in eine spontane Notfalländerung.
6. Grenzen der Betrachtung
ASLR ist eine probabilistische Mitigation, keine Garantie. Sie erhöht die Kosten eines Angriffs, verhindert ihn aber nicht. Bei einem Angriff, der hunderte Versuche pro Sekunde erlaubt und dessen Fehlschläge nur einen Worker kosten, ist die Wahrscheinlichkeit eines Treffers innerhalb weniger Minuten hoch. Deshalb bleibt die entscheidende Maßnahme der Patch auf 8.5.12, 8.4.27, 8.3.36 oder 8.2.35 – und bis dahin die Deaktivierung des RFC1867-Callbacks.
7. Fazit
- Seitenoffsets und fork-Vererbung reduzieren die effektive Entropie.
- Fehlendes PIE oder veraltete Container-Basisimages verschärfen das Problem.
emergency_restart_threshold,pm.max_requestsundLimitCORE=0erschweren Brute-Force-Versuche.- Binärhärtung muss Teil der CI und des Image-Builds sein, nicht nur eine einmalige Prüfung.
Wer ASLR als alleinige Verteidigung betrachtet, überschätzt sie. Wer sie mit Patch-Management, Engine-Härtung und Runtime-Erkennung kombiniert, gewinnt Zeit und reduziert die Erfolgswahrscheinlichkeit erheblich.