Tag Archives: CVE-2026-109618

After PHP2Shell Disclosure, My WAF Traffic Changed: Reading a Six-Hour Surge

After PHP2Shell became public, I noticed an increase in traffic and apparent exploit probes hitting my web infrastructure. The change was visible in my WAF monitoring: a relatively quiet period gave way to sustained request volume, more critical rule matches, and more deny decisions.

The interesting part was not the tallest spike. It was the higher level of activity that continued between the spikes.

This post looks at a six-hour dashboard capture, what it tells me about that activity, and how I would investigate whether particular requests were attempting PHP2Shell exploitation. The distinction matters: the dashboard shows increased traffic and security-rule activity; attributing those requests to a specific vulnerability requires request-level evidence.

What Changed in the Six-Hour Window

Figure 1 — My WAF monitoring capture. Times below refer to the displayed axis labels. The screenshot does not include a calendar date, timezone, or disclosure marker. Values are visual estimates, not measurements exported from the underlying time series.

The request-rate panel shows several distinct periods:

Displayed periodApproximate inspected trafficWhat stands out
07:00–09:20Mostly below 1 request/secondA relatively low level of traffic, interrupted by short bursts
Around 09:30–10:45Several bursts around 2–3 requests/secondActivity becomes more uneven
Around 11:15–12:30Often around 3–4 requests/secondA sustained elevation, rather than isolated spikes
Near 12:30A brief peak above 6 requests/secondThe largest visible burst, followed by a sharp decline

The deny/enforce series also rises during the busier period, reaching roughly 1 decision per second around its busiest sustained stretch. Critical detection-rule matches become more frequent, while the allow/enforce series rises substantially too.

That combination makes the window worth investigating. It does not mean every additional request was malicious. Legitimate visitors, crawlers, generic vulnerability scans, policy violations, and vulnerability-specific probes can all contribute to the same traffic graph.

I would also resist turning this image into a precise claim such as “attacks increased by 800%.” There is no exported baseline here, and the earlier part of this window is not a documented pre-disclosure control period. What I can say is that the later portion carries a visibly higher request rate alongside increased WAF detection and deny activity.

Why PHP2Shell Is Relevant

PHP2Shell refers to two critical vulnerabilities in the PHP runtime: CVE-2026-109618 and CVE-2026-109619, both rated CVSS v4.0 9.2. Public disclosure and fixes arrived on October 21, 2026, after a leak brought the coordinated release forward. Canonical’s disclosure summary documents that timeline.

These are runtime memory-safety problems. Updating a CMS plugin does not update the PHP interpreter beneath it.

CVE-2026-109618: Cleanup After an Interrupted Allocation

PHP’s out-of-memory handling could interrupt execution while internal request data was only partly initialized. Request shutdown could then use that invalid state. Multipart parsing provides several ways to create memory pressure, including inputs whose in-memory representation is much larger than their wire size.

The PHP OOM advisory documents unauthenticated remote code execution against Apache mod_php, including a path before application code runs. It also explains why exploitation depends on the server interface and execution path. Treating a small HTTP body or an application login screen as sufficient protection misses where the failure occurs.

CVE-2026-109619: State Outliving the Request That Owned It

A long-lived worker can retain references to request-owned memory after that memory has been released. A later request handled by the same worker may consume those references before normal initialization resets them, producing a use-after-free.

The PHP stale-state advisory describes separate operations that leave state behind and consume it later. This is why a sequence of requests and worker reuse can matter more than one conspicuous request. The reachable paths vary with configuration, extensions, and application behavior.

The threat model is an unauthenticated attacker who can reach an affected PHP-backed web service. Potential impact includes code execution with the worker’s privileges and disclosure of process memory. The researchers’ PHP2Shell overview describes both impacts and their differing prerequisites.

The Rule IDs Tell a Broader Story

The dashboard’s “Top matching CRS detection rules” table includes several familiar rule families. For orientation, these are their meanings in the upstream CRS v4.29.0 ruleset; the deployed version and local policy still need checking.

Rule IDUpstream detection or policy
920450Restricted HTTP header
930130Restricted-file access, including sensitive configuration and source paths
932160Unix shell code patterns
930120Operating-system file access
942550JSON-based SQL injection
920420Request content type disallowed by policy

Sources: upstream protocol rules, file-access rules, command-execution rules, and SQL injection rules.

This mixture is consistent with broad probing, although individual matches can also be false positives. None of these labels is a PHP2Shell verdict. A Unix shell pattern does not establish that a shell ran, and a restricted-file match does not establish that a file was read.

There are two counting traps here. One request can match several rules, so rule matches are not unique attack attempts. The table also shows fractional “Matches” values over its last-five-minutes window. Without its query definition, I would not report those values as literal request counts.

