Black October and PHP2Shell: what happened, how the bugs work, and what to do
Between 19 and 22 October 2026, a lot of security news landed at once. Someone on X called it “Black October,” and the name stuck. The loudest story in the pile is PHP2Shell, two critical bugs in the PHP engine that the PHP project says can give an unauthenticated attacker remote code execution.
This article is a research write-up. Every factual claim links to the source it came from, and where sources disagree I say so instead of picking a side. I also explain the bugs in plain language first, then go deeper for readers who want the technical detail.
The short version:
- PHP2Shell is two CVEs, CVE-2026-109618 and CVE-2026-109619, both CVSS v4.0 9.2.
- Fixes shipped on 21 October 2026 in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35.
- The PHP project says there is no complete mitigation. Updating is the only safe choice.
- PHP 8.1 and older get no fix from the PHP project. You need a vendor backport or an upgrade.
- “Black October” is a nickname, not an official event. Several incidents in the wave started months earlier.
In this article: What is Black October · PHP2Shell in plain English · The two CVEs · Timeline · Technical deep dive · Exploitation status · What to do now · Other incidents
New to this kind of bug? Our guides on command injection and the OWASP Top 10 cover why running attacker-controlled code on a server is the worst case for a web app. PHP2Shell reaches the same result through memory corruption instead of an injection flaw.
What is Black October?
Black October is an informal label for the security disclosures made from about 19 to 22 October 2026. SecurityToday reports that an X post from the account AISafetyMemes at 03:42 UTC on 22 October called the previous 48 hours “the worst in cybersecurity history” and coined the name. A Yahoo Tech analysis (linked in the sources) says its own searches of X for 21 to 23 October did not find that post, so treat the origin as unverified.
Both outlets make the same point about the label. This was mostly a wave of disclosures, not a sudden wave of new attacks. SecurityToday says many of the underlying attacks began weeks or months earlier, and it does not attribute the surge itself to AI. We list those incidents near the end of this article. For the general playbook for a week like this, see our post on zero-day detection and response.
The one that made mainstream news is PHP2Shell, because PHP is everywhere. Wiz says the flaws affect roughly 70% of the internet, a figure that comes from Wiz’s own announcement and which I have not seen independently measured.

Wiz’s announcement post on X, 21 October 2026. Source: x.com/wiz_io.
PHP2Shell in plain English
PHP is the language behind WordPress, Drupal, Joomla and a huge number of custom sites. If you are not sure how a request travels to a web server, our networking, IPs and ports primer is a gentle start. A program called the PHP interpreter reads the code and runs it for every visitor. PHP2Shell is a set of bugs in that interpreter, not in any one website.
Here is the idea without jargon. When PHP runs out of memory in the middle of handling a request, or hits a fatal error, it stops that request abruptly. The stop is supposed to be clean. These bugs exist because, in a few places, it was not. PHP had already written down a note saying “my data is over here,” then got interrupted before it finished setting up or cleaning up. The note stayed behind, pointing at memory that was either not ready or already given back.
Most PHP servers reuse the same worker process for many requests. So the next request on that worker can walk into the leftover mess. If an attacker controls what ends up in that memory, they can steer PHP into running their code.
The NCSC’s alert describes it in similar terms: an attacker could trigger an out-of-memory condition in PHP that results in memory corruption, and under certain conditions that could be exploited for unauthenticated remote code execution.

