Imbutus
← Attack

php2shell: three chains BETA

Same advisory, three different attacks. They need different targets and prove different things.

A result belongs to the chain that produced it. "php2shell works" alone means nothing.

ChainNeedsProves
A — application sinkAn include parameter, writable session dirCode as the web user, in the app
B — CVE engine chainmod_php prefork, one worker, display_errors, unpatchedCommand output on the host (no sink)
C — availability on FPMFPM pool, body above memory_limitTake-down only, never access

Chain A — the application's own sink

Upload progress writes a file; the app includes it. No memory corruption, no offsets.

  • An include / LFI parameter the app already has, discovered not assumed
  • A writable session directory
  • A body larger than memory_limit
  • Works on PATCHED PHP — it is not the 2026 CVE

php2shell lfi https://site/ --attempt --upload / --lfi "https://site/?page={page}"

A marker we uploaded executes as the web user. The finding is the application's: so is the fix.

Calling this "php2shell RCE" is wrong: the PHP patch does not stop it.

Chain B — the CVE engine chain

Out-of-memory plus stale request state corrupts freed memory; a fake HashTable with pDestructor = system() runs a command.

  • mod_php under Apache prefork, not FPM
  • The same worker must serve both requests: one worker, or pinning. Multiple workers are fine
  • display_errors on, memory_limit small vs body, session.upload_progress on, use_strict_mode off
  • Unpatched 8.3 / 8.4 / 8.5 and a build the offsets fit

php2shell-chain https://site/index.php --attempt -c "id>p.txt" --fetch /p.txt

PROOF: uid=33(www-data) — command output, with the leaked chunk and libphp bases before it.

Two smaller steps come first, a 4096-byte Set-Cookie leak and a libphp leak. Leaks without a PROOF mean wrong layout, not a safe site.

Proven on the 8.3 / 8.4 / 8.5 official builds; the build is discovered by sweeping the table.

Chain C — availability on FPM

Oversized form fields exhaust the pool; FPM exits each worker with code 70 and replaces it.

  • PHP-FPM, any pool size
  • memory_limit below the body, no edge body cap
  • N requests empty a pool of N

php-dos https://site/ --attempt --workers 20 --mb 12

Request failures and latency. It never yields a shell, and we never report it as access.

This is the honest answer when chain B cannot apply: take-down possible, code execution no.

Which one will the run use?

The plan decides, in this order:

  • A sink is recorded for the target → chain A first: seconds, no offsets, works on patched PHP
  • No sink, mod_php prefork, unpatched → chain B
  • No sink, FPM → chain C, reported as availability only
  • A chain never inherits another chain's result

Unverified or out of reach: leak shapes for PHP 5.4–8.2, display_errors=Off, Windows. No PROOF line means not proven, never "safe".