Back to the blog
SecurityPHPDevOps

Critical PHP flaw: unauthenticated RCE, update now

October 22, 2026·6 min read·Diego Horvatti

Yesterday, October 21, the PHP team published a notice with the most direct title possible: critical remote code execution vulnerabilities, update now. It's a critical PHP flaw that needs no login. In some setups, it fires before a single line of your application runs. If you have a PHP server answering requests on the internet, this is your problem for today.

I know. You write TypeScript, run Bun, deploy on Vercel. But do the quick math. The client's WordPress blog. The old Laravel admin panel that "nobody touches." That phpMyAdmin forgotten on a subdomain. PHP still runs a huge slice of the web, and odds are at least one of those is yours. Or belongs to someone who's going to call you.

What happened with PHP

There are two vulnerabilities, both rated 9.2 on CVSS v4.0 according to the pre-announcement on the 20th. The fixes shipped in versions 8.5.12, 8.4.27, 8.3.36 and 8.2.35. The official source is the php.net news archive.

The first one, CVE-2026-109618, involves running out of memory. When PHP hits its memory limit in the middle of a request, it can stop with internal data half built. Later, during request cleanup, that broken data is treated as valid. Memory corruption that an attacker can trigger is the classic path to code execution.

The second one, CVE-2026-109619, is about state that lives longer than it should. A piece of internal state from one request keeps pointing to memory that was already freed. The next request handled by the same worker can use that garbage before normal initialization cleans things up. A classic use-after-free, with the nasty twist of crossing request boundaries.

Who is actually at risk

Short answer: almost everyone serving the web with PHP. The notice mentions PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed.

The worst case is mod_php. The PHP team says there's a demonstrated exploit where an unauthenticated request triggers the attack before any application code runs. In other words, it doesn't matter if your Laravel app validates everything perfectly. The problem happens before Laravel wakes up.

Having the most secure code in the world doesn't help if the runtime falls over before it.

One point a lot of people will miss: PHP 8.1 and older no longer get official updates. If your server still runs 7.4, you're in the group that most needs to act and has the fewest ready-made options.

How to find out which version you run

Before anything else, find out what's in production. Don't trust what's in composer.json. Trust the binary:

php -v
# or, for the version the web server actually uses:
php-fpm -v

CLI and FPM can be on different versions on the same machine. I've seen it more often than I'd like. If you can create a temporary route, a phpinfo() behind authentication shows the real version serving requests. Then delete the route.

If you use Docker, look at the image tag:

# bad: can stay stuck on a vulnerable version for months
FROM php:8.3.10-fpm

# better: follows patches for the 8.3 line
FROM php:8.3-fpm

Pinning to a patch version gives you reproducibility, but it costs you dearly on a day like today. If you keep it pinned, make sure there's a process to bump it.

How to update without drama

There's only one right path: move to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, depending on your line.

  • Linux distributions got the patches early, so run your distro's package update and restart FPM or Apache.
  • Docker: rebuild the image with --pull and redeploy. Without --pull, you rebuild on top of the old cache and think you updated.
  • Windows: new binaries on the downloads page.
  • Shared hosting: open a ticket today and ask for the exact version. "We're looking into it" is not an answer.
# Debian/Ubuntu with distro packages
sudo apt update && sudo apt upgrade php8.3-fpm
sudo systemctl restart php8.3-fpm
php-fpm8.3 -v

There's a detail that catches a lot of people. If you use the rpms.remirepo.net repository, maintained by volunteers, the notice itself says it won't be updated today. The recommendation is to switch to distro packages or build from source. This isn't a knock on the maintainer, who does a huge amount of work for free. It's just the reality of depending on one person on a day when everything's on fire.

For anyone stuck on 7.4, 8.0 or 8.1, the security team published minimal backports in GitHub branches. They're aimed at people who build from source or don't have an LTS vendor. That's a rare move for unsupported versions, and it shows how big this is.

What if I can't update today?

It happens. Maintenance windows, a client who needs to approve, a server nobody knows the password to (that last one is a joke, but only sort of). The notice lists mitigations, with an honest caveat: they reduce specific attack paths, they don't make the installation safe.

For the out-of-memory flaw:

; in the FPM pool, so the application can't override it
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 4M

The idea is to remove the attacker-controlled memory exhaustion and shrink how much they can send. If the application needs uploads, the notice suggests a post_max_size of 32M, with upload_max_filesize below that. With no memory limit in PHP, the container or cgroup is what protects the machine, ideally killing only the affected worker. And don't swap this for RLIMIT_AS, which can trigger the very flaw you're trying to avoid.

It's also worth tightening limits at the proxy, like Nginx or your load balancer: body size, number of fields, multipart parts, header count. That includes requests without a trustworthy Content-Length.

For the flaw where state survives between requests:

session.upload_progress.enabled = 0
; PHP 8.5 only
fatal_error_backtraces = 0

This has to go in php.ini, the FPM pool, or the server config. ini_set() and .user.ini won't work, because they kick in too late. And restart every worker, or the new config won't apply to processes that are already running.

One detail I found interesting: hiding errors with @ or display_errors=0 doesn't help. The vulnerable path is the diagnostic being emitted, not being displayed. It's a good reminder that hiding a warning was never the same as fixing it.

What this changes in your workflow

The practical lesson isn't about PHP. It's about the time between "the patch is out" and "it's in production." In most teams I know, that time is measured in weeks, because nobody owns runtime updates. Application code gets PRs, reviews and CI. The base image just sits there until someone remembers.

A few things that apply to any stack, including your Node or Bun one:

  • Be able to list every runtime you have in production, with versions, in five minutes.
  • Use image tags that follow patches, or automate the bump with Renovate or Dependabot.
  • Have a deploy you can run on a Tuesday afternoon without fear.

My opinion, with no middle ground here: if you still run mod_php in 2026, this is the excuse you were missing to move to PHP-FPM or FrankenPHP behind a proxy. Not because FPM is immune. It isn't. But separating PHP from the web server gives you more places to put limits when the next notice like this shows up. And it will show up.

Update today, apply the mitigations where you can't, and write down how long it took. That number says more about the health of your infra than any dashboard. If you want to see how I handle deploys and infrastructure in my own projects, take a look at what I've been building.

LinkedIn summary

Yesterday PHP put out a warning with no sugarcoating: critical flaw, remote code execution without login, update now.

With mod_php, the attack fires before a single line of your application runs. Your Laravel app can validate everything. The problem happens before it even wakes up.

"But I use TypeScript and Bun." Fine. What about the client's WordPress, the old admin panel nobody touches, the phpMyAdmin forgotten on a subdomain?

The patched versions are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Check the binary, not composer.json. And if your server is still on 8.1 or older, there's no official update for it.

To me, the bigger lesson isn't even about PHP. It's about how long your team takes between a patch landing and that patch reaching production.

In the full article I explain how to check your version, update without drama, and what you can mitigate if you can't upgrade today. How long does it take you to apply a patch like this?

#PHP #CyberSecurity #DevOps #WebDevelopment #Infrastructure