exploitation php2shell aslr bypass

ASLR ohne Info-Leak umgehen: Distributions-Härtung, PIE und fork-Ausnutzung

php2shell.run Research Lab •

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

Distributionphp-fpm PIEFull RELROStack CanarylibcEinordnung
Ubuntu 22.04 LTSmeist DYNteilweiseja2.35mittel – Backport prüfen
Ubuntu 24.04 LTSDYNjaja2.39mittel – 8.3.36+ empfohlen
Debian 12DYNjaja2.36mittel – 8.2.35+ empfohlen
RHEL 9 / Rocky 9DYNjaja2.34mittel – Vendor-Patch prüfen
Alpine 3.20 (musl)DYNjajamusl 1.2andersartig – musl-Layout
Container-Imagesimageabhängigimageabhängigimageabhängigimageabhängigoft 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.

  1. Layout lernen: Ein harmloser Request erzeugt eine Fehlermeldung oder ein Timing-Muster, das Rückschlüsse auf Basisadressen erlaubt.
  2. Fehlversuche wiederholen: Ein Absturz beendet nur den Worker. Der Master startet sofort einen neuen, der dieselbe Layoutbasis erbt.
  3. 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

  1. Seitenoffsets und fork-Vererbung reduzieren die effektive Entropie.
  2. Fehlendes PIE oder veraltete Container-Basisimages verschärfen das Problem.
  3. emergency_restart_threshold, pm.max_requests und LimitCORE=0 erschweren Brute-Force-Versuche.
  4. 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.