PHP 8.5.12 RCE: what I would change after CVE-2026-109618

PHP 8.5.12 RCE: what I would change after CVE-2026-109618

PHP 8.5.12 shipped 21 Oct 2026 for CVE-2026-109618 and CVE-2026-109619. php -v on Ubuntu 24.04 still prints 8.3.6 after USN-8948-1. I would read the package revision, then the FPM binary Nginx uses. This is a PHP-core RCE, not a Laravel Composer bump.

· 11 min read #php #security #php-8-5 #cve #php-fpm

 CVE-2026-109618 is a PHP binary, not Laravel core

Caption: php -v first. Then dpkg-query. Then the FPM binary Nginx actually uses. Illuminate does not ship this CVE. composer update laravel/framework does not patch it.

I did not post an OOM or multipart payload at anything tonight. I opened Ubuntu 24.04.5 and installed archive php8.3-cli. php -v printed PHP 8.3.6, built 19 Oct 2026. PHP_VERSION_ID is 80306. dpkg-query -W php8.3-cli printed 8.3.6-0ubuntu0.24.04.13. That revision is the floor in USN-8948-1 (21 Oct 2026). A naive PHP_VERSION_ID >= 80336 check on this box is false. I will not invent that Ubuntu shipped 8.3.36.

The failure this week is famous for that split. Either I read php -v, see 8.3.6, and tell myself I am unpatched when the package already backported the RCE, or I upgrade CLI, leave Nginx on an old FPM socket, and tell myself I am patched because Composer is green. This is a lab note. I did not write a remote exploit. I did not paste a payload.

What changed

php.net’s security advisory is dated 21 Oct 2026. It names two critical unauthenticated RCEs on every web SAPI: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP, LiteSpeed. Risk is highest on mod_php. There are no complete mitigations. Updating is the only safe choice.

php.announce #513 tagged PHP 8.5.12 at 21 Oct 2026 14:10:11 UTC. Same-day security tags:

Branch Tag php.announce Note I would not skip
8.5 8.5.12 #513, 14:10 UTC Latest 8.5 I can cite on 30 Oct. php.watch dates the tag 2026 Oct 22.
8.4 8.4.27 #512, 14:32 UTC Latest 8.4.
8.3 8.3.36 #511, 14:00 UTC Ubuntu 24.04 archive is not this number.
8.2 8.2.35 #510, 14:00 UTC Security window closes 31 Dec 2026.

Advance notice was php.announce #509 on 20 Oct 2026 19:33:47 UTC. The PHP Foundation post (21 Oct 2026) says CVE IDs leaked on the 20th with a wrong CVSS of 10.0; the real score is 9.2.

What I will treat as locked:

  • CVE-2026-109618 / GHSA-w8hw-w9fx-j79w. Published 21 Oct 2026. Package is php-src, not laravel/framework. OOM handling used longjmp() and could leave request memory half-initialized. Cleanup then treated that memory as valid. Confirmed unauthenticated RCE on mod_php with default input and memory limits. RCE also confirmed on PHP-FPM through application-level paths. CVSS 9.2 (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). CVE.org datePublished 2026-10-21T13:48:34Z.
  • CVE-2026-109619 / GHSA-6wmf-4rv5-p3rp. Published 21 Oct 2026. Request-owned global state could survive after its memory was freed. The next request on the same worker could use that stale pointer. Fatal-backtrace RCE confirmed on mod_php. Session-lifecycle RCE confirmed on mod_php and FPM. Same CVSS 9.2. CVE.org follow-up 2026-10-21T13:54:43Z.
  • Affected php.net tags: 8.2.* < 8.2.35, 8.3.* < 8.3.36, 8.4.* < 8.4.27, 8.5.* < 8.5.12. PHP 8.1 and older have no php.net tag. Ubuntu 22.04 backported into php8.1 8.1.2-1ubuntu2.28.
  • Ubuntu 24.04 archive floor is a revision, not 8.3.36. USN-8948-1 (21 Oct 2026): php8.3-* 8.3.6-0ubuntu0.24.04.13. Launchpad uploaded 20 Oct 2026, published to noble-security 21 Oct 2026. Changelog names both CVEs. php -v stays 8.3.6.
  • ondrej Noble already has the four upstream tags. Launchpad php8.5 8.5.12-1+ubuntu24.04.1+deb.sury.org+1, created 21 Oct 2026. Same-day php8.4 8.4.27, php8.3 8.3.36, php8.2 8.2.35.

