CSIPE

Published

- 21 min read

The PHP Patch Needs a Runtime and Lookback Receipt


Books by the author

Compare all 5

As an Amazon Associate I earn from qualifying purchases. Buying through these links costs you nothing extra and helps pay for the blog.

A developer updates the PHP package on a server, sees a successful command, and closes the emergency ticket. The website still answers. The package manager says the fixed version is installed. Yet an old worker may still be serving requests, a container may still hold the previous runtime, and the investigation may begin four days too late.

That is the practical problem behind the two critical PHP flaws disclosed on 21 October 2026. PHP released versions 8.5.12, 8.4.27, 8.3.36, and 8.2.35 to fix CVE-2026-109618 and CVE-2026-109619. Both can let a remote attacker without an account run code on a web server. The PHP team says every installation serving web requests is at risk, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP, and LiteSpeed (PHP security advisory, 21 October 2026).

The date on the advisory is only one date in the incident. Amazon Threat Intelligence says its MadPot sensor network recorded an attempted exploitation on 17 October, four days before public disclosure. It saw more attempts before the scheduled release on 21 October, then saw proof-of-concept code used against sensors almost immediately after the embargo lifted. Amazon’s account says the flaws are now being broadly exploited, while also making an important distinction: it cannot say that every attempt it observed would have succeeded against a real server (AWS Security Blog, 21 October 2026).

So the job has two halves. First, move every exposed PHP runtime to a fixed build and prove the running process changed. Second, review the period beginning no later than 17 October for signs that someone reached the old runtime. A patch receipt answers what is running now. A lookback receipt answers what your evidence can say about the days before the patch.

What happened on 21 October

PHP sits below the application code in a large part of the web. WordPress, Drupal, Magento, Laravel applications, private administration portals, and custom APIs all depend on the runtime to turn an incoming request into work. A flaw in that shared layer can cross application brands and frameworks because the vulnerable code runs underneath them.

On 20 October, the PHP project gave advance notice of an emergency security release. The team planned to publish at 14:00 UTC on 21 October after details and CVE identifiers appeared before the original schedule. CERT-EU records the fixed release at 13:30 UTC and advised organisations to patch immediately. Where that could not happen, it recommended isolating internet-facing applications until the fix was installed and verified (CERT-EU advisory 2026-018, 21 October 2026).

The repaired branches are precise. PHP 8.5 needs 8.5.12 or later. PHP 8.4 needs 8.4.27 or later. PHP 8.3 needs 8.3.36 or later. PHP 8.2 needs 8.2.35 or later. PHP 8.1 and older no longer receive normal updates from the PHP project, although the security team published minimal source backports for 7.4, 8.0, and 8.1 because of the severity. Distribution and long-term-support vendors may also ship a backported repair under their own package version.

That last detail prevents a common inventory mistake. A distribution package can contain the security fix without displaying the upstream PHP version in the exact form above. Conversely, a machine may have a new command-line PHP binary while the web service uses another package, image, or socket. Compare the deployed runtime with the advisory from the vendor that actually supplied it. Do not judge a backport by its version string alone, and do not judge the web tier by an unrelated command-line binary.

The two flaws received a CVSS 4.0 score of 9.2 from the PHP security team. A score helps sort a large queue, but the mechanics explain the urgency better. The attack can arrive through an ordinary web request. It does not require a customer account, an administrator password, or a mistake in the application’s login code. In the highest-risk configuration identified by PHP, Apache with mod_php, a demonstrated path can trigger before the application itself starts running.

That means an application firewall inside the framework, a login check in a controller, or a route-level permission cannot be the primary defence. Those controls execute too late when the runtime fails while preparing the request. The fix has to reach the PHP layer that receives the traffic.

The scale is broad without needing inflated claims. KrebsOnSecurity reported on 21 October that Censys had identified about 2.58 million internet-facing hosts and 54.9 million web properties running PHP. Those figures describe visible use, not a count of confirmed vulnerable or compromised servers. They do explain why hosting companies, software vendors, and small engineering teams all received the same urgent patch problem at once (KrebsOnSecurity, 21 October 2026).

The failure happens below your framework

