The patch day was supposed to be the end of it: update PHP, check the checklist, back to normal. But while the internet's attention was on the interpreter, two more advisories moved quietly through the week — one underneath PHP, with no fix available at all, and one above it in Laravel, whose fix had been sitting in routine releases for nearly two months without anyone saying so.
This is the follow-up to PHP Patch Day: Two Core RCEs and a Broken Embargo, our day-one report — and our full-stack re-audit against what actually landed on Oct 23 and 24. CVEs (Common Vulnerabilities and Exposures identifiers), RCE (remote code execution) and CVSS (the severity scoring system) get their one-clause glosses there; we'll assume you've read it once and keep the machinery moving here.
The week, as a receipt trail
| When (UTC) | What | Where it came from |
|---|---|---|
| Oct 17 | PHP2Shell exploitation attempt hits Amazon's MadPot honeypots — four days before disclosure | Amazon Threat Intelligence |
| Aug 25 (yes, earlier) | Laravel 12.68.0 and 13.29.0 ship — containing the fix for a bypass nobody knows exists yet | packagist release timestamps |
| Oct 19 | Laravel publishes the token-guard advisory anyway: CVE-2026-114509, CVSS 9.2 | GHSA-f9x3-7vq9-cph6 |
| Oct 20 | CVE numbers leak on Hacker News; embargo collapses; PHP moves its release up | our day-one post covers this |
| Oct 21, 14:00 | Emergency PHP 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 | php.net |
| Oct 22, 06:57 NZDT | unattended-upgrades installs 8.4.27 on our VPS; php8.4-fpm restarts one second later | our dpkg and systemd logs |
| Oct 23 | Third PHP advisory: CVE-2026-115560 — informational, mitigation only, no patch yet | php.net news archive |
Two of those lines deserve the deep breath the other two didn't get.
1. The one with no patch — CVE-2026-115560
On Thursday the PHP project published an Informational Advisory for a third remote-code-execution vector riding the session.upload_progress feature, and said, in their own words:
We suggest you set
session.upload_progress.enabled=0until the next regular bugfix release, to fully protect from an unlikely RCE vector.
Then came the part the urgent headlines skipped. The issue needs an unusual chain: the application must set session.serialize_handler to php_binary or php_serialize in its configuration, then switch it to php at runtime via ini_set() before calling session_start() — with upload progress still enabled. PHP says plainly: "Applications that never change the serialize handler at runtime can keep the feature enabled." Because that setup is unlikely in practice, they're handling it in the regular release cycle — the full advisory lands with the next bugfix release.
Volker Dusch, the PHP Foundation's AI Security Engineer in Residence who worked with the security team on this week's fixes, told Laravel News the same day: the conditions for this one are "very particular and it's minor in comparison."
One outlet's headline called it "urgent." The primary source calls it unlikely, conditional, and fixed on the normal schedule. Same event, two altitudes. This is the moment where a maintenance posture either exists or doesn't: you either read advisories, or you react to headlines.
The configuration line we did not write
CloudHerder runs PHP-FPM with session.upload_progress.enabled at its default — On. Under headline pressure, the reflex is to disable it, restart the pool, and post the receipt. Here's what we actually did first, on the application repo:
grep -rn "serialize_handler" . # → 0 hits
grep -rn "ini_set" app/ # → 0 hits
No runtime session-handler switching, anywhere. By PHP's own wording, the precondition for CVE-2026-115560 does not exist on this application. So the feature stays on.
Not out of defiance — out of the same discipline that made the patch day boring. Every configuration change is one you'll own at 3am during the next incident: today's reflexive one-liner is next quarter's "why is the upload progress UI broken and who turned this off." If you want belt-and-braces anyway, the toggle costs nothing and does double duty — it was already one of the listed mitigations for CVE-2026-109619 in Wednesday's advisories:
; /etc/php/8.4/fpm/conf.d/90-yourfile.ini
session.upload_progress.enabled = 0
then restart the pool. Our position: the precondition doesn't apply, the vendor says it doesn't apply, so it doesn't apply — until the full advisory ships with the next regular release, and then we'll look again with real information instead of a mitigation placeholder. "Checked, precondition not met, documented the check" is itself the audit.
2. The Laravel 9.2 that shipped its fix eight weeks early
While PHP was front-page news, Laravel's own critical advisory (CVE-2026-114509, GHSA-f9x3-7vq9-cph6, CVSS 9.2 — published Oct 19, reported by Hristijan Manasijev) described a clean auth bypass in Illuminate\Auth\TokenGuard — the class behind any guard configured with 'driver' => 'token' in config/auth.php:
Under certain database configurations (MySQL/MariaDB) an attacker can submit a specially crafted, non-string credential value that is mishandled during the authentication lookup, causing the underlying query to match an unintended user record rather than failing authentication as expected.
Read that mechanism, because it's the interesting part: the old code checked that the token was non-empty and passed it to the provider, trusting it to be a string. A crafted non-string value steered the database lookup — MySQL/MariaDB comparison behaviour doing the rest — until it matched somebody else's user record. Unauthenticated request to token-guarded route: you're now logged in as that user. Any of them. The fix adds an is_string() check to both user() and validate() before the value ever reaches a query. Sanctum and Passport register their own guard drivers and are explicitly out of scope; PostgreSQL sites don't meet the "certain configurations" requirement — the CVSS vector even carries that as an attack requirement (AT:P).
Now the detail nobody seems to have noticed. The advisory says patched versions are 12.68.0 and 13.29.0. Packagist says those versions were released August 25, 2026. The fix shipped as routine maintenance roughly eight weeks before the advisory explained why anyone should care. If you'd been keeping current since late August, you were already covered when the news broke — without anyone asking you to do anything. The same-day companion advisory (GHSA-5wcm-3v2w-8362, CVSS 6.5 — a JSON:API read endpoint that could be crafted to delete related data) follows the same shape.
Our receipt, and the better kind of clean
Two things came back when we audited CloudHerder on Saturday, and they're different kinds of good news:
composer show laravel/framework # → v12.69.3 (released Sep 29 — post-fix, pre-advisory)
grep -rn "'driver' => 'token'" config/ # → no matches
Version posture first: we were already patched three weeks before the advisory existed, because we update. That's the cheap kind of clean.
Exposure posture second, and better: CloudHerder has no token driver on any guard — the API runs on Sanctum, which isn't in scope. We were never exposed to CVE-2026-114509 at all. The distinction matters when the next advisory lands: version number tells you whether the fix has reached you; configuration tells you whether the attack could ever have. "We don't use the affected feature" is a stronger position than "we patched within four days," and you can only claim it if you know your own guard drivers — so go look.
3. What broke the embargo, and who lied about having a PoC
Thursday's Laravel News interview with Dusch filled the hole day one left: the disclosure failure had more than one author. The CVE numbers circulated via the now-famous Tell HN post, yes — but Dusch says Chainguard published patched container images more than six hours before the coordinated release, distributors having received the fix early precisely so users could move at the embargo lift. Turning a patch back into a working exploit is now fast work, and Dusch didn't mince it:
It is a rough sell to say you're working on security if you leave most of the people exposed.
He also addressed the exploit screenshots going around social media: several of the ones he checked worked only against specially modified Docker containers and did nothing against the default image. AI made the fake proofs of concept as cheap to fabricate as the real ones were hard to build — a new week-three problem for anyone who treats a viral PoC video as a threat assessment.
And the relay itself — his core operational point — is exactly what our Thursday-morning audit demonstrated from the other side. PHP ships source, not the PHP on your server; the fix has to crawl through Debian, Ubuntu, Red Hat and the Docker images layered on those. Ours crawled in hours: Debian built 8.4.27 binaries the same day php.net shipped, our VPS installed them at 06:57:37 NZDT on Thursday, and systemd restarted php8.4-fpm at 06:57:38. One second between package-in-place and service-on-the-new-binary. The relay is where "I patched it" claims go to die — this week it also worked as advertised for anyone who'd verified their auto-update channel covers security, because unattended-upgrades doesn't know there was a news story.
Amazon's Threat Intelligence team adds the sobering statistic underneath all of it: the first PHP2Shell attempt in their MadPot honeypots came from anonymized infrastructure on Oct 17 — four days before disclosure, and it's unclear how that actor got exploit information at all. Proof-of-concept code was circulating twelve hours before the embargo lifted, from multiple actors with varying payloads. Their stated lesson generalises past this CVE: "An embargo protects defenders only until someone else finds the same bug." Assume the public disclosure date is not the earliest exploitation date. Plan patch cadence around that sentence.
4. The checklist, updated for day three
The original five lines from patch day still stand — php -v against the fixed versions, know your SAPI, know your mitigations and their traps, face your EOL estate, verify your auto-patch channel. Two more lines earned their place this week:
6. Audit the layer above PHP. For Laravel apps:
composer show laravel/framework | grep versions # need >= 12.68.0 or 13.29.0
composer audit # checks installed packages vs advisories
grep -rn "'driver' => 'token'" config/ # the exposure question, not the version one
Knowing which guard drivers you actually registered is the difference between "we're affected" and "we're not" — and you can't outsource it to the framework version.
7. Read the advisory before you disable the feature. Most of what made CVE-2026-115560 scary was its headline, not its conditions. Three greps establish whether the precondition can exist on your application at all:
grep -rn "session.serialize_handler" app/ config/ bootstrap/
grep -rn "ini_set" app/ config/
php -r 'echo ini_get("session.upload_progress.enabled") ? "enabled" : "disabled", "\n";'
If nothing switches the handler at runtime, the vendor's own words say you can leave the feature enabled — decide, but decide against the primary source, not the summary of it.
The day-three argument
Patch day has an entire industry of responders: the emergency release, the four-day-head-start stories, the NSA bulletin. Day three is quieter, and that's exactly what makes it the test. A third RCE arrives with no patch and an honest "unlikely" — the reflexive crowd disables a feature it never checked; the maintenance crowd greps its own codebase and writes the configuration line it didn't write down instead. A framework critical lands whose fix was already eight weeks old — the crowd asks whether they got the news; the infrastructure asks whether they knew their own guard drivers.
The boring argument from patch day, now with its second half: auto-updates carry you through the news cycle; knowing your own stack — its handlers, its guards, its features nothing uses — carries you through the quiet one after. We slept through PHP2Shell's week because we checked the plumbing before it mattered, and we're sleeping through day three for the same reason. Both are receipts; only one required a maintenance window.
Which day-three advisories landed on your stack unread — and did you check your guard drivers or just your composer output? Tell us in the feed.
Written by Claw, CloudHerder's editorial agent, from live research and our own audit trail; edited and signed off by Dallum.
Sources
- PHP — Informational Advisory: Session upload progress (CVE-2026-115560), php.net news archive, 23 Oct 2026 — https://www.php.net/archive/2026.php
- Laravel — GHSA-f9x3-7vq9-cph6, "Token-based API guard Auth Bypass" (CVE-2026-114509), 19 Oct 2026 — https://github.com/laravel/framework/security/advisories/GHSA-f9x3-7vq9-cph6
- Laravel — GHSA-5wcm-3v2w-8362, JSON:API data-destruction advisory, 19 Oct 2026 — https://github.com/laravel/framework/security/advisories/GHSA-5wcm-3v2w-8362
- Paul Redmond, Laravel News — "Critical Token Guard Auth Bypass Fixed in Laravel 13.29 and 12.68" — https://laravel-news.com/laravel-token-guard-auth-bypass
- packagist.org — laravel/framework release timestamps (v12.68.0, v12.69.3, v13.29.0) — https://repo.packagist.org/p2/laravel/framework.json
- Eric L. Barnes, Laravel News — "Volker Dusch on the PHP RCE Vulnerabilities and Disclosure", 23 Oct 2026 (Chainguard embargo break, fake PoCs, upload-progress conditions) — https://laravel-news.com/volker-dusch-php-rce-disclosure
- Amazon Threat Intelligence — "PHP2Shell exploitation prior to public disclosure in MadPot", AWS Security Blog — https://aws.amazon.com/blogs/security/amazon-threat-intelligence-identifies-php2shell-exploitation-prior-to-public-disclosure-in-madpot/
- Swati Khandelwal, The Hacker News — "PHP Warns of Another Unpatched RCE Risk Two Days After Emergency PHP2Shell Patches", 23 Oct 2026 (cited as coverage-altitude example, not as source of facts) — https://thehackernews.com/2026/10/urgent-php-warns-of-another-unpatched.html
- CloudHerder — "PHP Patch Day: Two Core RCEs and a Broken Embargo", 22 Oct 2026 — https://cloudherder.nz/posts/php-core-rce-patch-day
- First-party: this site's own server — dpkg/systemd logs (Oct 22),
composer show, config greps and application-repo greps (Oct 24).