php2shell: how LFI, upload and filter chains turn into RCE in PHP
Defensive guide to php2shell: how LFI, insecure upload, phar and PHP filter chains turn into RCE, real CVEs and public tools to validate and fix.

Looking for the October 2026 PHP2Shell CVEs (CVE-2026-109618 and CVE-2026-109619)? Read what to do now.
Summary: "php2shell" is not a single tool but a family of techniques that turn a common PHP flaw — Local/Remote File Inclusion, insecure upload,
phar://deserialization, log poisoning, and the modern PHP filter chains — into code execution (RCE) or a web shell. This guide is defensive: it explains how to detect, validate with public tools, and fix each path, without handing out ready-to-run exploits.
Almost every serious compromise chain in PHP applications ends in the same place: the attacker gets the interpreter to run their code. The starting point varies — a parameter that reaches include, a weakly validated upload form, a library that opens files with phar:// — but the destination is always a shell. Understanding these bridges is the first step to closing them.
What "php2shell" means
The term describes the transition from a weak primitive to RCE. An arbitrary file read is already serious on its own; but the defender’s real concern is when that read (or write, or inclusion) can be escalated into running commands on the server. Mapping those transitions is exactly what we do in a web application penetration test.
The most common bridges
- Local File Inclusion (LFI) to RCE. A user-controlled parameter reaching
include/requirelets an attacker include server files. Classically this required a "poisonable" file (logs, sessions). Today the PHP filter chain technique removed that prerequisite. - Insecure upload. Validation by extension or
Content-Type, executable upload directories, or predictable names let an attacker upload a.phpand reach it directly. phar://deserialization. File operations on a controlledphar://path can trigger metadata deserialization and gadget chains.- Log/session poisoning. Injecting PHP into a resource that is later included.
PHP filter chains: why LFI got more dangerous
The filter chain technique, published by Synacktiv in 2022, was a turning point. By chaining iconv charset conversions inside php://filter, an attacker can forge arbitrary bytes in memory from a sink that only reads a file. In practice, an LFI once rated "read-only" now allows RCE with no upload, no writable file, and no log access. The lesson for defenders is blunt: stop treating any file inclusion as low risk. Source: thehacker.recipes — PHP wrappers and streams.
Technique, example CVE, and how to validate
| Technique | CVE / example | How to validate (authorized environment) | Fix |
|---|---|---|---|
| Insecure upload to web shell | CVE-2024-11635 (WP File Upload, RCE, CVSS 10) | Try sending an unexpected file and check if it lands in a served/executable directory | Allow-list by magic bytes, no-execution directory, random names |
| Unauthenticated REST upload | CVE-2025-13329 (File Uploader for WooCommerce, CVSS 9.8) | Check REST endpoints that accept files without capability/nonce |
Authn and authz on the endpoint; validate type server-side |
| Command-parameter injection (mailer) | CVE-2016-10033 (PHPMailer < 5.2.18, CVSS 9.8) | Confirm the dependency version (composer.lock) |
Update the library; validate/sanitize the sender |
| LFI to RCE (filter chain) | Synacktiv technique (a class, not a single CVE) | Verify whether parameters reach include/require with php:///data:// wrappers |
Allow-list of includable files; never concatenate input into include |
phar:// deserialization |
GHSA phar-stream-wrapper | Map file calls that accept a controlled path | stream_wrapper_unregister('phar') when unused |
Public tools to validate (defensive use)
Use these tools only on systems you are authorized to test. They confirm exposure before an attacker does.
synacktiv/php_filter_chain_generator
A Python script that generates php://filter chains to assess whether an inclusion sink is exploitable. In a defensive context it is used to prove reachability of a suspect parameter to include and to prioritize the fix. Repository: https://github.com/synacktiv/php_filter_chain_generator.
PayloadsAllTheThings (File Inclusion section)
A highly reputed reference collection with the LFI/RFI taxonomy and PHP wrappers — excellent for building test cases and review checklists. Repository: https://github.com/swisskyrepo/PayloadsAllTheThings.
projectdiscovery/nuclei
A template-based scanner using YAML templates, with public templates for LFI, path traversal, and RCE patterns. Run nuclei -u https://target -tags lfi,rce for fast, reproducible triage in a pipeline. Repository: https://github.com/projectdiscovery/nuclei.
wpscanteam/wpscan
For the WordPress ecosystem, where most upload/RCE CVEs appear in plugins. wpscan --url https://target --enumerate vp lists known vulnerable plugins. Repository: https://github.com/wpscanteam/wpscan.
sullo/nikto
A classic web server scanner, useful for finding exposed files, open upload directories, and default configurations. Repository: https://github.com/sullo/nikto.
semgrep, Psalm, and PHPStan (static analysis)
The cheapest validation is in the code itself. Semgrep (https://github.com/semgrep/semgrep) finds input reaching include/system. Psalm (https://github.com/vimeo/psalm) and PHPStan (https://github.com/phpstan/phpstan) track taint and types, flagging untrusted data that reaches dangerous sinks. Run them in CI to block the bridge before deploy.
What changed in PHP 8.x
PHP 8 reduced — but did not eliminate — some paths. Automatic phar metadata deserialization in common file operations was removed; RCE only returns if code explicitly calls Phar::getMetadata(). allow_url_include remains Off by default, making direct remote RFI harder. But filter chains, insecure uploads, and local LFI remain fully viable — the surface moved, it did not vanish. Source: pentesterlab — PHAR Deserialization.
Remediation checklist
- Never concatenate input into
include/require; use a fixed allow-list of files. - Validate uploads by magic bytes; serve uploads from a domain/directory with no PHP execution.
- Keep
allow_url_include=Offand unregister unused wrappers. - Run static analysis (Semgrep/Psalm/PHPStan) in CI and scanners (nuclei/wpscan) in staging.
- Patch dependencies and monitor CVEs for the plugins/libraries you use.
Validating these bridges takes method and authorization — that is what separates a pentest from an automated scanner. If you also run APIs alongside the PHP app, see our guides on API penetration testing and BOLA/IDOR.
Frequently asked questions
Is "php2shell" a malware?
No. It is an umbrella term for techniques that escalate a PHP flaw into code execution. The risk is the vulnerability class in your application, not a specific file.
Does upgrading to PHP 8 protect me?
It helps with phar and RFI, but it does not fix insecure upload, local LFI, or filter chains. Code and configuration fixes remain mandatory.
Is a WAF enough?
A WAF reduces noise and blocks obvious payloads, but well-built filter chains and uploads frequently pass. Treat the WAF as a layer, not a fix.
How much does it cost to validate this in a pentest?
It depends on the application size and number of sinks. See our reference on penetration testing cost to scope it.