PHP2ShellCVE-2026-109618, CVE-2026-109619: critical RCE, exploited in the wild.Read the advisory
Security advisory · PHP engine

PHP2Shell: two critical remote code execution flaws in the PHP engine, fixed 21 October 2026.

CriticalCVSS 9.2 · v4.0CVE-2026-109618CVE-2026-109619Exploited in the wild

PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 fix two critical vulnerabilities that can allow unauthenticated remote code execution as the web-server account. The PHP project calls them out together as a single advisory; this toolkit tracks them as PHP2Shell. A working, unauthenticated exploit against Apache mod_php with default settings has been demonstrated. There is no complete configuration mitigation: updating is the only safe choice.

Fixed releases
  • 8.58.5.12
  • 8.48.4.27
  • 8.38.3.36
  • 8.28.2.35

Sources: php.net release archive (2026), GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp.

What happened

On 20 October 2026 the PHP project published advance notice of a critical security release, scheduled for 21 October 2026 at 14:00 UTC. The release shipped on schedule and fixes two independent memory-safety defects in the PHP engine. Both are reachable before any application code runs, both are rated CVSS v4.0 9.2 (critical), and both can lead to remote code execution by an unauthenticated attacker. The flaws have existed since PHP 5.4.0.

The two bugs are described in plain language below, at the level of detail the official advisories give. This page contains no exploit code, proof-of-concept, or trigger recipe.

CVE-2026-109618GHSA-w8hw-w9fx-j79wCVSS 9.2

Remote code execution via out-of-memory state corruption

PHP exits with longjmp() when it runs out of memory. Request memory can be reachable before it is fully initialised, so an out-of-memory error inside that window leaves invalid state that request shutdown later processes as valid. Multipart request parsing, which runs before any application code, can trigger it. Confirmed impact: unauthenticated remote code execution as the web worker account.

The fixThe fix removes longjmp() for out-of-memory errors, replacing it with a soft limit at the next VM safe point and a hard limit that calls _exit().

Introduced in PHP 5.4.0 · CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N · CWE-416, CWE-755, CWE-824, CWE-1325, CWE-1419

CVE-2026-109619GHSA-6wmf-4rv5-p3rpCVSS 9.2

Remote code execution via stale request state (use-after-free)

Request-owned state (error, session, object and extension state) can stay published in persistent worker globals after the request heap has been freed. If a later request on the same worker touches that state before normal initialisation resets it, the result is a use-after-free. Confirmed impact: unauthenticated memory disclosure and remote code execution as the web worker account.

The fixThe fix resets all request-owned state when a request is abandoned.

Introduced in PHP 5.4.0 · CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N · CWE-416

Are you affected?

Almost certainly, if you serve web requests with PHP older than the fixed releases. The vulnerable code paths run during request start-up and multipart request parsing, so they do not depend on your application's code. Every server-side PHP engine (SAPI) that handles web requests should be treated as affected until patched.

Apache mod_php is the highest risk.
Both advisories confirm a working remote-code-execution exploit against mod_php with default settings, because directory-level .htaccess and php_value/php_flag directives are applied before request start-up. PHP-FPM, FrankenPHP, LiteSpeed (LSAPI) and php-cgi are affected or treated as affected; see the per-SAPI breakdown below.
Quick check

Is your PHP version patched?

Type the version from php -v (for example 8.3.12). This runs entirely in your browser against PHPShield's php.net data; nothing is sent anywhere.

Run the full checker on /check for php.ini, composer.lock, webshell and exposed-key scans, all local to your machine.

Attack surface by SAPI

How each PHP server API is exposed, taken directly from the two advisories. “Highest” means a complete exploit has been demonstrated; “high” means affected or treated as affected without a complete public demonstration.