CVE-2026-109618 concerns what happens when PHP runs out of memory while preparing request data. In plain English, the runtime can be interrupted while an internal structure is only half built. Cleanup then handles that half-built structure as though it were complete. Carefully shaped request data can influence the memory involved, turning a crash path into a route for executing code.

The advisory calls this out-of-memory corruption. A normal memory_limit sounds like a safety control because it stops one request from consuming the whole machine. Here, the act of enforcing that limit can interrupt initialization at the dangerous moment. The limit did not create the underlying memory bug, but an attacker can use the failure path to reach it.

CVE-2026-109619 concerns state that survives longer than the memory behind it. PHP workers handle many requests over their lifetime. After a fatal error, an internal pointer from one request can remain even though the referenced memory has been released. A later request handled by that same worker can use the stale state before normal initialization replaces it. The result can include code execution and, according to the PHP advisory, disclosure of memory.

A restaurant analogy helps. One flaw resembles stopping a kitchen halfway through setting a station, then asking the cleanup crew to put away tools that were never properly placed. The other resembles leaving yesterday’s ticket number on a hook after the ticket has been destroyed, then using that number for today’s order. Both problems live in the kitchen’s operating process, not in the recipe chosen by the customer.

This is why changing WordPress settings or adding an authentication rule cannot settle the issue. The application may never receive control before the runtime enters the vulnerable path. Datadog Security Labs independently reproduced both flaws and reported that application-level authentication could not block the demonstrated attacks. The control point has to sit before vulnerable PHP or replace it with a fixed runtime (Datadog Security Labs, 21 October 2026).

The worker model also explains why a restart matters. Updating files on disk changes what a new process will load. It does not necessarily replace every process already running in memory. PHP-FPM pools, Apache workers, long-lived containers, and managed service units need the activation step appropriate to their deployment. A repaired package beside an old worker is inventory progress, not a completed fix.

The same distinction applies to images. Editing a package in one running container may remove immediate exposure from that instance. If the image tag, Dockerfile, lockfile, or base-image digest remains unchanged, the next rollout can restore the vulnerable runtime. Repair the build input, create a new artifact, deploy it, and verify the running digest. Otherwise autoscaling becomes a rollback mechanism.

Embedded PHP deserves its own line in the inventory. An appliance, desktop product, vendor panel, or bundled web service may carry PHP without advertising itself as a PHP application. CERT-EU points operators to the NCSC-NL inventory of affected software for this reason. As of 22 October, that public list was still changing, so use it as a discovery aid and confirm status with each product vendor rather than treating one snapshot as permanent truth (NCSC-NL php2shell product inventory).

The advisory date is too late for the search window

An incident timeline that begins when the patch email arrived misses the earliest public evidence. Amazon says it found an attempted exploit from 17 October in its sensor data. The source address belonged to a commercial anonymization network, and Amazon could not determine how the actor obtained information before disclosure. That uncertainty should remain visible. The observation supports an earlier investigation date; it does not identify the person behind the request or prove successful compromise elsewhere.

Amazon then observed several attempts between 02:00 and 08:00 UTC on 21 October, before the scheduled 14:00 UTC embargo lift. The payloads and anonymization choices varied, which the company says suggested multiple actors. After the patch and details became public, more proof-of-concept code appeared within hours and hit its sensors almost immediately.

A sensible local window therefore begins by 17 October 2026. It may need to begin earlier if your own evidence shows unusual multipart uploads, fatal errors, process launches, or file changes before that date. The first event seen by one sensor network is a floor for investigation, not proof that no one acted earlier anywhere else.

This changes the questions for a server patched on 21 October. “Did we see attacks after the advisory?” is too narrow. Ask whether the server accepted untrusted web traffic from at least 17 October until the fixed workers began serving. Then ask what records cover that interval and what those records can actually reveal.

Web access logs may show request methods, paths, sizes, response codes, user agents, source addresses, and timing. They may not retain multipart form fields or decoded request bodies. PHP and framework logs may show fatal errors but omit the request that caused them. Process telemetry may record a web worker starting a shell, downloader, or unexpected interpreter. File monitoring may show a new PHP file, modified startup configuration, or changed scheduled task. Network records may show the web server calling an unfamiliar external address.

