Two pieces live here:
p2s_verify.py— the blind, no-artifact exploitability verifier (the safe default).engine/— the low-level use-after-free chain primitive the verifier drives (third-party, MIT; see the repositoryNOTICE). RedSide's dynamic-ASLR calibration is applied on top viaphp2shell-dynamic-aslr.patch.
For the one-command experience (fingerprint → offsets → check → optional HTTP-readback
proof) use ../p2s.py instead. Use the pieces here when you want the blind check on
its own, or to drive a target whose offsets you supply by hand.
Confirms RCE without staging anything, sharing anything, or reading data back. It
runs sleep N (7 bytes — it fits the 14-byte command carrier) through the chain and
times the trigger request: a normal request is sub-second; if code executed, the
destructor runs the sleep and the response is delayed ~N seconds. That delay is the
proof.
# auto: fingerprint the version, pull offsets from ../real-vm/offsets_db.json, sweep htdata
python3 exploit/p2s_verify.py -u http://target/ --sleep 8
# manual: supply offsets for a build not in the database
python3 exploit/p2s_verify.py -u http://target/ \
--zif-offset 0x.. --dtor-offset 0x.. --htdata-offset 0x..Options: --sleep N (timing signal), --sweep K (htdata ±K page sweep per candidate),
--retries N, --version X.Y.Z (skip fingerprint), --zif/--dtor/--htdata-offset.
Output on success:
WORKED - RCE CONFIRMED: command executed (request delayed ~Ns) — and it names the
build offsets that landed. No file, no exfil.
The raw chain filters leaked pointers against a hard-coded userspace window that only
holds on hosts with 28-bit mmap entropy (e.g. Kali). On hosts with
vm.mmap_rnd_bits=32 (Ubuntu 24.04 / kernel 6.8) PHP maps lower and every leak is
discarded. The patch replaces the fixed window with a self-calibrating gate: it
widens the pre-filter to a host-agnostic noise gate and lets the code discover the real
base each run. No ASLR base is assumed and no host hardening is disabled.
p2s.py and p2s_verify.py apply this gate internally, so for the normal drivers you
don't need to patch anything. The .patch file is provided for anyone driving engine/
directly:
git apply exploit/php2shell-dynamic-aslr.patch # from a checkout of the engineThe command is embedded in a fixed 167-byte heap object that also holds a forged
HashTable, execute_data, and string header; the command is the tail, capped at 14 bytes
(167 − 0x98 − 1 NUL). sleep 8 fits directly. For a longer command, use a short
loader such as sh</tmp/c (8 bytes) that reads an arbitrarily long staged script.