In CRS anomaly scoring, detection and blocking are separate: rules contribute to a transaction score, and the configured threshold determines the blocking decision. “Critical” is a rule severity, not confirmation of compromise. The CRS anomaly-scoring documentation explains that separation.

Three Dashboard Readings I Would Not Take at Face Value

The screenshot contains a useful reminder that dashboard panels answer different questions.

“Suspicious requests/minute: 0” does not clear the whole window. Historical deny activity is visible elsewhere. The tile might use a different classification, filter, or reduction; its query is not supplied. I would inspect that definition before treating it as an all-clear signal.

“Allow/enforce” does not mean verified harmless. It records the decision represented by that metric. Conversely, a deny decision needs its enforcement phase and transaction context checked before making claims about what reached the application. Response-side blocking, for example, occurs after backend processing.

The displayed 37.232 ms p95 is WAF evaluation time. It is not end-to-end application latency, and the image does not establish the aggregation window behind that value. Likewise, scrape health 1 describes the monitored scrape signal, not the health of every origin service.

From a Traffic Surge to Exploit Evidence

The diagram below separates the observed activity from the evidence needed to attribute it. It is an investigation workflow, not a claim that my site followed a successful PHP2Shell attack chain.

Figure 2 — The screenshot establishes the traffic change. Request logs and runtime evidence establish what individual requests attempted and whether an affected service was reached.

I would investigate the elevated interval in three passes:

  1. Identify the transactions. Export WAF audit and reverse-proxy access records for the same interval. Retain timestamps with timezone, request IDs, destination host, method, URI, content type, request size where available, matched rule IDs, anomaly score, decision, and enforcement phase. Deduplicate by transaction before counting attempts.
  2. Check what reached the origin. Correlate request IDs with backend responses, PHP memory errors, worker crashes or restarts, and unusual response sizes. Check where body inspection is enabled and where request bodies are truncated or rejected. An HTTP status alone cannot resolve the request’s impact.
  3. Establish the runtime and outcome. Determine whether the target route actually reaches PHP, which build serves it, and whether the request pattern fits an advisory-described path. Correlate any suspected success with process, filesystem, network, or memory-disclosure evidence.

The researchers identify unusual multipart requests, memory-limit failures, worker instability, and unexpectedly large or malformed PHPSESSID response cookies as investigation leads. These signals need correlation; their absence does not establish safety. Memory disclosure may occur without a spawned shell. PHP2Shell investigation guidance

For a Debian/Ubuntu host using systemd, this is a starting point for the runtime-error pass. The service names are examples; PHP errors may instead be routed to application, container, or dedicated error logs.

# Review relevant service messages for the last six hours.
journalctl -u apache2 -u php8.3-fpm --since "6 hours ago" --no-pager \
  | rg -i 'allowed memory size|out of memory|segfault|child.*exited|fatal error'

This is triage, not an exploit detector. A memory error has many possible causes. The value comes from linking it to the matching HTTP transaction and the worker that handled it.

What I Would Prioritize on an Exposed PHP Service

First, establish whether PHP is on the request path at all. Internet-wide scanners do not need an accurate inventory of my stack to send probes. WAF matches alone cannot establish that the underlying service is vulnerable.

For an affected deployment, the PHP security release lists these first fixed upstream releases:

PHP branchFirst fixed release
8.28.2.35
8.38.3.36
8.48.4.27
8.58.5.12

Install a fixed build or a vendor-confirmed backport, then restart the serving workers. Check the runtime handling web requests: php --version in a terminal can describe a different installation. For containers, rebuild and replace the running workload. Older branches require checking vendor support or moving to a supported release; a version string alone can miss a distribution backport.

Keep WAF inspection and appropriate request limits in place, but verify their coverage. A rule match is not proof that all PHP2Shell variants are covered. Restrict direct origin access so traffic cannot bypass the intended inspection path.

There is also a follow-up worth checking even after the October 21 update. On October 23, PHP advised setting session.upload_progress.enabled=0 pending a regular bugfix release for a further, configuration-dependent issue tracked as CVE-2026-115560. Its prerequisites include switching the session serializer at runtime under a particular configuration. Follow the PHP session-upload-progress advisory, apply relevant settings before request parsing, and restart workers. This is additional guidance, not a replacement for the security update.

What I Take Away From This Window

The operational lesson from my dashboard is the sustained elevation: more inspected traffic, more critical rule matches, and more deny activity in the period after PHP2Shell became public. That is enough to prioritize investigation and check exposure.

It is not enough to count confirmed PHP2Shell attempts or claim that exploitation succeeded. Those conclusions need the request records and runtime evidence behind the charts. My next question is which transactions explain the change, what reached the origin, and whether any affected PHP runtime was in their path.

References