The UK NCSC alert, published 21 October 2026. Source: ncsc.gov.uk.
The two CVEs at a glance
| CVE-2026-109618 | CVE-2026-109619 | |
|---|---|---|
| PHP project label | RCE via OOM corruption | RCE via stale global state |
| Short description | Out-of-memory handling can interrupt PHP while internal request data is only partially initialized. | Request-owned internal state can survive after the memory behind it has been freed. |
| CVSS v4.0 | 9.2 (Critical) | 9.2 (Critical) |
| Vector | AV:N/AC:H/AT:P/PR:N/UI:N | AV:N/AC:H/AT:P/PR:N/UI:N |
| Advisory | GHSA-w8hw-w9fx-j79w | GHSA-6wmf-4rv5-p3rp |
| Fixed in | 8.2.35, 8.3.36, 8.4.27, 8.5.12 | 8.2.35, 8.3.36, 8.4.27, 8.5.12 |
Both advisories list the same weakness types: use after free (CWE-416), improper handling of exceptional conditions (CWE-755), access of an uninitialized pointer (CWE-824), improperly controlled sequential memory allocation (CWE-1325) and incorrect initialization of a resource (CWE-1419).
The vector says Attack Complexity: High and Attack Requirements: Present. That matters, and we come back to it below.

The PHP project’s GitHub advisory for CVE-2026-109618. Source: github.com/php/php-src.
Timeline
Dates below come from Wiz Research and the PHP.net news archive unless noted. Times are from Recorded Future.
| Date (2026) | Event |
|---|---|
| 19 August | Wiz Research discovers the first PHP vulnerability. |
| 20 August | Wiz confirms unauthenticated RCE against Drupal. |
| 21 August | Wiz confirms unauthenticated RCE against WordPress. |
| 24 August | Wiz reports the issues to the PHP security team. |
| 21 September | PHP acknowledges the reports and begins a coordinated fix. |
| 20 October | PHP announces an advance notice for a security release the next day. |
| 21 October, 02:29 UTC | Per Recorded Future, an archive with leaked details is first submitted to VirusTotal. |
| 21 October, 07:34 UTC | Per Recorded Future, Patchstack reports active exploitation incidents from hosting partners. |
| 21 October | PHP releases 8.5.12, 8.4.27, 8.3.36 and 8.2.35. The NCSC, the NSA and others publish warnings. |
| 23 October | PHP publishes an informational advisory about session upload progress and a third CVE (CVE-2026-115560). |
Technical deep dive
This section is for developers, sysadmins and pentesters. If you only need the fix, skip to What to do now. If you want safe hands-on practice with the code-execution side of web security, try our command injection lab guide and insecure deserialization lab guide. They teach different bug classes, but the goal (code execution on the server) is the same.
The shared root cause: longjmp and half-finished state
Wiz states that all of the disclosed vulnerabilities are memory-safety issues stemming from how PHP handles fatal errors, especially out-of-memory (OOM) errors. The PHP advisory for CVE-2026-109618 says PHP uses longjmp() to exit the current context when it runs out of memory. In C, longjmp() jumps straight back to a saved point and skips the code between. Anything that code would have initialized or cleaned up is skipped too.
The advisory describes the result this way: in some contexts, request memory is reachable before it is fully initialized. If an allocation fails at that point, PHP does not account for the early exit and leaves the request memory in an invalid state. The failed allocation does not need to be related to the corrupted memory. Request shutdown may then read or branch on that memory.
Imperva’s technical write-up summarizes the pattern as bailouts that interrupt PHP after it has published a pointer or allocated a cache slot but before cleanup finishes.
CVE-2026-109618: OOM corruption
The advisory lists several ways a request can reach OOM while staying inside PHP’s apparent limits:
- A multipart request without a usable declared content length can pass the initial
post_max_sizecheck and keep supplying body data. - A multipart part can contain an unlimited number of headers. PHP uses far more memory to represent them than they take on the wire, so a request within
post_max_sizecan exhaustmemory_limit. - Empty file controls do not count toward
max_file_uploadsbut still create file metadata arrays, and nested field names multiply that work. - Application-specific inputs that cause large allocations can also reach OOM.
Wiz describes two concrete bugs under this CVE:
- A use after free in the session upload-progress unserializer. The session module decodes data during upload-progress handling and keeps bookkeeping in
BG(unserialize). A fatal allocation failure can leave that global published after the request heap is destroyed. A later request can then treat attacker-reclaimed memory as live unserializer state. - A memory disclosure that works regardless of the application. Multipart parsing (RFC 1867) runs during request startup, before module initialization finishes. A session ID can be published in
PS(id). After an OOM abort, that pointer may reference freed memory. A later multipart request reaches upload-progress cleanup, which trusts the stale pointer and length, and the reclaimed heap contents are URL-encoded into aPHPSESSIDresponse cookie. Wiz says this works even if the application never callssession_start(). Imperva’s analysis puts the amount of process memory exposed at about 67 KB.
That second bug matters even without code execution. Leaked worker memory can contain passwords, tokens, API keys and certificates from other requests.
CVE-2026-109619: stale global state
The advisory says request-owned global state (error, session, object and extension state) can survive in a persistent worker after the request heap is freed. A later operation that runs before normal request initialization can then use that stale state. Fatal errors, including OOM, can skip the reset step, and non-fatal diagnostics during shutdown can repopulate error state after it was cleared.
Wiz describes two bugs here:
- A fatal-error backtrace use after free.
EG(last_fatal_error_backtrace)keeps the backtrace of the last fatal error. A fatal OOM can destroy the request heap without clearing it, and the next request can reach the dangling pointer through an early diagnostic. The advisory names PHP 8.5’sfatal_error_backtracessetting as the basis of the confirmed mod_php RCE. - An OPcache type confusion. OPcache stores compiled structures in shared memory and hands out indices into per-worker runtime-pointer maps. In
zend_accel_script_persist(), the process-localCG(map_ptr_last)advances before the shared frontier is published. If an allocation fails between those two steps, slots stay marked free while partially persisted objects still reference them. When another worker reuses a slot for a different runtime object, stale metadata resolves to a pointer of the wrong type. That corrupts shared OPcache state across workers.
The advisory adds a second confirmed path: session upload progress, exploitable through OOM during input processing and validated against mod_php and PHP-FPM.
Which servers are exposed
The PHP project says all PHP installations serving web requests are at risk, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. It considers the risk highest for mod_php, with a demonstrated exploit where an unauthenticated request triggers the attack before any application code runs.
The CVE-2026-109618 advisory is more specific about what has and has not been shown:
| Server API | Status in the advisory |
|---|---|
| Apache mod_php | Validated. It runs shutdown even when startup fails. RCE was demonstrated over HTTP with default limits. |
| PHP-FPM | Considered susceptible from source analysis. No complete exploit demonstrated. |
| FrankenPHP | Likely susceptible to application-independent attacks, from source analysis. |
| LSAPI | Likely susceptible to application-specific exploits, unvalidated. |
For CVE-2026-109619 the advisory says the session upload-progress path was validated against both mod_php and PHP-FPM, and that the request that plants the stale state and the request that uses it must hit the same worker.
How an RCE gets built from this
Wiz says its RCE chains combined the disclosure bug, to defeat address randomization, with a corruption primitive from the use-after-free or OPcache bugs. It demonstrated unauthenticated RCE against the latest container images of WordPress, Drupal and Joomla at the time of testing, and notes that a bare “Hello World” app on an affected PHP version can also be vulnerable. Wiz did not publish exploit code in its write-up, and this article does not include any.
Why “High complexity” still means patch now
The CVSS vector rates attack complexity as High. Imperva notes the attacker needs a later request to reach the same worker and place crafted data into the freed space, otherwise the worker may crash or respond normally. That is a real hurdle. It is also the kind of hurdle that automation handles well, and Trail of Bits’ Dan Guido told 404 Media that a colleague built a working exploit in one day with an AI coding tool. Even a failed attempt can crash workers, so exposure is not limited to successful compromise.
Was PHP2Shell exploited in the wild?
This is where sources conflict, so here is each claim with its owner.
- Patchstack, as cited by Recorded Future and CircleID: hosting partners reported active exploitation incidents on 21 October. Patchstack’s own telemetry showed probing but, as of 13:57 UTC, no hard evidence of successful breaches.
- Cloudflare, as summarized by PPC Land: the vulnerability leaked before the official release date and Cloudflare observed attack traffic against its customers. It also said attackers were mutating payloads to try to get around its rules. The post gave no volumes.
- 404 Media: reports figures it attributes to Cloudflare’s CEO, thousands of exploit attempts in the 10 hours before the announcement and millions by the time of publication. I could not find those numbers in the Cloudflare post summaries I read, so treat them as 404 Media’s reporting.
- Imperva: during pre-disclosure monitoring it saw no traffic matching its monitored exploit patterns.
- 404 Media also reports no immediate, widespread exploitation, and that many low-effort LLM-written exploits circulating online likely do not work.
- Recorded Future: public GitHub repositories claim proof-of-concept exploits, but quality varies. One researcher published two PoCs and then invalidated the theory they relied on. Insikt Group had not tested them.
My reading: probing and attempts are well documented, confirmed compromises are not. That is not a reason to wait.
Who found it, and the AI question
The PHP advisories credit four reporters by handle: lcfr-hax, sagitzadik-wiz, tomash-areion and amyb-oai. Wiz’s write-up credits its researchers Sagi Tzadik, Shir Tamari, Nir Ohfeld and Tomer Schwartz, with verification help from the PHP team, Calif and OpenAI.
Wiz says AI tooling played a large role and lists Wiz Atlas, Gemini 4 Argon and GPT-5.6 Sol. Other outlets describe it differently: TechTarget and others attribute discovery to GPT-5.6 Sol. Press coverage also notes the bugs sat in the code for more than a decade, and Wiz says every PHP version from the last decade is affected by at least one of the flaws, back to PHP 5.4.
There was friction around the disclosure itself. 404 Media reports that Chainguard published fixes hours before the official disclosure and was criticized for it, including by the PHP Foundation. Chainguard’s CEO said the company independently found the same bugs by prompting a frontier model. Amazon said it saw exploit attempts before the official disclosure. Whether anything leaked, or several parties simply found the same easy bugs, is unresolved in the reporting I read.
The 404 Media piece (linked in the sources) covers the wider argument about AI and vulnerability discovery. For our own view on that question, see will cybersecurity be replaced by AI.
What the patch changes
The CVE-2026-109618 advisory says the fix removes longjmp() for OOM. A soft OOM is now raised at the next VM safe point, and a hard OOM triggers _exit() at a second, higher threshold. For CVE-2026-109619 the advisory says the only reliable mitigation is a release with the complete state-reset patch.
What to do now
1. Find every PHP install
The NCSC’s first recommendation is to find all PHP installations, starting with internet-facing systems, and it says not to limit discovery to outdated versions because every version may be affected. Start from what the internet can see: our subdomain finder lists hosts you may have forgotten, and the guides on subdomain enumeration and enumerating network services show how to turn that list into a service inventory (the Nmap lab guide covers the tool). PHP can also be embedded in third-party products and appliances. The NCSC says NCSC-NL maintains a list of affected products.
2. Update to a fixed release
| Branch | Fixed version |
|---|---|
| 8.5 | 8.5.12 |
| 8.4 | 8.4.27 |
| 8.3 | 8.3.36 |
| 8.2 | 8.2.35 |
| 8.1 and older | No fix from the PHP project |
On Debian or Ubuntu with a third-party PHP repository, the flow in Laravel Forge’s advisory is a good template. Check the version, confirm the fixed package is the install candidate, then upgrade every installed package for that branch:
php8.4 -v
sudo apt-get update
apt-cache policy php8.4-fpm
sudo apt-get install --only-upgrade -y \
-o Dpkg::Options::="--force-confold" \
$(dpkg -l 'php8.4*' | awk '/^ii/ {print $2}')
php-fpm8.4 -v
Three details that catch people out:
- Forge notes that its automatic security updates do not patch PHP when it comes from a separate package repository. Check whether your own setup behaves the same way.
- Long-running PHP processes started from the command line keep running the old code until restarted. That includes queue workers, Horizon, Octane and Reverb.
- Containers and single-binary builds carry their own PHP. Rebuild the image on a fixed version. Upgrading the host does not change what is inside the image.
A distribution may backport the fix without changing the visible version string to 8.x.y. The PHP advisory tells users on older branches to contact their vendor for details. Confirm with your distro’s security tracker, not only with php -v.
3. If you cannot patch yet
The PHP advisory lists partial measures and states they do not make an installation safe. The two advisories together suggest:
; php.ini or FPM pool config, then restart all workers
session.upload_progress.enabled = 0
fatal_error_backtraces = 0 ; PHP 8.5 only
post_max_size = <conservative value>
The CVE-2026-109618 advisory also lists memory_limit=-1 as its primary configuration mitigation, with the caveat that it is incomplete, plus bounding multipart parts, headers per part and uploads, blocking multipart requests at the frontend if the application does not use them, and using a container or cgroup memory.max. It warns against RLIMIT_AS and RLIMIT_DATA because they make mmap() fail and trigger the vulnerable path. For CVE-2026-109619 it adds pm.max_requests=1 if performance allows.
The NCSC’s blunt fallback: if mitigation is not possible or would take too long, consider taking the system offline until it is fully mitigated. The NSA made a similar recommendation, as reported by TweakTown. I could not load the NSA page itself, so I am relying on press reports for that guidance.
One more item from 23 October: PHP’s informational advisory recommends session.upload_progress.enabled=0 until the next regular bugfix release. It relates to CVE-2026-115560 and only applies to applications that switch session.serialize_handler to php at runtime with ini_set() before calling session_start(), with upload progress still on. PHP calls the setup unlikely in practice and says it will publish a full advisory with the next release.
4. Look for signs of compromise
Patching does not evict someone who is already in. Our HTTP security header analyzer will not detect PHP2Shell, but it is a quick way to review the rest of an exposed server while you are there (see also the HTTP security headers guide). The signals suggested by Wiz, Imperva and Recorded Future are:
- Unusual multipart requests followed by memory-limit errors in the PHP error log.
- Repeated HTTP 500 responses and PHP worker crashes or churn.
- Unexpectedly large or malformed
PHPSESSIDvalues in response cookies. - Unexpected child processes launched by the web server or PHP.
- Changed application files, new persistence, unusual outbound connections.
- Log review from 20 October 2026 onward, per Recorded Future.
The PHP advisory warns that clean logs do not prove safety, and Wiz notes that application logs may show little because the bug sits below the application layer. If you find something, start incident response and rotate credentials the PHP process could read, including database passwords, API keys and session secrets.
Other Black October incidents
Several of these are supply chain and credential problems, not memory bugs. The fixes are familiar: rotate secrets, pin dependencies and keep tokens short lived. Our posts on GitHub token leak risks and JWT security cover two of those themes.
The Yahoo Tech analysis and SecurityToday list many more items. A condensed version follows. I have kept to what those articles state and flagged where reports differ.
| Incident | What the sources report |
|---|---|
| eMudhra certificates | Eight intermediate CA certificates (six TLS, two code signing) had broken private keys. Mozilla’s Dennis Jackson said the keys were effectively all zeroes except the last byte. The issue was reported on 17 October. Apple, Microsoft and Mozilla removed trust between 19 and 20 October. See Inc42, and use our SSL certificate checker to inspect a certificate chain. |
| Node.js security release | Released 21 October as 22.23.4, 24.22.1 and 26.12.1, including a Brotli decoder out-of-bounds write (CVE-2026-111497). Per Konfirmity, it fixes 10 vulnerabilities and the CA bundle update removes trust tied to the eMudhra intermediates. |
| GitLab | Reports describe a CVSS 10.0 unauthenticated RCE through a regex integer overflow in CI/CD configuration. The articles I read give different CVE IDs (CVE-2026-110264 in one, CVE-2026-97229 in Rapid7’s coverage of an earlier 12 October release), so check GitLab’s own advisory before relying on either. |
| LiteLLM 1.107.0 | Per SecurityToday, the PyPI wheels contained a credential stealer and loader. The release and Docker image were removed. |
| UpdraftCentral and Migrator | Per SecurityToday, stolen keys were used on 17 and 18 October to create admin accounts and install malicious plugins. About 400,000 sites with an affected key were vulnerable. Fixed in 1.26.13 and 2.26.13. |
| Zeabur | Per SecurityToday, an attacker used plaintext internal credentials to take data from 321,810 accounts. The company is shutting down. |
| Railway | On 21 October, an unauthorized party may have accessed encrypted environment variables and decrypted some. Railway recommends rotating all variables and keys. |
| Tor exit relays | A researcher documented 333 compromised exit relays (about 10% of exit relays) redirecting SSH password logins to interception servers in early October. They were removed by 5 October. |
| SafePay ransomware | Joint advisory AA26-293A on 20 October from the FBI, CISA, the Secret Service and European partners, citing at least 600 victims. |
| AOMEI Backupper | A supply chain compromise delivering a dropper and backdoor, attributed by Google Threat Intelligence Group and Mandiant to UNC7017. |
| NGINX, Siemens, Signal | F5 rates three NGINX memory bugs 9.2 while the NGINX project rates them medium. A Siemens Software Center flaw (CVE-2026-110936) is on CISA’s KEV list. A Signal RingRTC bug (CVE-2026-113380) was fixed in September. |
SecurityToday’s article is a machine translation and says the German original is authoritative. Some figures in it, such as the Cairn platform numbers, are secondhand from Reuters. Verify any single number before you quote it.
Need help checking your exposure?
If you run PHP on the public internet and want someone to verify patch status, review configuration and look for signs of compromise, that is covered by our external network penetration testing and web application penetration testing. See all security services or contact us with a short description of your setup. Not sure what a test should cost or include? Read penetration testing cost first.
If you would rather learn to find this class of problem yourself, look at our cybersecurity training and the ethical hacker roadmap.
Related reading
- Zero-day vulnerabilities: how to detect, contain, and respond
- Command injection explained
- OWASP Top 10 2025 explained
- HTTP security headers guide
- Subdomain enumeration and recon guide
- Server-side request forgery (SSRF) explained
How this report was researched
I read the PHP project’s security announcements and both GitHub advisories, the NCSC alert, Wiz Research’s write-up, and vendor analysis from Imperva, Recorded Future and Laravel Forge. For incident context I used 404 Media, SecurityToday, Yahoo Tech and the secondary sources linked in the text. I could not load the NSA’s page (it returned an error), Cloudflare’s own blog post, or the NCSC PDF body, so statements about those come from the press coverage linked next to them. If any detail here conflicts with the PHP project’s advisories, the advisories win.
Sources
- PHP.net news archive 2026: security advisory, release announcements, informational advisory
- GHSA-w8hw-w9fx-j79w, CVE-2026-109618
- GHSA-6wmf-4rv5-p3rp, CVE-2026-109619
- NCSC: Critical security issue affecting PHP
- Wiz Research: PHP2Shell
- Imperva: PHP2Shell technical analysis
- Recorded Future: PHP2Shell exploitation
- Laravel Forge: PHP security advisory
- 404 Media: PHP2Shell, Black October and AI
- SecurityToday: Black October, many attacks had been running for months
- Yahoo Tech: Black October is a disclosure wave
- PPC Land: Cloudflare and PHP2Shell
- CircleID: PHP emergency patches and exploit reports
- TweakTown: NSA warning
- Inc42: eMudhra key flaw
- Konfirmity: Node.js October 2026 security releases
Screenshots are cropped captures of public pages, credited to their publishers above. This article will be updated as the PHP project, vendors and agencies publish more.