None of those sources is complete by itself. An access log with no obvious exploit string cannot prove the memory flaw was never reached. A web application firewall block can prove that one request was rejected, while saying nothing about traffic routed around the service or attack variants that did not match its rule. A clean file scan can miss execution that lived only in memory or used existing administration tools.

The correct output is a bounded statement. For example: “We reviewed retained proxy, web, process, file-change, and outbound-connection records from 17 October 00:00 UTC through the verified deployment time. We found no unexplained child processes or file changes. Request bodies were not retained, so the review cannot exclude every attempted exploit.” That sentence is useful because it names both the evidence and its edge.

Treat source addresses as leads rather than verdicts. Amazon published addresses seen in its sensors but warned that some belong to shared anonymization services and are not exclusive to this activity. Searching for them may find related traffic. Blocking them alone will not cover other actors, and seeing one address does not establish that a request succeeded.

Find the PHP that actually receives the request

A production estate rarely has one clean answer to “Which PHP version do we run?” The public website may use PHP-FPM in Kubernetes. An old administration panel may use mod_php on a virtual machine. A scheduled job may call a command-line binary from the host. A purchased appliance may carry another copy behind its own interface. The emergency begins with finding each request path, not with running one version command.

Start at the outside. List public hostnames, load balancers, content-delivery routes, reverse proxies, direct origin addresses, control panels, upload endpoints, and partner-facing services. Map each route to the workload that finally parses the web request. Include standby regions and disaster-recovery systems. A powered-off image can wait, but it still needs repair before someone activates it during the next outage.

Then identify the runtime source. It may come from a Linux distribution package, an official container, a hosting control panel, a language image maintained by another team, or a vendor appliance. Record the branch, package release, image digest, and supplier advisory. If a distribution backported the fix, keep the vendor notice beside the observed package version so another engineer can reproduce the decision.

Do not trust the shell’s default php -v without tracing the service. The command may resolve to /usr/bin/php, while PHP-FPM uses a different package or container. A container host may have no vulnerable PHP at all even though several pods do. A reverse proxy may send /admin to an old pool while normal pages use the new one. Query the service manager, pool configuration, container metadata, or a controlled runtime diagnostic through the path under test.

A temporary diagnostic page can expose version information, but publishing one to the internet creates another information leak and may remain forgotten. Prefer deployment metadata, package evidence inside the workload, and a restricted health or administration route. If a diagnostic endpoint is necessary, protect it, collect the result, and remove it in the same change.

Shared hosting complicates ownership. The customer may control application files while the provider controls PHP packages and worker restarts. Ask the provider for the fixed branch, the activation time, and evidence that all serving workers changed. A status-page promise that “mitigations are in place” is weaker than a runtime version and completion time tied to your service.

Serverless and platform services need the same question in a different form. You may choose a runtime family but never touch the underlying package. Check the provider advisory and deployment status. If the provider says a redeployment is required, perform it. If it says the platform patched transparently, record the provider’s dated statement and verify the application is on the supported runtime family.

Build pipelines are part of scope because they can reintroduce old PHP. Search base images, internal mirrors, golden machine images, autoscaling templates, development containers, and recovery backups. A repaired production pod followed by a night-time scale-out from yesterday’s image is a failed patch with a short delay.

Patch, activate, and prove

