CVE
CVE-2026-109618, CVE-2026-109619, CVE-2026-115560, CVE-2026-46300, CVE-2026-123039
CWE
CWE-416, CWE-908, CWE-843, CWE-456
Affected Surface
- PHP (php-src) 8.2 before 8.2.35, 8.3 before 8.3.36, 8.4 before 8.4.27, and 8.5 before 8.5.12
- End-of-life PHP 8.1 and older lines, which Wiz says are affected back to 5.4 and which get no upstream release
- Apache `mod_php` (confirmed pre-application RCE), PHP-FPM (session-lifecycle RCE validated by the PHP security team), FrankenPHP (likely susceptible), and LiteSpeed LSAPI (conditional)
- Any PHP application on those runtimes, including WordPress, Drupal, Joomla, MediaWiki, and Magento; the bug is in the engine and does not depend on application code
- Official and derived `php`, `wordpress`, `drupal`, and `joomla` container images, and distribution PHP packages until vendor backports land
- Check Point Security Gateways with the Mobile Access portal, R80 through R82.20, and Spark firewalls R81.10.x, R82.00.x, R77.20.x and R80.20.x (`sk1000302`), exploited in the wild with Gaia OS `CVE-2026-123039` (R81.20, R82, R82.10 and R82.20, `sk1000362`)
Update, 14 November 2026 (Check Point gateways breached through PHP2Shell, then rooted with CVE-2026-123039): On 12 November, Check Point Research and a Check Point security advisory confirmed that PHP2Shell was used in the wild against the Mobile Access portal on Check Point Security Gateways, which runs Apache and PHP on the appliance. BleepingComputer and CISA, which added the follow-on privilege escalation to KEV on 13 November, back up the chain. Check Point lists every Security Gateway from R80 to R82.20 and several Spark firewall lines as exposed to PHP2Shell (CVSS 9.2, sk1000302). It shipped a PHP2Shell hotfix on 23 October and folded it into the November Jumbo Hotfix.
- Two waves. Exploitation began on 30 October from
130.94.19[.]204against Israeli organizations, with destructive activity at some of them. A broader second wave on 2 November hit organizations in several countries and showed no destructive activity. Check Point says the activity continued until 4 November. - The PHP2Shell part. On the gateways, PHP session files under
/opt/CPcvpn-R81.20/tmp/contained the markerupload_progress_pwn, and the portal’s Apache/PHP workers logged hundreds of segmentation faults in the same window. Both fit thesession.upload_progresspath described in theCVE-2026-109619section below. - Root through a 0-day. The attackers then went from the portal service account to root with
CVE-2026-123039, a Gaia OS flaw in the DynamicCLI daemon, which runs as root and accepts unauthenticated commands on the world-writable socket/tmp/dynamic_socket. CISA addedCVE-2026-123039to KEV on 13 November. - Post-exploitation. With root, they wrote PHP webshells into the portal’s template directory as
zz*.tplfiles (keyed withzz2026, uploads saved to/tmp/.up.tpl, later copied tochallenge_render.tpl). They then ran the TSGA backdoor as/tmp/CPconfigdaemon(nohup /tmp/CPconfigdaemon >/tmp/.bd.log 2>&1 &), which relaunches asslot_CPconfigdaemon, listens on TCP11072and the Unix socket/tmp/.p_sock, and drops the TSProxy library as/lib/libcpfwcommon.so. From there they ran thegogoscanner, moved over SSH to a VMware ESXi host asvpxuser, and ran thedestroy_v6wiper, which leavesDREAM_ANNEXATION.txtnotes. Check Point ties TSGA and TSProxy to the PhiliKit toolset that ESET has associated with UNC5221, but does not attribute the operation with high confidence. - IOCs (SHA-256): webshell
edb3e92eb7bda7413a09edcc4b1ba3cbed4bcdf42808a8abce63b6e4c3fda2b3,slot_CPconfigdaemon4171b521fa6441382aa2fa014a08e18ff9a6e0ab6096c9144072bf46b37a2ec8,CPconfigdaemond7549671c7189979f0624ec52dad9ea6a707d23146e3585ab8f8c4720eca0c63,gogo3b51080a909023f7d484e2443740b572307ce0054eb9e32d98f0a06bfcd2db90,destroy_v6c7d9477309fac6c638a1128442056a242de086996add8cb49cd6496c36003237,DREAM_ANNEXATION.txt0522937c0c0337be54fe25493539e5f5f8ceb9742c980aaff1cfc9515f3a96f7. Source IPs:130.94.19[.]204,130.94.19[.]168.
This matters beyond Check Point customers. It is a documented case of PHP2Shell used against PHP embedded in a vendor appliance, which is the kind of PHP that CISA’s 21 October alert told organizations to inventory. If you run Check Point gateways with Mobile Access enabled, install the November Jumbo Hotfix, then hunt on the appliance before trusting it:
# On the gateway (expert mode)
grep -rl 'upload_progress_pwn' /opt/CPcvpn-*/tmp/ 2>/dev/null
ls -la /tmp/CPconfigdaemon /tmp/.bd.log /tmp/.up.tpl /tmp/.p_sock /lib/libcpfwcommon.so 2>/dev/null
ps -ef | grep -E '[s]lot_CPconfigdaemon|[C]Pconfigdaemon'
netstat -lntp 2>/dev/null | grep ':11072 '
find / -name 'zz*.tpl' -o -name 'challenge_render.tpl' 2>/dev/null
ls -l /tmp/dynamic_socket
Check Point’s other indicators are scanner or wiper tooling under /tmp and gaps in firewall traffic logs after a logging daemon crash. Until the CVE-2026-123039 hotfix is installed, its interim mitigation is to stop the dynamic_server process and keep it from restarting. Any gateway with these artifacts should be treated as fully compromised, along with ESXi hosts reachable from it with vpxuser credentials.
Update, 29 October 2026: On 28 October 2026, CISA added the second half of the firewall takeover chain, Linux kernel CVE-2026-46300 (“Fragnesia”), to its Known Exploited Vulnerabilities (KEV) Catalog with a three-day federal remediation due date of 31 October 2026 and Forensic triage required per BOD 26-04: Yes. Because Fragnesia overwrites read-only files such as /etc/pam.d/su or /usr/bin/sudo in the Linux kernel’s in-memory page cache without modifying the on-disk inode, rebooting a compromised PHP host to apply kernel or PHP updates erases the in-memory /etc/pam.d/su backdoor before responders can see it. Before restarting or dropping caches on any unpatched Linux + PHP host, compare the page-cache view of /etc/pam.d/su and /usr/bin/su against direct O_DIRECT disk reads (dd if=/etc/pam.d/su iflag=direct status=none | sha256sum vs. sha256sum /etc/pam.d/su) and inspect running processes and web roots for webshells.
Update, 28 October 2026: Researchers at Dream (corroborated by The Hacker News and SecurityWeek) disclosed an in-the-wild campaign that chained a custom PHP2Shell (CVE-2026-109618 / CVE-2026-109619) exploit against session.upload_progress on PHP 8.0.30 with the Fragnesia (CVE-2026-46300) Linux kernel XFRM ESP-in-TCP page-cache privilege escalation to root 25 firewall and management servers across the United States, Japan, Taiwan, and South Korea between 23 and 24 October 2026:
- Four-stage PHP2Shell + Fragnesia root chain: The attacker built 11 JSON offset profiles (
libphp.soSHA-256 digests, ELF build IDs, andzval_ptr_dtor/zif_systemoffsets forPHP 8.0.30and7.4.33) and used a four-step sequence (A1~383 MB multipart memory exhaustion ->B1Set-Cookiepointer leak oflibphp.so->A2~500 MB stalehttp_session_varszend_referencewithmod_data=NULL->B2forgedHashTableentry pointingpDestructortozif_systemasnobody), then firedCVE-2026-46300in the sameB2request to splicepam_permit.sointo the 774-byte/etc/pam.d/sufile, elevate torootdespite SELinuxhttpd_tconfinement, and plant a 103-byte PHP root webshell. - Lateral movement to internal management server and Active Directory: On two rooted firewalls belonging to a U.S. government-affiliated organization, the attacker established an 8-port boot-persistent reverse SSH tunnel (
systemd+rc.local+ioxSOCKS5 proxy), pivoted on 24 October (11:35 UTC) to root an internal firewall management server over the tunnel with the same PHP2Shell + Fragnesia chain, decrypted the stored Active Directory LDAP service-account password using the device master key, enumerated all domain users and groups, and ran password-spraying and backdoor account-creation scripts. - Unit 42 retraction note: Separately, Palo Alto Networks Unit 42 updated its threat brief on 25 and 28 October to retract its earlier 23 October assessment that the European government post-compromise activity it observed had originated from PHP2Shell exploitation.
Update, 27 October 2026: On 26 October 2026, CISA added both CVE-2026-109618 (PHP Memory Corruption Vulnerability) and CVE-2026-109619 (PHP Stale Internal Request-State Vulnerability) to its Known Exploited Vulnerabilities (KEV) Catalog and revised its PHP Alert. Under Binding Operational Directive (BOD) 26-04, CISA assigned a three-day remediation deadline of 29 October 2026 for Federal Civilian Executive Branch agencies and set Forensic triage required per BOD 26-04: Yes on both entries, requiring defenders to check whether internet-exposed PHP systems were compromised before patches (8.5.12, 8.4.27, 8.3.36, 8.2.35, or -1 on Windows) were applied. FIRST EPSS scores have also climbed to 0.99981 (99.98th percentile) for CVE-2026-109618 and 0.99997 (99.99th percentile) for CVE-2026-109619.
Update, 26 October 2026: Over the weekend of 24 to 26 October, Perplexity detection and response lead Adel Karimi and Expel principal threat researcher Marcus Hutchins confirmed in-the-wild remote shell execution (CVE-2026-109618) across three Apache mod_php (PHP 8.4.26) honeypots. Whereas the first 26 hours of sensor telemetry had recorded 30 out-of-memory errors and 11 worker crashes without code execution, on 24 October two source IPs launched 35 working shells (3 on a single sensor and 32 across all three) using a four-stage HTTP request chain: exhausting PHP’s memory, reading back an 800 KiB decoded PHPSESSID cookie heap dump, computing a live function address from that dump using offsets from the public PoC exploit to defeat ASLR, and firing the shell payload on the next request. Separately, on 26 October the PHP project shipped -1 Windows rebuilds (8.5.12-1, 8.4.27-1, 8.3.36-1, and 8.2.35-1) after discovering that the 21 October Windows packages bundled brotli 1.2.0 (CVE-2026-111497) inside php_curl.dll.
Update, 24 October 2026: Late on 23 October, Palo Alto Networks Unit 42 updated its PHP2Shell threat brief with incident-response telemetry from a compromised European government entity where an attacker used an agentic AI post-exploitation framework after gaining initial access via PHP2Shell. Within two hours of the initial PHP exploit, the automated agent executed short, serially spaced command batches wrapped in machine-parseable section delimiters (==SHELLUSER==, ==PASSWD==) designed for LLM output parsing, dumped password hashes and multi-factor authentication secrets, established an NTLM-relay tunnel into Active Directory, and attempted Domain Admin privilege escalation. On the same day (23 October), CISA revised its PHP alert to add a SIGMA rule detecting PHP worker processes (php-fpm, php-cgi, httpd, apache2, frankenphp, lsws) spawning shells or download utilities, the FBI’s Internet Crime Complaint Center (IC3) published Public Service Announcement PSA261023, and the Canadian Centre for Cyber Security issued AL26-026 Update 1 advising administrators to keep session.upload_progress.enabled = 0 in place to block the unpatched CVE-2026-115560 runtime session.serialize_handler vector.
Update, 23 October 2026: On 23 October, the PHP security team published a follow-up informational advisory for a third, unpatched RCE vector tracked as CVE-2026-115560 that remains present in the 21 October emergency releases (8.5.12, 8.4.27, 8.3.36, and 8.2.35). The flaw is reachable when session.upload_progress.enabled is left on (1 by default) and an application sets session.serialize_handler to php_binary or php_serialize in its configuration, then switches session.serialize_handler to php at runtime via ini_set() before calling session_start(). Setting session.upload_progress.enabled = 0 in php.ini or the SAPI pool configuration blocks CVE-2026-115560 until the next scheduled PHP bugfix release. Separately, Amazon Threat Intelligence disclosed that its MadPot honeypots recorded the first in-the-wild PHP2Shell (CVE-2026-109618 / CVE-2026-109619) exploit attempt on 17 October 2026, four days before the embargo lifted, so defenders should push log hunting back to 17 October rather than 21 October. Cloudflare reported attack traffic scaling into the millions by 22 October, and Sophos documented threat actors attempting to deploy the Potassium Mirai botnet variant from 85.11.167[.]243, 217.60.242[.]76, and 45.198.224[.]169 (fetching a loader from 92.118.112[.]100 and connecting to thunder[.]godofthunder[.]st / 134.199.154[.]64:50792). For legacy Linux hosts, Debian/Ubuntu Sury PPA packages (php7.3 7.3.33-35, php7.4 7.4.33-31, php8.0 8.0.30-25, and php8.1 8.1.34-9) and unofficial community source backports for RHEL/CentOS 6/7 and Amazon Linux 1/2 (5.3.3, 5.4.16, 7.3.33) now carry ports of commit 712e60f9ed.
Most PHP security advisories are about a function or an extension: unserialize(), phar://, mb_*, GD. PHP2Shell is about the engine’s error model. For decades PHP has handled “allowed memory size exhausted” by longjmp()ing out of whatever C code was running, then tearing down the request heap. When that jump lands in the middle of request startup, worker globals can still point at freed or half-initialized request memory. The next request on the same worker reads them.
The PHP security team split the fallout into two CVEs, both CVSS v4.0 9.2:
| CVE | Advisory | Root cause | Demonstrated impact |
|---|---|---|---|
| CVE-2026-109618 | GHSA-w8hw-w9fx-j79w | OOM longjmp() interrupts initialization while request memory is reachable but not yet valid. Shutdown then reads or branches on it. | Unauthenticated RCE over HTTP against mod_php before application code runs. Remote worker memory disclosure through PHPSESSID. |
| CVE-2026-109619 | GHSA-6wmf-4rv5-p3rp | Worker globals (error, session, object, extension state) still point into a freed request heap. A later request uses them before initialization resets them. | Cross-request use-after-free leading to RCE on mod_php and FPM. Wiz built Drupal and WordPress chains from the fatal-error backtrace and OPcache variants. |
Fixed releases are PHP 8.2.35, 8.3.36, 8.4.27, and 8.5.12. Wiz says every PHP version from the last decade, back to 5.4, has at least one of the bugs. Lines 8.1 and older are end-of-life, and Aikido reports that the PHP security team has published minimal source backports for 7.4, 8.0, and 8.1.
Why this is already a 0-day problem
Disclosure did not go to plan. Aikido reports that the PHP team published an advance notice on 20 October after details leaked before the embargo ended, and then moved the release up to 21 October. Cloudflare had WAF rules live for all plans from 03:02 UTC on the 21st, roughly nine hours before public disclosure. It says it saw attack traffic against customers and watched attackers mutate payloads to get around the rules. watchTowr says its honeypot network saw exploitation on 21 October and that it reproduced CVE-2026-109618 itself. Neither CVE was in CISA KEV as of the catalog’s 21 October release, but PHP2Shell should be handled as exploited now.
Update, 22 October: CISA published a separate alert on 21 October telling organizations to inventory PHP, including PHP embedded in other products, and to take PHP 8.1 and older systems offline if they cannot be patched or updated. The alert says CISA itself had not yet observed exploitation when it was published, and the KEV catalog still did not list either CVE on 22 October. SecurityWeek quotes Hadrian as seeing “php2shell exploited in the wild at scale”, mostly through the session upload-progress path. That is the PS(id) / BG(unserialize) producer described below. Patchstack and Vega reported attempts as well, Vega’s based on a public PoC. Treat session.upload_progress.enabled = 0 as the first stopgap on any host you cannot upgrade today.
CVE-2026-109618: OOM during multipart startup
PHP parses multipart/form-data bodies (RFC 1867) during php_request_startup(), before any script runs and before normal module request initialization has finished. The advisory lists three ways to push that parser into OOM while staying inside the limits PHP thinks it’s enforcing:
- a multipart request with no usable
Content-Lengthcan pass thepost_max_sizecheck and keep feeding decoded body data - a single part can carry any number of headers, and PHP’s in-memory representation of them is much larger than the bytes on the wire
- empty file controls don’t count toward
max_file_uploadsbut still build the usual$_FILESmetadata arrays, and nested field names multiply that work
Before the fix, hitting memory_limit went through zend_mm_safe_error() in Zend/zend_alloc.c, which raised a fatal error and then jumped straight out:
static ZEND_COLD ZEND_NORETURN void zend_mm_safe_error(zend_mm_heap *heap,
const char *format,
size_t limit,
...
size_t size)
{
heap->overflow = 1;
zend_try {
zend_error_noreturn(E_ERROR, format, limit, ... size);
} zend_catch {
} zend_end_try();
heap->overflow = 0;
zend_bailout(); /* longjmp() to the nearest zend_try */
exit(1);
}
zend_bailout() doesn’t care what the caller was halfway through. The allocation that fails doesn’t even need to be related to the state left invalid. It only has to happen while some request structure has been published but not finished.
The SAPI decides whether that state gets read. Apache mod_php always runs request shutdown, even when request startup failed, so the corrupted state is processed before any PHP file is opened. That is why Wiz describes even a “Hello World” page as exploitable. FPM kills the child when startup fails, which closes the startup path, but an OOM after normal startup can still leave invalid state for shutdown. The advisory treats FPM as susceptible without a complete FPM exploit for this CVE. It describes FrankenPHP as likely susceptible in the same way as mod_php, and LSAPI as following the FPM model.
Memory disclosure through PHPSESSID
Wiz’s write-up includes a non-RCE primitive that matters for triage. Session upload-progress handling (session.upload_progress.enabled, on by default) can initialize session state during multipart startup and publish a session ID in PS(id). If OOM aborts the request at that point, the request heap is freed and PS(id) keeps pointing into it. On a later multipart request to the same worker, the upload-progress cleanup path runs before normal session initialization resets the pointer. PHP treats the stale pointer and length as a live zend_string and URL-encodes the reclaimed heap into the PHPSESSID response cookie.
This works even if the application never calls session_start(). The attacker controls how much memory comes back, and Wiz reports that the disclosed region contained data from other requests. That can include passwords, tokens, API keys, and certificates. An oversized or malformed PHPSESSID cookie in a response is one of the more useful detection signals.
CVE-2026-109619: stale globals across requests
The second advisory generalizes the problem as a producer and a consumer:
- a reachable operation stores request-owned state in a worker global (the producer)
- a fatal error skips the matching reset, or a later diagnostic repopulates the global after it was cleared
- PHP frees the request heap while the global still points into it
- the same worker takes another request
- something reads, frees, or branches on that global before normal request initialization replaces it (the consumer)
Fatal interruptions include OOM, parse and compile errors, uncaught errors, a failed require, and Maximum execution time exceeded. The advisory confirms two early consumers.
The first is any INI diagnostic raised before request initialization. With mod_php, .htaccess and Apache php_value, php_flag, and php_admin_* directives are applied before php_request_startup(). A deprecation as ordinary as auto_detect_line_endings=1 is enough. error_reporting, @, display_errors, and log_errors don’t help, because PHP touches the stale error state before it decides whether to report anything. FPM is exposed in the same way when nginx or mod_proxy_fcgi passes PHP_VALUE or PHP_ADMIN_VALUE.
The second is session upload progress, which reaches session and unserializer state before ordinary session initialization.
Wiz reported four bugs to PHP. Besides the PHPSESSID leak, three became RCE chains. Wiz files the first under CVE-2026-109618 and the other two under CVE-2026-109619, but all three follow this producer and consumer pattern:
- Upload-progress unserializer use-after-free. Session decoding during upload progress stores bookkeeping in
BG(unserialize). An OOM leaves that published after the heap is destroyed. Combined with thePHPSESSIDleak to defeat ASLR, this produced pre-auth RCE againstwordpress:latest,drupal:latest, andjoomla:latest. - Fatal-error backtrace use-after-free (PHP 8.5). With
fatal_error_backtracesenabled,EG(last_fatal_error_backtrace)is never reset during shutdown. A diagnostic early in the next request dereferences it. The PHP team calls this the strongest demonstrated case, and Wiz used it for a Drupal RCE. - OPcache type confusion.
zend_accel_script_persist()advances the process-localCG(map_ptr_last)before it publishes the shared frontier. An allocation failure between those two steps leaves persisted objects referring to slots another worker will reuse for a different type. Wiz used this for a WordPress RCE.
What the fix changes
All four branches take the same change, php-src commit 3584a98bda0f (“Avoid state corruption via bailout”, Arnaud Le Blanc with Ilija Tovilo, Jakub Zelenka, Nora Dossche, and Nicolas Grekas). It does two things.
First, it removes longjmp() from the soft OOM path. zend_mm_safe_error() is gone. Exceeding memory_limit now queues a delayed error and sets the VM interrupt flag, so the engine raises the fatal error at the next safe point instead of in the middle of an allocation:
static ZEND_COLD void zend_mm_delayed_verror(const char *format, va_list args)
{
if (ZEND_HAS_OOM()) {
return;
}
...
EG(delayed_oom).message = zend_string_init(buf, strlen(buf), /* persistent */ true);
EG(delayed_oom).filename = script_filename;
EG(delayed_oom).lineno = script_lineno;
EG(delayed_oom).state = ZEND_OOM_STATE_PENDING;
zend_atomic_bool_store_ex(&EG(vm_interrupt), true);
}
So that a delayed error can’t become unbounded allocation, the patch adds a second, higher hard limit. Crossing it skips cleanup entirely and terminates the process:
if (!heap->fallible && add_size > heap->hard_limit - heap->real_size) {
zend_mm_hard_limit_error(heap, add_size ZEND_FILE_LINE_RELAY_CC); /* ... _exit(137); */
}
zend_execute_scripts() checks for a pending OOM after zend_execute(), and zend_deactivate() releases EG(delayed_oom). The commit also adds dozens of OOM regression tests under Zend/tests/, such as oom_array_fill_*, oom_json_decode_*, oom_hard_limit_exceeded.phpt, and oom_interrupt_unfinished_call_frame_fiber.phpt.
Second, it hardens the session upload-progress path. php_session_rfc1867_update() and php_session_rfc1867_cleanup() used to call php_session_initialize() and force the status to active whatever happened. Now they set a guard flag, bail out on failure, and decode objects only as __PHP_Incomplete_Class, so no autoloaders, __wakeup(), or __unserialize() run before RINIT:
- php_session_initialize();
- PS(session_status) = php_session_active;
+ /* Runs before RINIT: no user code may be invoked, see php_session_unserialize_init() */
+ PS(in_rfc1867_update) = true;
+ if (php_session_initialize() == FAILURE || PS(session_status) != php_session_active) {
+ PS(in_rfc1867_update) = false;
+ return;
+ }
static void php_session_unserialize_init(php_unserialize_data_t *var_hash)
{
PHP_VAR_UNSERIALIZE_INIT(*var_hash);
if (PS(in_rfc1867_update)) {
php_var_unserialize_set_incomplete_classes_only(*var_hash, true);
}
}
The RFC 1867 callback also returns early if modules are already active, which covers request_parse_body() calls that could otherwise corrupt a live session. php_rinit_session_globals() now resets PS(in_rfc1867_update), and the old TODO noting that these globals should always be reset at shutdown has been deleted.
Finding exposure
Check the runtime itself and don’t trust the image tag. Distributions often backport without bumping the upstream version, so on distro packages look for the CVE IDs in the changelog:
php -r 'echo PHP_VERSION, PHP_EOL;'
php -i | grep -E '^(Server API|session.upload_progress.enabled|memory_limit|fatal_error_backtraces) '
# Debian / Ubuntu
apt changelog php8.2 2>/dev/null | grep -E 'CVE-2026-10961[89]'
# RHEL / Fedora / Alma / Rocky
rpm -q --changelog php-common 2>/dev/null | grep -E 'CVE-2026-10961[89]'
Inventory PHP base images in Dockerfiles and manifests. Anything below the fixed patch versions, or any floating wordpress, drupal, joomla, or php tag built before 21 October, needs a rebuild:
rg -n '^\s*FROM\s+(\S+/)?(php|wordpress|drupal|joomla)(:|@|\s|$)' --glob 'Dockerfile*' .
rg -n 'image:\s*\S*(php|wordpress|drupal|joomla)' --glob '*.y*ml' .
Aikido notes that the Remi repository (rpms.remirepo.net) would not have updates on release day. If you depend on it, switch to distribution packages or build from source for now.
In logs, the signals named by Wiz, Aikido, and the PHP team include:
- multipart POSTs followed by
Allowed memory size of ... bytes exhaustedor bursts of HTTP 500s - oversized or malformed
PHPSESSIDvalues in response headers - unexplained PHP worker crashes and restarts
- repeated deprecation or warning lines that appear before the target script runs
None of these prove the server is clean. The bugs run below the application, and a failure can happen before PHP writes anything.
If you can’t patch today
Upgrading is the only complete fix. The advisories list partial mitigations that cover different variants, so apply them together, set them administratively (php_admin_value or the main php.ini, not .user.ini), and restart every worker:
; CVE-2026-109618: removes the predictable per-request OOM the confirmed exploit relies on
memory_limit = -1
; CVE-2026-109619: removes the demonstrated upload-progress producer and consumer
session.upload_progress.enabled = 0
; PHP 8.5 only: removes the fatal-error backtrace producer
fatal_error_backtraces = 0
memory_limit=-1 trades the RCE for a denial-of-service risk, so pair it with a frontend cap on decoded body size and a conservative post_max_size. Lower max_input_nesting_level, and bound input variables, multipart parts, and per-part headers. If the application doesn’t take multipart uploads, block multipart/form-data at the edge. Do not use RLIMIT_AS or RLIMIT_DATA as a substitute. They make mmap() fail, and that sends PHP back down the vulnerable longjmp() path. For the stale-state class, the advisory also suggests removing per-request INI overrides that emit diagnostics, and pm.max_requests=1 on FPM pools that can absorb the cost.
Prioritize internet-facing mod_php first, since that is where the application-independent chain was confirmed. Then rotate secrets on any public PHP host that was unpatched while exploitation was active. The PHPSESSID leak can return memory from other users’ requests without ever reaching code execution.
From research to remediation
Check whether this pattern exists in your codebase
Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.
References
- PHP advisory GHSA-w8hw-w9fx-j79w (CVE-2026-109618)
- PHP advisory GHSA-6wmf-4rv5-p3rp (CVE-2026-109619)
- php-src fix commit 3584a98: Avoid state corruption via bailout
- oss-security: CVE-2026-109618 PHP 8.2 to 8.5 RCE via OOM corruption
- NVD: CVE-2026-109618
- NVD: CVE-2026-109619
- Wiz Research: PHP2Shell
- Cloudflare: protections against php2shell
- watchTowr: PHP2Shell FAQ
- Aikido: what you need to know about PHP2Shell
- CISA: Critical vulnerabilities in PHP require immediate patching
- SecurityWeek: PHP2Shell patches land as first exploitation attempts hit sensors
- PHP.net informational advisory: Session upload progress (CVE-2026-115560)
- AWS Security Blog: Amazon Threat Intelligence identifies PHP2Shell exploitation prior to public disclosure in MadPot
- The Hacker News: PHP warns of another unpatched RCE two days after emergency PHP2Shell patches (CVE-2026-115560)
- The Hacker News: PHP2Shell exploit attempts hit millions as attackers try to deploy Mirai malware
- oss-security: Unofficial backports for CVE-2026-109618 / CVE-2026-109619 to EOL PHP 7.3 and 5.4/5.3
- Palo Alto Networks Unit 42 (updated 23 October 2026): Threat brief on CVE-2026-109618 and CVE-2026-109619 and agentic AI post-exploitation
- FBI IC3 Public Service Announcement PSA261023 (23 October 2026): Critical vulnerability alert for PHP core
- Canadian Centre for Cyber Security AL26-026 Update 1 (23 October 2026): CVE-2026-109618, CVE-2026-109619, and CVE-2026-115560
- BleepingComputer (26 October 2026): PHP2Shell security flaws now successfully exploited in the wild
- SecurityWeek (26 October 2026): PHP2Shell exploitation moves from probes to working shells
- CISA (26 October 2026): CISA adds CVE-2026-109618 and CVE-2026-109619 to the Known Exploited Vulnerabilities catalog
- The Hacker News (27 October 2026): CISA confirms active exploitation of two PHP2Shell flaws enabling remote code execution
- Dream Research (26 October 2026): Chained PHP2Shell exploit roots 25 servers, including US government servers
- The Hacker News (28 October 2026): PHP2Shell attack roots 25 servers, breaches US government-affiliated network
- CISA (28 October 2026): CISA adds CVE-2026-46300 (Linux Kernel Fragnesia) to the Known Exploited Vulnerabilities catalog
- Check Point Research (12 November 2026): PHP2Shell exploitation on Check Point Gateway
- Check Point (12 November 2026): Security advisory, active exploitation of PHP2Shell and local privilege escalation (CVE-2026-123039)
- BleepingComputer: Check Point gateways hacked via PHP2Shell to deploy VMware ESXi wiper