What I will treat as rumor: a Laravel-core CVE; composer audit as the check; PHP 8.5.13 as a tag I can install (php.watch still lists 8.5.12 as latest on 30 Oct 2026; the PHP-8.5 NEWS file still heads an undated 8.5.13 block); CVE-2026-115560 as a shipped fix (informational advisory 23 Oct 2026); 8.6.0RC4 as production (php.watch dates RC4 21 Oct 2026, contains the same RCE fixes, RC5 planned 5 Nov, GA still 19 Nov 2026); Linux needing the 26 Oct Windows-only Brotli rebuilds 8.5.12-1 (php.announce #514).

Official stopgaps on the 21 Oct advisory are incomplete: memory_limit=-1, a low post_max_size, session.upload_progress.enabled=0 in a pool or php.ini that applies before request parsing (not ini_set()), and on PHP 8.5 fatal_error_backtraces=0. My CLI still has session.upload_progress.enabled=1. fatal_error_backtraces is not an ini key on this 8.3 binary. None of that is the patch.

What I would change in a real Laravel/PHP app this week

Three edits. None of them is “rewrite the app.” None of them is a Composer lock bump.

1. Prove the binary Nginx uses. Then prove the package revision.

php -v
php -r 'echo PHP_VERSION_ID, PHP_EOL;'
# the pool in the vhost, not the one I wish I had
php-fpm8.3 -v   # or php-fpm8.5 / php-fpm8.4
dpkg-query -W -f='${Package} ${Version}\n' php8.3-cli php8.3-fpm
dpkg --compare-versions "$(dpkg-query -W -f='${Version}' php8.3-fpm)" ge '8.3.6-0ubuntu0.24.04.13' && echo distro-fpm-patched

This lab has no FPM. command -v php-fpm8.3 is empty. The error I actually expect on a box that “already upgraded” is the CLI/FPM split, or the version-id lie:

PHP 8.3.6 (cli) (built: Oct 19 2026)
PHP_VERSION_ID=80306
# dpkg-query -W php8.3-cli
php8.3-cli 8.3.6-0ubuntu0.24.04.13

2. Stop trusting PHP_VERSION_ID alone on Ubuntu archive PHP.

function phpRceFloorMetNaive(): bool
{
    $id = PHP_VERSION_ID;

    return match (true) {
        $id >= 80500 => $id >= 80512,
        $id >= 80400 => $id >= 80427,
        $id >= 80300 => $id >= 80336,
        $id >= 80200 => $id >= 80235,
        default => false,
    };
}

// Before — this is false on Ubuntu 24.04 even after USN-8948-1.
// phpRceFloorMetNaive(); // false; PHP_VERSION is still 8.3.6
function ubuntuPhpPackageVersion(string $sapi = 'fpm'): string
{
    $pkg = 'php' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '-' . $sapi;
    $out = [];
    // Single-quote ${Version} for bash. Unquoted, the shell eats it.
    exec('dpkg-query -W -f=\'${Version}\' ' . escapeshellarg($pkg) . ' 2>/dev/null', $out);

    return $out[0] ?? '';
}

function phpRceFloorMet(): bool
{
    if (phpRceFloorMetNaive()) {
        return true;
    }

    $fpm = ubuntuPhpPackageVersion('fpm');
    $cli = ubuntuPhpPackageVersion('cli');

    // USN-8948-1 floor for noble php8.3. Do not treat 8.3.6 as unpatched.
    foreach ([$fpm, $cli] as $ver) {
        if ($ver !== '' && version_compare($ver, '8.3.6-0ubuntu0.24.04.13', '>=')) {
            return true;
        }
    }

    return false;
}

// After — refuse to boot the HTTP kernel until the SAPI that serves
// requests is on a patched tag or a patched Ubuntu revision.
if (PHP_SAPI !== 'cli' && ! phpRceFloorMet()) {
    abort(503, 'HTTP SAPI is below the 21 Oct 2026 PHP RCE floor.');
}

That 503 is a brake, not the patch. The patch is the PHP package plus a worker restart. version_compare is good enough for this noble string. On ondrej I would skip it and require php -v to print 8.5.12 (or 8.4.27 / 8.3.36 / 8.2.35). I ran the naive function on this CLI. It returned false.

3. Install the floor I can cite. Reload the pool Nginx uses. Set the stopgap in the pool, not in app/.

On Ubuntu 24.04 without a PPA:

apt-cache policy php8.3-fpm
sudo apt update
sudo apt install --only-upgrade php8.3-cli php8.3-fpm
sudo systemctl reload php8.3-fpm
php-fpm8.3 -v
dpkg-query -W php8.3-fpm

On Ubuntu 24.04 with ppa:ondrej/php (or packages.sury.org if that is what the box already uses):

sudo apt update
sudo apt install --only-upgrade php8.5-cli php8.5-fpm
sudo systemctl reload php8.5-fpm
php-fpm8.5 -v

Then in the FPM pool, not in Laravel:

; /etc/php/8.3/fpm/pool.d/www.conf  (or the 8.5 pool Nginx points at)
php_admin_value[session.upload_progress.enabled] = 0

reload is the step I skip when I am tired. OPcache keeps the old worker until that happens. ini_set() in AppServiceProvider is too late. If I already set that pool value for 109619, the 23 Oct note says keep it — and still do not claim 115560 shipped.

What would bite on a self-hosted VPS

 Version check, package revision, then PPA vs distro PHP

Caption: php -v lies on Ubuntu archive PHP. dpkg-query is the distro check. ondrej Noble already has 8.5.12. Reload the pool Nginx uses. I did not paste an OOM payload.

First bite: the socket, not php -v. I install php8.5-cli so Composer looks modern, and I leave Nginx on unix:/run/php/php8.3-fpm.sock. These CVEs die in the worker that parsed the request. That worker is FPM. This lab has no FPM. CLI-only is not a web fix.

Second bite: stock Ubuntu vs ondrej. Distro php -v stays 8.3.6. The patched package is 8.3.6-0ubuntu0.24.04.13. If I wait for PHP_VERSION_ID 80336, I wait forever on noble archive PHP. Ubuntu 26.04’s USN floor is php8.5 8.5.4-0ubuntu1.5. Ubuntu 26.10 is 8.5.9-0ubuntu2.1. Those are also not 8.5.12.

Third bite: the stopgaps look like a patch and are not. Default session.upload_progress.enabled on this CLI is 1. memory_limit=-1 on FPM is a different outage. Shared hosting that will not reload the pool is the fourth camp. config.platform.php stays on the FPM I serve.

Nine days after the tag

 Advisory timeline from 20 Oct notice to this 30 Oct lab note

Caption: Advance notice 20 Oct. Four security tags, both GHSAs, and USN-8948-1 on 21 Oct. Informational CVE-2026-115560 on 23 Oct, not shipped. Windows Brotli rebuilds 26 Oct. I am writing 30 Oct. php.watch still has 8.5.12 as latest 8.5.

php.net supported versions (retrieved 30 Oct 2026): 8.5 active until 31 Dec 2027. 8.4 active until 31 Dec 2026. 8.2 security until 31 Dec 2026. Honest target on a Laravel 13 VPS: FPM on 8.5.12 via ondrej, or noble archive php8.3-fpm 8.3.6-0ubuntu0.24.04.13, CLI and socket the same floor. I would not invent 8.5.13 today. If FPM starts dying on zend_mm_heap corrupted after an 8.5.12 bump around SimpleXMLElement plus json_encode(), that is GH-24488 (opened 21 Oct 2026; 8.4 commit 22 Oct 2026), not the RCE coming back. The 8.5.13 NEWS block I retrieved does not list that fix. It is not a shipped tag.

Are you on this in production?

If FPM already prints 8.5.12 (or 8.4.27 / 8.3.36 / 8.2.35 on that branch) you are on the php.net floor. If you are on Ubuntu 24.04 archive PHP and dpkg-query shows 8.3.6-0ubuntu0.24.04.13 for the FPM package Nginx uses, you are on the distro floor even though php -v still says 8.3.6. If CLI is patched and phpinfo() in the browser is still 8.5.11 or 8.3.6-0ubuntu0.24.04.12 — or the host will not give you that pool — you are not. Shared hosting with no PPA and no USN is the last camp. I did not flip a client cluster tonight. I want to know which of those you are in.

Did you hit the same wall?

I would get stuck on php -v still showing 8.3.6 after USN-8948-1, or on CLI 8.5.12 / FPM 8.5.11 after apt install. Did you hit the same thing, or a different one — a panel that will not reload the pool, ini_set for session.upload_progress that never applied, a host that will not add ondrej? Tell me in the comments. I read them.

If the blocker is the VPS itself (PPA vs distro PHP, the wrong socket, a host that will not give you 8.5.12), I do this class of bump for a living: Hire for deploy.

References

Share:

Get new posts in your inbox

No spam. One short email per new article — practical PHP, Laravel, devops, and AI-assisted workflows.

Comments

Powered by GitHub Discussions via Giscus. A free GitHub account is required.