Patch PHP against PHP2Shell remote code execution
CVE-2026-109618 and CVE-2026-109619 allow unauthenticated code execution in PHP. Update the runtime. Temporary configuration changes are incomplete.
Authdog Team

On October 21, 2026, PHP published fixes for two critical bugs now called PHP2Shell. CVE-2026-109618 and CVE-2026-109619 can let an unauthenticated HTTP request run code as the account that serves PHP. Each is scored CVSS v4.0 9.2. The patched releases are PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35.
Authdog's identity service is not a PHP application. This note is for the PHP you run beside it: your app, CMS, admin panel, control panel, and any appliance that embeds a PHP interpreter. If that process can read an Authdog client secret, session cookie, or webhook secret, code execution on that host is an identity incident as well as a host incident.
CISA's October 21 alert said it had not observed exploitation at publication time, and still told operators to patch immediately. Independent reporting the same day described scanning and exploitation attempts. Treat every internet-facing PHP runtime as exposed until the process that answers HTTP is a fixed build.
What is affected
PHP says every installation that serves web requests is in scope, including Apache with mod_php, PHP-FPM, php-cgi, FrankenPHP, and LiteSpeed. Risk is highest on mod_php, where request cleanup runs even when startup fails, before your script begins.
The two bugs are not the same shape:
- CVE-2026-109618 is remote code execution through out-of-memory handling. PHP uses
longjmp()when an allocation fails. In some request-startup paths the request's memory is only partly initialized. Shutdown then reads that state. PHP confirmed code execution over HTTP against mod_php with default input and memory limits. FPM exits the child when request startup fails, so that exact startup failure does not survive on FPM. A later out-of-memory condition, after startup has succeeded, can still reach invalid state on the way into shutdown. PHP has not published a complete FPM exploit for this bug. FrankenPHP follows the mod_php shutdown pattern and is treated as likely exposed. - CVE-2026-109619 is remote code execution through stale worker state. A worker keeps global fields that point at memory owned by a single request. If a fatal error, or a late diagnostic, skips the reset, the next request on that same worker can use the freed memory before normal initialization replaces it. PHP confirmed this on mod_php through fatal-error backtraces in PHP 8.5, and on both mod_php and PHP-FPM through session upload progress. Upload progress is on by default. The application does not have to call
session_start().
A larger worker pool lowers the chance that two requests land on the same process. It does not remove the bug. A successful run can leave the worker alive and still return a normal HTTP status. Wait for a patched binary, not for a crash.
php -v on a shell can be a different build from the interpreter inside Apache, FPM, or a container. Record the version that serves the site. Include images, sidecars, and vendor products where you cannot upgrade PHP yourself. Those need the vendor's package.
| Branch | Affected | First fixed release |
|---|---|---|
| 8.5 | Before 8.5.12 | 8.5.12 |
| 8.4 | Before 8.4.27 | 8.4.27 |
| 8.3 | Before 8.3.36 | 8.3.36 |
| 8.2 | Before 8.2.35 | 8.2.35 |
| 8.1 and older | Outside normal PHP support | Vendor backport, or move to a supported branch |
PHP published minimal source backports for 7.4, 8.0, and 8.1. Distributions may ship their own packages on a different version number. Match the vendor advisory, not only the upstream version string. CISA's guidance for PHP 8.1 and older, when you cannot patch, is to take those systems offline until they are updated.
Update the runtime
The PHP advisories are direct about this: a release that contains the patch is the only complete fix. Configuration changes below reduce some confirmed paths. They leave others open, including allocation failure inside the operating system and state that an extension publishes on its own.
For each deployment:
- Record the web SAPI version, the package or image, the owner, and how that runtime is updated.
- Install PHP 8.5.12, 8.4.27, 8.3.36, or 8.2.35, or the vendor build that backports both fixes.
- Rebuild container images. A running container keeps the old interpreter until you replace it.
- Restart or replace every worker. A reload that leaves old children in place is still the vulnerable binary.
- Confirm the version from the web runtime after the restart, including every pool, directory override, and sidecar.
If you cannot patch today
Take internet-facing PHP offline when you can. A reverse proxy or firewall in front of PHP can drop dangerous requests before they reach the parser. That is a bridge to the patch, not a substitute. NCSC-NL's php2shell notes track the same point.
Apply temporary settings in configuration that loads before PHP parses the request (php.ini, the FPM pool, or the server admin config). Then restart every worker. A per-directory or per-request override can undo the setting on the next hit. Check that no pool, .user.ini, or application config puts a finite memory_limit back.
For CVE-2026-109618, PHP's primary configuration mitigation is an administrative memory_limit=-1. That removes the predictable per-request cap the confirmed exploit uses. It is incomplete. If mmap() or another allocation fails, PHP can still longjmp() while request memory is incomplete. Unlimited PHP memory also means one request can push the host into real memory pressure. Keep post_max_size conservative, and enforce the decoded body size at the proxy, including requests that omit a usable Content-Length. Lower max_input_nesting_level, and cap input variables, multipart parts, headers inside each part, and uploads. If the application never accepts multipart/form-data, block that content type at the proxy.
Do not use RLIMIT_AS or RLIMIT_DATA as a stand-in. Those limits make mmap() fail at the cap, which is the vulnerable path. A cgroup memory.max can stop a container from growing without bound, with a sharp edge: the kernel may kill the master and every worker unless each worker has its own group. Killing one FPM child fails only that child's requests. The master replaces it.
For CVE-2026-109619, set session.upload_progress.enabled=0. On PHP 8.5 also set fatal_error_backtraces=0. Restart workers after both. Hiding errors does not help. display_errors, log_errors, error_reporting, and @ do not stop early code from reading stale state. The warning is consumed before PHP decides whether to print it.
pm.max_requests=1 forces a worker to exit after one request, so stale state has no later request to land on. Use it only as a short-term brake. It costs a process start on every request, and it does not fix CVE-2026-109618's cleanup path inside a single request.
There is no configuration switch that covers every extension path. An application callback can still publish request memory and then hit a fatal error. The state-reset patch is what closes that class.
What to do about identity material
Code execution uses the web worker's privileges, often a low-privilege account such as www-data. That account can still read the environment, config files, and session storage the application uses.
If the PHP app integrates with Authdog:
- Rotate the OAuth client secret for that application and redeploy it from a secret store. Assume a secret that lived on the host is copied.
- Treat session cookies and refresh material the app could read as exposed. Revoke active sessions for the affected environment.
- Rotate webhook signing secrets if that app received Authdog webhooks.
- Review audit events for token use and admin actions you do not recognize. A stolen client secret is useful from somewhere else, after the PHP host is patched.
Session checks, Authdog SDK calls, and application authentication run in your script. On the confirmed mod_php path for CVE-2026-109618, the failure happens while PHP is still starting the request. Your sign-in wall never runs.
Keep upload progress disabled after you patch if nothing in the product needs it. PHP's own note on that feature is that it is a poor place to leave an early session path enabled.
What to look for
Patch first, then hunt. A quiet log is not proof the host was never hit. Correlate three layers: the request, the PHP error log, and child processes.
- A PHP or Apache worker spawning a shell, an interpreter, or a download tool. CISA calls this out specifically. If the application legitimately runs commands, compare them with the baseline you already have.
Allowed memory size … exhaustedwith the script locationUnknown on line 0. That location means the failure happened during request startup, before a file ran. Pair it with large POST bodies. Ordinary traffic can exhaust memory too, so the line is a lead, not a verdict.- Apache children exiting with a segmentation fault while those startup errors are appearing. Wrong guesses during an attempt crash workers. A refined attempt may not.
- Early warnings, deprecations, fatal errors, and
Maximum execution time exceededon routes an outsider can reach, especially around uploads and per-request INI overrides.
During a short audit window, PHP suggests log_errors=On, a working error_log, and error_reporting=E_ALL in startup configuration. Historical logs collected with deprecations filtered out will miss part of the CVE-2026-109619 story. Fix the errors you find. Absence of a line does not clear the host.
After the new binary is in place, keep the proxy body limits. They remain useful against denial of service even when they are no longer your RCE control.
Read next
| Read | To learn how to |
|---|---|
| CVE-2026-109618 advisory | See PHP's description of the out-of-memory cleanup bug and the temporary memory_limit guidance |
| CVE-2026-109619 advisory | See the stale-state bug, upload progress, and the PHP 8.5 backtrace setting |
| CISA alert | Follow the patch-now actions for internet-facing and embedded PHP |
| NCSC-NL php2shell | Track operational status, fixed versions, and backport notes |