Back to Blog
7 min read

PHP2Shell (CVE-2026-109618): critical PHP RCE explained

If your website runs on PHP — and every WordPress, WooCommerce, Laravel, Drupal and Magento site does — October 2026 brought the most serious PHP security issue in years. Two flaws in PHP itself, nicknamed PHP2Shell, let an unauthenticated attacker run code on the web server. CISA has confirmed they are being exploited in real attacks.

This post explains what the flaws are, who is affected, how to check your servers, and what to do if you cannot patch today.

Code on a screen

The short version

  • CVEs: CVE-2026-109618 and CVE-2026-109619 (plus a third, CVE-2026-115560, disclosed two days later).
  • Impact: Unauthenticated remote code execution on the web server.
  • Severity: CVSS 4.0 score of 9.2 from the PHP project (some trackers show 9.8 under CVSS 3.1).
  • Exploited: Yes. CISA added both CVEs to its Known Exploited Vulnerabilities catalog on October 26, 2026, with a three-day deadline for US federal agencies.
  • Fixed in: PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35, released October 21, 2026.
  • Not fixed by the PHP project: PHP 8.1 and older are end of life. Minimal source backports exist for 7.4, 8.0 and 8.1, but you should get a patched package from your vendor or upgrade.

What the two flaws are

Both bugs are in the PHP engine, not in an extension or a framework, which is why they affect almost every PHP application.

CVE-2026-109618: memory corruption on out-of-memory

PHP enforces memory_limit by jumping out of whatever it is doing when a request runs out of memory. If that happens at the wrong moment — for example, while PHP is still parsing a form-based file upload — some internal request data is only partly set up. During cleanup, PHP can then treat that half-initialised data as valid and free memory that may still contain attacker-controlled bytes.

The PHP Foundation says that on Apache with mod_php, a single unauthenticated request can trigger this before any application code runs, and full remote code execution was demonstrated with default settings. RCE was also confirmed on PHP-FPM through application code paths.

CVE-2026-109619: stale state between requests

PHP workers handle many requests one after another. If a fatal error happens at the wrong point, internal state belonging to that request can survive after its memory has been freed. The next request on the same worker may then use that stale state before it is reset — a classic use-after-free. The reporter demonstrated RCE against a real-world application on Apache with mod_php.

CVE-2026-115560: a third, narrower issue

On October 23, the PHP project disclosed a third RCE issue in session upload progress. It only applies when session.serialize_handler is set to php_binary or php_serialize in configuration and then switched to php at runtime with ini_set() before session_start(), with upload progress still enabled. The project judged it unlikely to be a risk and is fixing it in the next regular release, with session.upload_progress.enabled=0 as the interim mitigation.

Who is affected

According to the PHP project, all PHP installations that serve web requests: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. The risk is highest with mod_php. Products that bundle their own PHP runtime (control panels, appliances, some desktop and NAS software) may be affected too; NCSC-NL maintains a list of affected third-party software at github.com/NCSC-NL/php2shell.

For WordPress site owners this is the important part: updating WordPress, your theme or your plugins does not fix this. The fix is in PHP itself, which is managed by your host or by whoever runs your server.

How to check your version

On the server:

php -v           # CLI version
php-fpm8.3 -v    # FPM version (adjust the binary name to your PHP version)

The CLI and the version actually serving your site can differ. To see what the web server really runs, create a temporary file with <?php echo PHP_VERSION;, open it in the browser, then delete it. In WordPress, Tools → Site Health → Info → Server shows the PHP version too.

You are safe from these two CVEs if the version is 8.5.12+, 8.4.27+, 8.3.36+ or 8.2.35+, or if your distribution's security advisory says its package includes the fix. Distributions and LTS vendors received the patches in advance, so their package versions often have their own numbering — check the advisory, not just the version string.

How to fix it

  1. Update PHP to a fixed release, or install your distribution's patched package:

    # Debian / Ubuntu
    sudo apt update && sudo apt install --only-upgrade 'php8.*'
    
    # RHEL / AlmaLinux / Rocky
    sudo dnf update 'php*'
    
  2. Restart every PHP worker so no process keeps running the old code:

    sudo systemctl restart php8.3-fpm   # or your PHP-FPM service name
    sudo systemctl restart apache2      # if you use mod_php
    
  3. Prioritise internet-facing servers, and mod_php setups first.

  4. On shared or managed hosting, ask your host to confirm in writing that your PHP build includes the October 21 fixes. If you are still on PHP 8.1 or older, this is the moment to move to 8.3 or 8.4 — 8.2 itself reaches end of life on December 31, 2026.

A note for users of the popular Remi repository: on release day no updated packages were available there, and the PHP team advised switching to distribution packages or building from source until they were.

If you cannot patch today

The PHP project lists mitigations that reduce exposure but do not make an installation safe. Use them only as a bridge to the update:

; CVE-2026-109619 and CVE-2026-115560 — set before request parsing, then restart all workers
session.upload_progress.enabled = 0
fatal_error_backtraces = 0   ; PHP 8.5 only

; CVE-2026-109618
memory_limit = -1            ; set with php_admin_value in the FPM pool or Apache config
post_max_size = 8M           ; or 32M if you need uploads; keep upload_max_filesize below it

Important details from the advisory:

  • Do not rely on ini_set() or .user.ini for the session setting; it must apply before the request is parsed.
  • With memory_limit = -1, put a memory limit on the container or cgroup instead so one runaway worker cannot exhaust the server. Do not use RLIMIT_AS or RLIMIT_DATA.
  • Enforce request size, field count and multipart part limits at the web server or reverse proxy.
  • If none of this is possible, take internet-facing PHP servers off the public network until they are patched.

What I would do this week

  1. Inventory every server and container image that runs PHP, including staging sites and old projects nobody looks at.
  2. Patch and restart workers, starting with public-facing mod_php hosts.
  3. Rebuild Docker images — an old php:8.3-apache image pulled months ago still contains the vulnerable PHP.
  4. Check web server logs since mid-October for unusual multipart uploads and repeated PHP fatal errors or worker crashes, and review the server for unexpected files, cron jobs and processes if you were exposed.
  5. Plan the move off any PHP version that no longer receives security fixes.

If you run WordPress sites and are not sure what PHP version your host gives you, or you want someone to check and patch your server, book a free consultation and I will take a look.

Sources

Feel Free to Share:

Related Articles

More articles on WordPress, DevOps and web performance

A remotely reachable use-after-free in the Linux kernel's IPv4 TCP handshake, present since kernel 4.4. How serious it is, how to check your kernel, and how to patch, livepatch or mitigate it.

Next.js 16.4.5 and 15.5.29 fix seven vulnerabilities, mostly cache bugs that can leak one user's or tenant's data to another. Which apps are affected and how to upgrade safely.

A critical Remote Code Execution (RCE) vulnerability dubbed 'React2Shell' has been discovered in Next.js, affecting versions using React Server Components. Learn how to protect your application.

Need a WordPress site that is fast, secure and stays online?

Tell me about your site and goals. I will tell you what I would change and what it would take.

  • Free 15-minute call
  • No commitment
  • Clear next steps