CVE-2026-109618 · Remote code execution via out-of-memory state corruption
Server API (SAPI)RiskHow it is exposed
Apache mod_php
apache2handler
highestExploit validated
PHP-FPM
fpm-fcgi
highAffected by source analysis; no complete exploit demonstrated
FrankenPHP
frankenphp
highLikely affected (source analysis)
LiteSpeed (LSAPI)
litespeed
highLikely affected, unvalidated
php-cgi
cgi-fcgi
highServes web requests; treat as affected
CVE-2026-109619 · Remote code execution via stale request state (use-after-free)
Server API (SAPI)RiskHow it is exposed
Apache mod_php
apache2handler
highestAffected: .htaccess and php_value/php_flag directory settings are applied before request startup
PHP-FPM
fpm-fcgi
highConditionally exposed when the front end sends PHP_VALUE / PHP_ADMIN_VALUE that produce a diagnostic; session upload progress validated
LiteSpeed (LSAPI)
litespeed
highConditional candidate
FrankenPHP
frankenphp
mediumNo early consumer established in the default configuration; not proven immune

What to do now

1. Update: this is the only complete fix

Move every PHP install that serves web requests to its branch's fixed release: 8.5.12, 8.4.27, 8.3.36 or 8.2.35. The PHP project is explicit that configuration changes are not a substitute. Linux distributions and LTS vendors received the patches in advance, so your vendor package may already carry the fix under its own version string.

Open the patch playbooks for exact, copy-pasteable upgrade commands for your platform (Ubuntu/Debian, RHEL family, Alpine, Docker, cPanel, Plesk, Homebrew, Windows and more).

2. If you genuinely cannot update yet, apply interim mitigations

These reduce exposure. They do not make the install safe.
The advisories state plainly that there are no complete mitigations and that updating is the only safe choice. Apply these only to buy time, and remove them once patched (except settings you have no reason to keep on).

Set these administratively so applications cannot override them (php_admin_value/php_admin_flag in the FPM pool or php_admin_value in the Apache config, not ini_set() or .user.ini), then restart every worker.

Interim php.ini mitigations
; Set administratively, then restart every worker.
; These reduce exposure; they do NOT make the install safe.
session.upload_progress.enabled=0
fatal_error_backtraces=0        ; PHP 8.5 only
memory_limit=-1
post_max_size=8M
max_input_nesting_level=16
CVE-2026-109618 notes
  • memory_limit=-1admin-enforced The primary configuration mitigation, and not a complete one. Set it so applications cannot override it (php_admin_value in the FPM pool or Apache config) and restart every worker. One request can then use all memory: watch memory and put the limit back after patching.
  • post_max_size=8Madmin-enforced Keep it conservative: 8M without uploads, 32M if uploads are needed. Keep upload_max_filesize below it.
  • max_input_nesting_level=16admin-enforced The advisory recommends reducing it below the default of 64 and bounding input variables, multipart parts, headers per part and uploads.
  • At the web server / proxy: Enforce limits at the web server or proxy for decoded request size, input field count, multipart part count, header count and nesting depth, including requests with no usable Content-Length. If the application does not use multipart/form-data, block it at the front end.
  • Do not do this: Do not use RLIMIT_AS or RLIMIT_DATA as a substitute. They make mmap() fail, which triggers the vulnerable longjmp() path. A container or cgroup memory.max limit is an availability boundary only.
CVE-2026-109619 notes
  • session.upload_progress.enabled=0admin-enforced Set it in php.ini, the FPM pool, or server configuration that applies before request parsing, then restart every worker. Do not rely on ini_set() or .user.ini. This blocks the confirmed session paths, not every variant. Keep it off after patching if you do not use the feature.
  • fatal_error_backtraces=0admin-enforcedPHP 8.5+ PHP 8.5 only. Removes the producer used by the confirmed RCE case. Restart every worker.
  • Configuration: Remove invalid or deprecated INI settings applied per request that emit diagnostics before startup: auto_detect_line_endings=1, legacy assert.* settings (8.3+), legacy session settings (8.4+), report_memleaks=0 (8.5), and invalid memory_limit or date.timezone values. Turning off error_reporting or display_errors, or using @, does not help.
  • Configuration: Fix fatal errors and execution timeouts that attackers can reach, including failures in callbacks, upload or import processing, and cleanup. Consider pm.max_requests=1 if you can afford the performance cost.
  • Audit: Turn on log_errors, a working error_log and error_reporting=E_ALL in startup config to find warnings and fatal errors. An empty log is not proof of safety.

3. Restart every worker