The fastest safe response is a short sequence with an owner and a receipt at each step. It should work for a five-server shop and for a larger platform team because the questions remain the same even when the automation differs.

  1. Freeze the exposure map. Record every internet-facing or partner-facing PHP route, the workload behind it, the current runtime evidence, and the person responsible. Include direct origin access that bypasses the normal proxy. Mark unknowns as unknowns. Guessing “probably 8.4” hides the exact machine most likely to be missed.

  2. Choose the fix from the supplying vendor. Move upstream PHP to 8.5.12, 8.4.27, 8.3.36, or 8.2.35. For distribution packages, managed hosting, appliances, or long-term-support branches, follow the supplier’s advisory and retain the package or image identifier that contains the backport. PHP 8.1 and older need a supported upgrade, a vendor-supported backport, or a carefully governed source backport as a temporary bridge.

  3. Repair the build source. Update the Dockerfile, base-image digest, image tag policy, machine template, package lock, or configuration-management input. Build a new immutable artifact where that is your normal route. Avoid making a running container the only place where the fixed package exists.

  4. Deploy and replace every worker. Restart PHP-FPM pools, recycle Apache workers, replace containers, or follow the hosting provider’s activation process. Check high-availability peers and autoscaling groups. The PHP advisory explicitly requires worker restarts for some temporary settings because process state matters here; the same operational caution belongs beside the permanent update.

  5. Verify through the serving path. Query the workload behind each load balancer or record the version from each running artifact. Confirm the package, process start time, image digest, and deployment revision. Send a harmless request through the public route and correlate it with a fixed backend. A green homepage proves availability. It does not reveal which runtime served it unless the deployment evidence connects the two.

  6. Test rollback and scale-out inputs. Start one fresh instance from the current template or image and confirm it contains the repair before allowing normal scaling. Inspect the previous deployment revision and prevent an automatic rollback to it. Keep a known-good fixed artifact available for emergency recovery.

  7. Open the lookback from 17 October. Preserve relevant access, proxy, worker, process, file, identity, and outbound-network records before retention removes them. Search for suspicious multipart upload activity, fatal-error clusters, unexpected child processes, new files in served directories, changed startup hooks, and unfamiliar outbound connections. Treat indicators as investigation leads and account for what the logs do not retain.

  8. Escalate on evidence, not on the headline alone. An unexplained shell launched by a web worker, a new executable in a temporary directory, a modified PHP file, an unknown scheduled task, or suspicious outbound traffic justifies incident handling. Isolate the affected workload, preserve evidence, replace it from a known-good source, and rotate credentials the web process could read. Do not erase the machine before collecting the records needed to scope the event unless ongoing harm requires immediate containment.

  9. Write one closure record per route. Keep the old runtime, supplying vendor, fixed package or image, deployment time, worker replacement evidence, fresh-instance test, lookback interval, records reviewed, findings, unknowns, and owner. A dashboard percentage can summarise the campaign. The route-level record is what closes it.

This sequence avoids two bad extremes. One is changing a package and assuming the incident is over. The other is rebuilding every server in panic without checking whether it was exposed or preserving useful evidence. Patch all affected serving runtimes promptly, then scale the investigation according to reachability and local signals.

Temporary controls buy time, not closure

The PHP team published configuration changes for operators who cannot update immediately. These measures are narrow. The advisory says only an update fully fixes the flaws, and CERT-EU says isolation should be considered when an internet-facing application cannot yet be repaired.

For CVE-2026-109618, PHP recommends setting memory_limit=-1 in a place applications cannot override. It also recommends a low post_max_size, with 8 MB suggested where uploads are unnecessary and 32 MB where they are required, while keeping upload_max_filesize below that value. Proxy or web-server limits on decoded request size, field count, multipart parts, headers, and nesting can reduce the reachable shapes.

Those settings have operational consequences. Removing the PHP memory limit can let one request consume more memory until the operating system or container stops it. A container or control-group memory boundary can limit the blast radius, but the PHP advisory warns against substituting address-space limits that can trigger the vulnerable allocation failure. Test the change against the application’s workload and watch memory use.

For CVE-2026-109619, the advisory recommends disabling session upload progress and restarting every worker. On PHP 8.5 it also recommends disabling fatal-error backtraces, followed by another worker restart. These settings block confirmed paths, not every possible variation. Applying them inside application code is too late because request parsing has already begun.

A web application firewall can reduce exposure to known request patterns. Cloud providers and security vendors shipped rules quickly on 21 October. That buys patching time when all traffic genuinely passes through the control. It does not protect a direct origin address, an internal route, a partner connection, or a new variation the rule does not recognise.

Isolation is the cleanest temporary boundary when the application can tolerate it. Remove the service from public reach, allow only a small trusted source range, or place a maintenance response at an upstream proxy while the backend is repaired. Confirm that the origin cannot still be reached directly. A maintenance page at the content-delivery layer is decoration if the vulnerable server remains public on another address.

