Local-only PoC lab for CVE-2026-109618 / CVE-2026-109619 (php2shell) — two critical RCEs in PHP core, session.upload_progress, on by default since 5.4. Docker A/B (vulnerable vs patched builds), race harness, ASAN proof.
# php2shell-lab — CVE-2026-109618 / CVE-2026-109619 lab
⚠️ **LOCAL TARGET ONLY (127.0.0.1).** Never aimed at third-party systems. PoC for
demonstration and remediation purposes only.
## Layout
```
php2shell-lab/
├── docker-compose.yml # fpm+nginx (realistic target), vuln-stock (php -S), ASAN A/B
├── target/www/index.php # target: session + peek/destroy/regenerate/gc actions
├── fpm/zz-lab.ini # lab INI (cleanup=Off, freq=1, min_freq=0)
├── fpm/nginx.conf # fastcgi_request_buffering off (streamed body)
├── asan/Dockerfile # AddressSanitizer PHP build of an arbitrary commit (ARG PHP_COMMIT)
├── harness/race.py # throttled multipart upload + concurrent disturbances
```
## Vulnerable images, frozen (pulled BEFORE the Oct 21, 14:00 UTC release)
- `php:8.4-cli@sha256:e2432ac8…` (pulled 11:15 CEST)
- `php:8.4-fpm@sha256:f5431b57…` (pulled 11:25 CEST)
Docker Hub overwrites the tags with patched builds at release time — the digests
pinned in docker-compose.yml keep a reproducible vulnerable reference.
## Start
```bash
docker compose up -d fpm nginx # realistic target on 127.0.0.1:8898
curl 'http://127.0.0.1:8898/index.php?a=ping'
```
## Harness (composable disturbances)
```bash
python3 harness/race.py --port 8898 --modes destroy --rounds 20
# modes: destroy | regenerate | gc | double | abort (comma-separated)
# --modes "" = slow upload alone (observation)
```
Proven in the lab: after a completed upload, the session contains the
`upload_progress_<attacker-key>` entry (full metadata). The feature is **enabled by
default** since PHP 5.4 — which is exactly where the leak's "every PHP ≥ 5.4" claim comes from.
## T0 procedure (after the Oct 21, 14:00 UTC release)
```bash
cd php-src && git fetch origin
git log --oneline origin/PHP-8.4 -15 # locate the fix commit(s)
git diff <parent>..<fix> -- ext/session/ # root cause
# ASAN A/B (~10 min builds each):
PHP_COMMIT_VULN=<parent sha> PHP_COMMIT_FIXED=<fix sha>