A running PHP worker keeps the old engine in memory. Neither the patch nor an interim php_admin_* setting takes effect until the process restarts. Reload or restart PHP-FPM, Apache (mod_php), FrankenPHP, LiteSpeed lsphp and any long-running php-cgi pools. The PHPShield CLI flags workers that started before the current PHP binary as HOST-STALE-WORKER findings.

4. Rotate secrets that the worker could have leaked

CVE-2026-109619 can disclose memory from a web worker, so treat secrets that lived in that worker's process as potentially exposed on any unpatched, internet-facing server: application and framework keys (APP_KEY), database and cache credentials, API tokens, session secrets, and any private keys or wallet material handled in-process. Rotate them after patching and restarting.

If you operate Solana or other crypto keys from a PHP host, the crypto & Solana guide covers finding exposed keys and checking whether a hot wallet has moved funds.

5. Scan for signs of compromise

Because a demonstrated exploit yields code execution as the web account, assume an unpatched, exposed server may already have been touched. Scan your document roots for webshells, PHP inside upload directories, modified WordPress core files and exposed keys with the local checkers or the CLI (phpshield scan). Everything runs on your own machine; nothing is uploaded.

Timeline

Only events stated in the official sources. Times are UTC where the source gives them.

  1. 2026-10-20

    Advance notice published.

    The PHP project announced an upcoming critical security release addressing two vulnerabilities, both rated CVSS v4.0 9.2, and reserved the right to release earlier if either flaw became public or evidence of active exploitation appeared.

  2. 2026-10-21 · 14:00 UTC

    Scheduled release time.

    The release was scheduled for Wednesday 21 October 2026 at 14:00 UTC.

  3. 2026-10-21

    PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 released.

    The security releases ship, fixing CVE-2026-109618 and CVE-2026-109619. The advisory states a working unauthenticated exploit against mod_php was demonstrated and urges all users to update immediately. PHP 8.1 and older receive no official release.

  4. 2026-10-23

    Follow-up advisory: session upload progress.

    The PHP project published an informational advisory (CVE-2026-115560) for a narrower issue in session upload progress with a switched serialize handler, with the full advisory to follow in the next bugfix release. The recommendation is to keep session.upload_progress.enabled=0, the same setting the PHP2Shell mitigations already turn off.

Frequently asked

I'm on PHP 8.1 or 7.x. Is there a fix for me?

Not from the PHP project: 8.1 and older are end of life and get no official release. The project published minimal source backports for 7.4, 8.0 and 8.1, and some distributions and LTS vendors ship them under their own package versions. Treat a vendor advisory that names CVE-2026-109618 and CVE-2026-109619 as the fix; otherwise move to a supported branch.

My distro still shows an old version number. Am I unpatched?

Not necessarily. Distributions backport security fixes into the branch they ship, so the reported patch number can stay below php.net's while containing the fix. Check the vendor advisory or the package changelog (for example apt-get changelog php8.2) for the two CVE ids. The CLI runtime check does this for you: phpshield scan reads the dpkg or rpm changelog of the package that owns each PHP binary and reports HOST-PHP-VENDOR-BACKPORT when the fix is recorded there, so the version string on its own is not proof either way.

I use the Remi repository. Can I just update from there?

Yes, once Remi has published the fixed build for your stream. Check with dnf check-update against the remi repositories first, then update the php module stream and restart every worker.

How do I verify I'm patched after updating?

Run php -v for every binary (php8.4 -v, /opt/.../bin/php -v) and confirm each reports the branch's fixed release or newer. The version a browser sees can differ from the CLI, so also confirm the web-facing runtime, and restart every worker so no process still runs the old engine.

Should I rely on RLIMIT_AS, a cgroup memory cap, or a container limit?

No. RLIMIT_AS and RLIMIT_DATA make mmap() fail, which triggers the vulnerable longjmp() path. A container or cgroup memory.max limit bounds availability only; it does not stop the bug. The only complete fix is updating.

References

PHPShield is independent and not affiliated with the PHP Group. Every factual claim on this page is drawn from the sources above and from PHPShield's curated advisory data (browse the data). Vulnerability data from php.net, updated 2026-10-22.