PHP2Shell is a PHP runtime security problem, not a WordPress-only plugin issue. The name refers to two critical PHP vulnerabilities that can allow an unauthenticated remote attacker to execute code with the permissions of a web worker. This WebCareSG guide explains how to check your PHP runtime safely, apply the correct fix, and investigate a website if you suspect it was exposed.
Do not try to reproduce the vulnerability against a live website. A safe review begins with the PHP version, the web-serving SAPI, the installed packages, and the logs and files that changed around the time of suspicious activity.
What is PHP2Shell?
PHP2Shell is the name commonly used for two critical PHP issues:
- CVE-2026-109618: remote code execution through an out-of-memory corruption path.
- CVE-2026-109619: remote code execution through stale global state that can survive across PHP worker requests.
Both issues affect the PHP engine itself. The PHP security team says that all PHP installations serving web requests should be assessed, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP, and LiteSpeed. The exact exposure depends on the PHP version, SAPI, extensions, configuration, and application request paths.
If exploited, the attacker may be able to run commands as the PHP worker, read or change website files, add a web shell, steal credentials, or use the server to attack other systems. A clean homepage does not prove that the server is safe.
Which PHP versions contain the fix?
Upgrade to one of these fixed releases or a newer vendor package:
| PHP branch | Minimum fixed release |
|---|---|
| 8.5 | 8.5.12 |
| 8.4 | 8.4.27 |
| 8.3 | 8.3.36 |
| 8.2 | 8.2.35 |
PHP 8.1 and older do not receive normal security updates from the PHP project. Your operating-system or hosting vendor may provide a backported fix, but you should plan to move to a supported PHP branch. PHP 8.2 also reaches end of life on 31 December 2026, so it should be treated as a transition branch rather than a long-term destination.
How to check for PHP2Shell exposure
Step 1: Check the PHP version used by the website
From the server command line, check the CLI version:
php -v
php --ini
The CLI version may not be the same version serving the website. Also check the hosting control panel, PHP-FPM pool, Apache module, or RunCloud application settings. If several PHP versions are installed, inspect every virtual host and pool.
Step 2: Identify the web SAPI and active workers
Find out whether the site uses PHP-FPM, Apache mod_php, php-cgi, LiteSpeed, or another PHP-serving mode:
ps aux | grep -E '[p]hp-fpm|[p]hp-cgi|[a]pache2|[h]ttpd|[l]itespeed'
This matters because the vulnerable request paths and the restart procedure differ between SAPIs. Do not assume that upgrading the CLI binary also upgrades the PHP workers serving web traffic.
Step 3: Compare every web-facing PHP runtime with the fixed versions
Any web-facing runtime below the fixed release for its branch should be treated as needing urgent action. A version hidden from the HTTP response is still vulnerable if the underlying worker is old.
Step 4: Use a remote check as triage, not as proof of patching
You can use the WebCareSG PHP2Shell Checker to test an authorised, publicly reachable website. It can help identify whether the exposed service responds like a vulnerable target, but it cannot see a private origin, verify every PHP pool, or replace a server-side version check. A “not vulnerable” result should always be followed by confirmation of the installed PHP package.
Never upload a proof-of-concept file or send exploit payloads to a production website. If you need a deeper review, use a staging copy or ask a security professional to perform an authorised assessment.
Step 5: Review logs for signs of abuse
Review the web-server, PHP-FPM, PHP error, reverse-proxy, and hosting logs around the time the site was exposed. Look for:
- unexpected POST requests to PHP endpoints;
- bursts of 400, 500, or 502 responses followed by worker restarts;
- fatal errors, memory exhaustion, timeouts, or repeated initialization warnings;
- new PHP files, modified plugins or themes, unknown administrator accounts, or outbound requests from the web worker;
- commands or child processes launched by PHP that are not part of normal application behavior.
For a first review, list PHP files changed recently in the site directory. Adjust the path and time window for your server:
find /var/www/example.com -type f \
\( -name '*.php' -o -name '*.phtml' \) \
-mtime -14 -print
The absence of a matching log entry does not prove that the site was not attacked. Historical logs may be incomplete, and some vulnerable paths can fail before the application writes a useful message.
How to fix PHP2Shell
Step 1: Back up and record the current state
Take a tested database and file backup before changing the runtime. Record the PHP version, SAPI, loaded extensions, active sites, PHP-FPM pools, recent log findings, and current credentials. If you see signs of compromise, preserve a copy of the affected files and logs for investigation.
Step 2: Apply the vendor PHP security update
Use your Linux distribution, hosting provider, RunCloud, Plesk, cPanel, or PHP vendor to install the fixed package. Do not replace the server PHP binary with an unrelated download without checking the operating system and application dependencies.
For source-built PHP, use the official PHP security release or a trusted vendor backport. The target is PHP 8.5.12, 8.4.27, 8.3.36, or 8.2.35, or a newer patched release.
Step 3: Restart every PHP worker
After the package update, restart the service that actually serves the website. For PHP-FPM, the service name depends on the installed branch, for example:
sudo systemctl restart php8.3-fpm
For Apache mod_php or LiteSpeed, follow the service-specific restart procedure. A package update without replacing old workers leaves existing processes running the old code.
Step 4: Use temporary controls only if an update is delayed
The PHP team states that updating is the only complete solution. If your vendor has approved an emergency delay, set session.upload_progress.enabled=0 in startup configuration such as php.ini or the PHP-FPM pool, then restart every worker. Do not rely on ini_set() or a per-directory .user.ini setting for this protection.
This blocks a specific session-upload path; it does not make an old PHP runtime safe. Restricting public PHP endpoints, applying a WAF rule, reducing unnecessary uploads, and limiting worker exposure can reduce risk while you patch, but none is a replacement for the vendor update.
Step 5: Investigate and recover if compromise is possible
If you find suspicious files, accounts, commands, or logs:
- isolate the site or restrict it to a maintenance page;
- preserve logs and a forensic copy before deleting files;
- scan the webroot, database, plugins, themes, cron jobs, and server accounts;
- rotate WordPress, hosting, SSH, FTP, database, API, and email credentials;
- restore only from a known-clean backup after checking how persistence was created;
- patch PHP and all application components before returning the site to public traffic.
Step 6: Verify the repair
Confirm the version used by the web workers, not only the CLI:
php -v
systemctl status php8.3-fpm --no-pager
Then retest the website, uploads, forms, scheduled jobs, and PHP-FPM health. Re-run an authorised external check if appropriate, review new logs, and monitor for unexpected files or processes.
Sources and further reading
- PHP security advisory and update guidance
- PHP advisory: RCE via OOM corruption
- PHP advisory: RCE via stale global state
- PHP 8 ChangeLog and fixed releases
Security guidance changes as vendors publish new information. Confirm the fixed package and restart procedure with your operating-system or hosting provider before changing a critical production server.