PHP · vulnerability · CVE · remote code execution · patching · incident response
Critical PHP vulnerabilities – patch now (CVE-2026-109618 and CVE-2026-109619)
Two critical PHP vulnerabilities with CVSS 9.2 allow unauthenticated remote code execution. Fixed in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 — here is how to check your exposure and remediate today.
Published 6 min read
On 20 October the PHP project announced a security release for two critical vulnerabilities. On 21 October at 13:30 UTC the fixes were released. Both vulnerabilities allow unauthenticated remote code execution with a CVSS score of 9.2, and public proof-of-concept code is already circulating.
This is not a vulnerability you schedule for the next maintenance window. It is one you remediate today.
The short version
- Update to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35.
- All PHP that handles web requests is affected — PHP-FPM, mod_php, php-cgi, FrankenPHP and LiteSpeed.
- A single unauthenticated request can be enough, and it can land before your application code even runs.
- PoC code is public. Expect broad scanning within hours, not weeks.
- If you cannot patch immediately: isolate the internet-facing services until the fix is in place and verified.
What happened
On 20 October the PHP project announced that a security release was coming. The following day, 21 October at 13:30 UTC, the fixes were published in four versions at once: 8.5.12, 8.4.27, 8.3.36 and 8.2.35. CERT-EU issued a security advisory the same day and urged organisations to patch immediately.
That the fix arrived quickly is good. That the technical description became public at the same time means the distance from "patch available" to "attackers trying" is measured in hours.
What the vulnerabilities actually do
CVE-2026-109618 – memory corruption at the memory boundary
PHP stops a request that exceeds memory_limit by aborting it with longjmp(). That means the abort can happen in the middle of an internal operation, while the request's data is only partially initialised — for example during the parsing of a file upload in a form.
The cleanup of the request can then treat invalid data as valid and free memory that was never initialised. That memory may still contain data from earlier allocations controlled by the attacker. The result is memory corruption that can lead to code execution.
CVE-2026-109619 – residual global state
Internal state belonging to one request can survive after its memory has been freed — for example after a fatal error. A later request handled by the same worker process can use that residual state before normal initialisation has had time to reset it.
Neither vulnerability therefore depends on your application having a bug. They sit in PHP's own request handling.
A demonstrated exploit shows that a single unauthenticated request can trigger the attack before any application code runs.
Who is affected
Every PHP installation serving web traffic. The PHP team considers mod_php to be the most exposed configuration.
- PHP 8.5 before 8.5.12
- PHP 8.4 before 8.4.27
- PHP 8.3 before 8.3.36
- PHP 8.2 before 8.2.35
- End-of-life branches (PHP 8.1 and older), which no longer receive regular updates
In addition: many commercial and open source products use PHP underneath. They can be affected even if you do not run a PHP installation yourself. NCSC-NL has published an inventory of third-party software that embeds PHP.
The most common mistake we see: counting only the servers you set up yourself. Do not forget containers, CI images, appliances, internal tools and vendor-operated services.
Check your exposure – five minutes per environment
- Run
php -von every host and in every container. Not just one. - Inventory the runtimes: PHP-FPM pools, Apache with mod_php, php-cgi, FrankenPHP, LiteSpeed.
- Rebuild the container images — the base image is what counts, not the running instance.
- Ask vendors who operate PHP-dependent products for you: which version are you running, and when will you be patched?
- Find end-of-life branches. PHP 8.1 and older receive no regular fixes.
Remediate
Distribution packages. Linux distributions and LTS vendors had the fixes in advance. Update through the package manager and restart the worker processes.
Containers. Update the base image and rebuild. Restarting an old image solves nothing.
Self-compiled. Upgrade to 8.5.12, 8.4.27, 8.3.36 or 8.2.35.
End-of-life versions. The PHP team has published minimal backports for 7.4, 8.0 and 8.1 for those who build from source or lack an LTS vendor. If you have an LTS vendor: contact them first.
rpms.remirepo.net: this voluntarily maintained repository receives no updates on release day. The PHP team urges users to switch to distribution packages or build from source.
Restart PHP-FPM and the other worker processes after updating. A new binary without a restart is still the old process.
If you cannot patch today
The measures below reduce exposure to specific attack paths. They do not make the installation secure. Only an update closes the vulnerability.
Against CVE-2026-109618:
- Set
memory_limit=-1in a way the application cannot override, for examplephp_admin_valuein the FPM pool or in the Apache configuration. Check that no pool, directory or application setting puts a limit back. - Set a low
post_max_size: 8M for applications without file uploads, 32M where uploads are required. Keepupload_max_filesizebelowpost_max_size. - Set limits in the web server or proxy for decoded request size, number of fields, number of multipart parts, number of headers and nesting depth. The limits must also cover requests without a usable
Content-Length. - Use a memory limit in the container or cgroup as an availability limit, preferably with a policy that kills only the affected worker. Do not use
RLIMIT_ASorRLIMIT_DATA— they can trigger the vulnerable allocation path.
Against CVE-2026-109619:
- Set
session.upload_progress.enabled=0and restart every worker process. Set it inphp.ini, in the FPM pool or in the server configuration so that it applies before request parsing. Do not rely onini_set()or FPM's.user.ini. - On PHP 8.5: set
fatal_error_backtraces=0and restart every worker process. - Remove invalid or outdated per-request settings that emit diagnostics before initialisation. Hiding the diagnostics with
error_reporting,display_errorsor@does not help. - Fix fatal errors and execution time limits that an attacker can reach — in callbacks, uploads, imports and cleanup.
Why you should not wait
Three things separate this event from an ordinary patch:
- No authentication is required. There is no login to hide behind.
- The attack happens before your code. Your application logic, WAF rules and permission model are not a barrier here.
- The PoC is already out. What usually takes weeks has already been done by someone else.
The cost of a patch and a restart today is negligible compared with the cost of a breach. That applies in particular if you are covered by NIS2, where a significant incident must be reported within 24 hours.
After patching – three things
- Verify on every node. The version — not just that the package manager said OK.
- Read the logs backwards. Multipart POSTs, unexpected 500 responses, crashed workers and traffic from unknown sources over the last days.
- At the slightest suspicion: run the incident routine. Isolate, preserve logs, assess whether notification is required.
Sources
- CERT-EU, Security Advisory 2026-018: https://cert.europa.eu/publications/security-advisories/2026-018/
- PHP 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35: https://www.php.net/
- GHSA-w8hw-w9fx-j79w: https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w
- GHSA-6wmf-4rv5-p3rp: https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp
- NCSC-NL, inventory of products with embedded PHP: https://github.com/NCSC-NL/php2shell
Vulnerabilities like this are the argument for continuous security: patching, verification and monitoring that run all the time — not an annual review. Read more about what continuous security means in practice.
Want to know where you stand?
A free 30-minute call gives you an honest picture of your exposure — no sales pitch.
