Repository navigation
Conversation
The Apache stage-1/2/3 chain does not transfer to PHP-FPM: a pre-RINIT
OOM kills the worker (FPM_EXIT_SOFTWARE). This adds the FPM-viable
primitive instead, exploiting the same CVEs through the stale
BG(unserialize) state:
- producer: post-RINIT OOM leaves {level=1, data=d} dangling
- consume: pre-RINIT rfc1867 reuses d; a 4 MB builder-pool body
reclaims the 8216-byte struct and forges var_hash
- R:2 becomes an IS_REFERENCE -> fake HashTable whose pDestructor
is zif_system; session shutdown fires the command (verified:
uid=1000 proof in the nginx+FPM lab)
Ships tools/php2shell_fpm.py (arm/reclaim/fire client, patchcheck),
tools/php2shell_fpm_labs.py (user-prefix nginx+FPM lab, no Docker),
profiles/8.4.26-fpm.json, schema extensions (sapi: fpm, fpm block),
FPM dispatch in php2shell_auto.py (--sapi/--builder), unit and
lab-gated integration tests, and docs/fpm-chain.md. patchcheck
confirms 8.4.27 is clean (level=0, workers survive).
patchcheck iterates both lab builds and takes no --base; endgame and primitive require --base/--builder before the subcommand name.
- redact the local lab username from the published proof example - document the FPM profile in docs/profile-inventory.md (table synced with php2shell_inventory.py output, including the pre-existing missing 8.3.0/8.5.0 rows) - state the two-slot reclaim/retry behaviour in the profile status and the README table (probabilistic disclosure) - correct the README profile counters (14 profiles, 6 verified RCE)
- add `leak` and `no-oracle` subcommands: leak the Zend-MM chunk and php-fpm PIE bases from a stale short-session string OOB read, then brute-force the two reclaim slots without reading /proc/<pid>/mem - keep `--chunk`/`--pie` overrides and a /proc/<pid>/maps fallback; make `endgame` fall back to the no-oracle geometry when the oracle probe finds no cover - record the verified leak geometry and no-oracle candidates in the FPM profile, extend the profile schema, and document the builder arm page - add leak/dechunk unit tests, a live no-oracle RCE test, and lab helper pages (`bigvar.php`, `leakvar.php`, `refb.php`) plus lab-only app diagnostics - update README, the FPM chain guide, and the profile inventory; correct the unit-test count and remote-FPM limitation wording Verified on the 8.4.26 nginx+FPM lab: leak prints chunk=0x7f4f48800000 pie=0x55d75fc00000; no-oracle reaches uid=1000 without --chunk/--pie; 51 unit tests pass (3 jsonschema live tests skipped unless installed), and primitive/endgame/no-oracle integration tests pass.
Replace the FPM client and labs launcher with the version-aware chain and add the supporting tools: - php2shell_fpm.py gains a fully remote `calibrate` (derives the leak and reclaim geometry of any 8.0+ build over HTTP), the pre-auth session-file `poison` that covers PHP <= 7.x, and a `no-oracle` endgame that refuses the local /proc fallback unless explicitly asked. - php2shell_fpm_versions.py is the single source of truth for the affected-version matrix (is_vulnerable / fixed_in / source_is_fixed classify a tarball by the upstream fix marker, not a version string). - php2shell_fpm_profile.py drafts a profile from an unstripped php-fpm using nm plus php2shell_fpm_offsets.c, compiled per build for the struct layouts. - php2shell_fpm_matrix.py verifies the remote chain on every unpatched build, one lab at a time. - build_php.sh retries truncated tarballs and patches the two glibc misdetections that stop 7.x from building with a current toolchain; build_all.sh builds several versions in parallel. Verified on 8.2.34, 8.3.35, 8.4.0, 8.4.25 and 8.4.26 (amd64). 8.5.x is intentionally out of scope; the fix ships in 8.5.12.
Ship the calibrated profiles for the last vulnerable release of every tracked branch plus the earliest 8.4, all marked verified/rce: 8.2.34, 8.3.35, 8.4.0, 8.4.25, 8.4.26 Each records the symbols, the BG(unserialize) offsets, the reclaim geometry, the HTTP-only leak/no-oracle geometry and the compiled struct layout for that build. Introduce profiles/fpm-schema.json as the strict validator for FPM profiles (it requires the fpm block, status, oom_mb and distro), and add the `layout` block to the generic profiles/schema.json so every build profile -- Apache and FPM alike -- still validates in a single pass. The inventory test skips both schema files instead of mistaking one for a profile.
The pre-existing research tools were written before ruff was adopted and never linted (the old `lint` target only ran gitleaks). Now that ruff enforces E/F/W/I/UP/B/SIM, fix the findings so `make lint` and CI pass repo-wide: - wrap one-shot open(...).read() calls in context managers (SIM115); - drop the redundant UTF-8 coding declarations and open() mode args; - use builtin generics and f-strings instead of typing/percent formats; - sort the imports. Behavior is unchanged; the unit suite still passes.
Makefile: add targets for building nginx/PHP (build-nginx, build-php, build-all), drafting a profile (profile-draft), remote calibration (calibrate-remote), the remote chain (remote) and the unpatched matrix (matrix). Split the rootless FPM lab targets (labs-fpm-*) from the Docker labs, add labs-fpm-smoke/ports, and split profile validation into the generic schema and the strict FPM schema. `lint` now runs ruff (matching CI) and the gitleaks scan moves to its own `secrets` target. CI: lint with ruff, compile the tools, check shell syntax, run the unit suite, and validate both the generic and FPM profile schemas, alongside the existing gitleaks secrets job. Add the manual live-lab workflow (build + exploit 8.4.x) and the tag-triggered release workflow, plus a dev requirements file (jsonschema, ruff) the CI installs. pyproject: keep the php2shell identity but modernize the build backend (setuptools>=77, build_meta, PEP 639 license-files) and add the php-fpm keyword.
README: the status table now lists the five verified FPM builds, the features section describes the remote calibrate/poison/no-oracle flow and the version registry, and the FPM lab quick start uses the corrected `up --only` CLI with build/calibrate/poison/matrix examples. Update the repository layout and drop the stale claim that the FPM chain always needs a /proc oracle -- only the lab endgame/primitive modes do. docs/fpm-chain.md: add the affected-version table, the PHP <= 7.x session-file plant (poison), remote calibration and the unpatched matrix, expand the lab port map to every verified build, and fix `up --only`. docs/labs.md: give the rootless nginx+FPM laboratory its own section with the build steps and the full port map, linking back to fpm-chain.md.
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
NGINX PHP-FPM VERSIONS