Every temporary control needs an expiry condition. Write the affected route, the measure applied, the time, the person who approved the business impact, and the event that removes it. “WAF enabled” should end with “fixed runtime verified on every backend,” not with an open-ended assumption that the filter became the permanent patch.

What a compromise would put at risk

Remote code execution takes on the authority of the PHP worker and the surrounding deployment. That authority differs by system. A carefully confined container may hold read-only application code, a short-lived database credential, and no cloud metadata access. An old shared server may let the web account read configuration for several sites, write to served directories, call local administration tools, and reach a production database.

Inventory the worker’s real reach before choosing recovery steps. Check environment variables, mounted secrets, configuration files, service-account tokens, database credentials, object-storage keys, message-queue access, internal network routes, and writable volumes. Include credentials used only during startup if they remain in the process environment or filesystem.

If evidence supports execution, replacing the PHP runtime alone is insufficient. Rebuild the workload from a known-good source. Rotate secrets the process could read, even if logs do not show each one being used. Review database and cloud activity performed under those identities. Inspect adjacent services reachable from the compromised network position. Preserve the old artifact or disk according to the response plan so later questions do not depend on memory.

The boundary can lower the cost of that response. A web container with a read-only root filesystem and a narrow service identity leaves fewer places to persist and fewer credentials to replace. Network policy that permits only the database and one required API leaves fewer outbound routes. External logs remain available even if the workload is replaced. Those controls do not remove the PHP flaw, but they turn a runtime failure into a smaller incident.

Shared hosting creates the opposite concern. Several sites may run under one account or worker pool. A writable directory used by one site may serve code for another. One database password file may contain several tenants. Scope follows the operating boundary, not the domain name in the first alert.

Backups need a date check too. Restoring a filesystem captured after 17 October may restore a malicious file along with the site. Restoring an older image may restore the vulnerable PHP version. A useful recovery combines clean application content with a fixed runtime and newly issued credentials, then validates the result before traffic returns.

There is no evidence in the cited advisories that every vulnerable PHP server was compromised. There is evidence of attempted exploitation before disclosure and broad exploitation after it. That supports prompt patching and a dated review. It does not support declaring an incident on every host without local evidence.

Keep the receipt after the emergency

This event exposes an inventory gap that will outlive both CVEs. Teams often track applications and libraries while treating the interpreter, worker mode, and base image as background detail. The runtime is part of the attack surface. Put it beside the service name, owner, exposure, update route, and deployment evidence.

Record how the runtime becomes active. For PHP-FPM, name the pool and restart mechanism. For Apache, record the module and worker recycle. For containers, record the source image, immutable digest, deployment revision, and scale-out template. For managed hosting, retain the provider’s runtime statement and the control that selects the branch.

Add a fresh-instance check to the normal release path. Starting one workload from the current image and reading its package evidence catches stale registries, mutable tags, and templates that production has quietly stopped using. The test is cheap compared with discovering the old runtime during an emergency scale-out.

Keep enough telemetry outside the web server to investigate the gap between first exploitation and disclosure. Access logs alone rarely settle code execution. Process creation, file changes, outbound connections, workload identity use, and deployment events provide the context that web logs lack. Retention should cover more than the day an advisory arrives.

Patch timing deserves a consequence-based rule. An unauthenticated flaw in an internet-facing runtime belongs on an hours clock once a supported fix exists. A seven-day target may suit an internal tool behind several controls. The same calendar promise for both ignores what an attacker can reach and what the process can access.

The durable model is small. Identify the request path. Name the runtime. Repair its source. Replace the running workers. Prove a fresh instance stays fixed. Search from the earliest supported date and state the limits of the evidence. That model works for the next interpreter flaw even when the CVE numbers and configuration names change.

PHP2Shell is serious because the vulnerable layer is common, the request can arrive without a login, and exploitation attempts began before many defenders knew a patch was coming. The response still benefits from calm precision. Patch the process that serves the request, not the binary that happens to answer in your shell. Search from 17 October, not from the time the ticket was created. Close with evidence another engineer can repeat.

For more practical security explanations without a daily flood, join the newsletter. It is one email per month.

Sources