Skip to content

Releases: slabbdev/php2shell-cve-2026-109618-109619

v0.2.0 — root cause, trigger reproduction, A/B proofs

Choose a tag to compare

@slabbdev slabbdev released this 21 Oct 15:11

The release-day update. The embargo lifted at 14:00 UTC; here is what the lab shows.

Root cause (from the official diff)

Fix commit 506e87fa7d — "Avoid state corruption via bailout" — fixes both CVEs (GHSA-w8hw-w9fx-j79w / CVE-2026-109618, GHSA-6wmf-4rv5-p3rp / CVE-2026-109619). An OOM during multipart parsing (including inside the session.upload_progress handler) used to trigger zend_bailout — a longjmp with no cleanup — leaving unreset globals and dangling pointers; the next request on that worker re-enters startup on corrupted state (CWE-416 use-after-free, CWE-824). The fix adds a clean UnwindExit path, allocation-failure tracking (ZEND_HAS_OOM()), global resets, and a new oom_rfc1867_* test battery. Patched releases: 8.3.36, 8.4.27, 8.5.12.

Trigger reproduced (proof/)

Official fix-test vector — multipart POST with a 1 MB PHP_SESSION_UPLOAD_PROGRESS value — against the frozen vulnerable build (8.4.28-dev, ASAN, memory_limit=4M): Allowed memory size ... exhausted in Unknown on line 0, 6/6 requests, dying before any application code runs. Same request on the patched build (8.4.27-dev at the fix commit): same user-visible Fatal through the clean UnwindExit path (different allocation pattern), state intact.

Official tests on the patched build

11 passed, 1 skipped (slow-under-ASan), 0 failed — including oom_rfc1867_upload_progress.phpt, the exact blueprint of our trigger.

We do not ship a weaponized UAF-to-RCE chain: the GHSAs document the corruption class, the fix's tests document the triggers, patch day is for fixing. See the README for the impact-payload demo (educational, local-only).

v0.1.1 — race harness v2 + WordPress demo target

Choose a tag to compare

@slabbdev slabbdev released this 21 Oct 12:11

Second pre-release, still pre-patch (the emergency PHP release lands at 14:00 UTC today).

Race harness v2

  • --path: target any PHP route (e.g. /wp-login.php on the WordPress demo)
  • --parts: N small multipart fields — the lever that stretches the race window
  • tolerant liveness probe, composable disturbance modes unchanged

Protocol fix

The harness never sent the final multipart boundary — every "completed" upload
was actually hanging until timeout. Fixed; uploads now complete with 200 OK.

Calibration (measured, not guessed)

  • The race window is the rfc1867 parse loop — php-fpm buffers the request body,
    so slow-streaming the upload is irrelevant.
  • Parts count is the lever under the post_max_size 8M default: 6k-12k parts
    → measured 60-200 ms parse window per request (hundreds of racer shots).
  • With default INI (min_freq=1s), the upload_progress entry never touches
    disk
    — it exists in memory only. The proof instrument is ASAN, not file watching.

WordPress demo target (wordpress-demo/)

Stock WordPress on the frozen vulnerable php:8.4-fpm digest, default PHP
config
— the point being: a default-config stack falls. mysqli compiled at boot,
internal no-egress network for fpm/db, nginx bound to 127.0.0.1:8080, throwaway
creds only.

Next: v0.2.0 after the official release — root-cause diff, ASAN A/B proof
(UAF on vulnerable build, clean on patched build), full PoC.

php2shell-lab v0.1.0 — initial lab (pre-patch)

Choose a tag to compare

@slabbdev slabbdev released this 21 Oct 10:45

Initial local-only lab for CVE-2026-109618 / CVE-2026-109619 (php2shell), built before the emergency PHP release of Oct 21, 14:00 UTC.

  • Frozen vulnerable reference images — php:8.4-cli + php:8.4-fpm, digest-pinned (pulled pre-release; Docker Hub overwrites the tags at 14:00 UTC)
  • Realistic target — nginx + php-fpm with fastcgi_request_buffering off
  • Proven attack surface — attacker-controlled upload_progress_<key> entry written into the server session (feature on by default since PHP 5.4)
  • Race harness — throttled raw-socket multipart upload + composable disturbances (destroy / regenerate / gc / double / abort)
  • ASAN recipe — AddressSanitizer PHP build of any commit (ARG PHP_COMMIT), validated on PHP-8.4 HEAD

Next: v0.2.0 after the official release — root-cause diff, A/B proof (UAF on vulnerable build, clean on patched build), full PoC and remediation details.