PHP announced fixes for two critical bugs on Wednesday, 21 October 2026 at 14:00 UTC. Why are these important? It’s because the vulnerabilities were reportedly leaked before their planned disclosure. I’ll update this post as more information is made available and PHP and the distros publish. Until then, treat this post as a speculative and preventive information. All information is now populated and we have lost enough sleep over these. Time for the next vulnerability.

Current information: CVE-2026-109618 and CVE-2026-109619
| Item | Status | Source |
|---|---|---|
| CVE-2026-109618 | RCE via out-of-memory corruption (GHSA-w8hw-w9fx-j79w) | PHP |
| CVE-2026-109619 | RCE via stale global state (GHSA-6wmf-4rv5-p3rp) | PHP |
| Related: CVE-2026-115560 | An unlikely RCE path through session upload progress, only when an app switches session.serialize_handler at runtime. Fix due in PHP’s next regular release; until then PHP suggests session.upload_progress.enabled=0 | PHP, 23 Oct |
| Severity | CVSS v4.0 9.2 for both: AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N | GHSAs |
| Fixed builds | 8.5.12, 8.4.27, 8.3.36, 8.2.35, and 8.6.0 RC4. Source-only backports for 7.4, 8.0 and 8.1 | PHP |
| Windows builds | Out for all four: 8.5.12, 8.4.27, 8.3.36 and 8.2.35 | PHP downloads |
| Docker official images | Updated to 8.2.35, 8.3.36, 8.4.27 and 8.5.12 on 21 Oct. Rebuild anything built on them | docker-library/php |
| Vendor workaround | Partial only. Updating is the only complete fix | PHP |
| Exploitation | Confirmed: CISA’s KEV listing on 26 Oct. Amazon saw one attempt against its honeypots on 17 Oct, four days before disclosure. On 24 Oct, researcher Adel Karimi recorded 35 successful shell executions on three honeypots running PHP 8.4.26 under Apache mod_php, from two IPs; the traffic matched CVE-2026-109618 only, and he warns his single-worker setup favoured the attacker. Delft University’s threat lab counted more than 50,000 exploit requests from over 600 IPs, 171 of which sent exploit code or droppers. Sophos and Palo Alto Networks walked back earlier compromise claims. The honeypot, Delft, Sophos and Palo Alto details come from The Register’s 27 Oct report | Amazon, watchTowr, Beazley |
| CISA | Both CVEs are in KEV, added 26 Oct 2026, due 29 Oct 2026 for US federal civilian agencies, per KEV catalog version 2026.10.27 (released 27 Oct, 19:00 UTC). KEV names them “PHP Memory Corruption Vulnerability” (CVE-2026-109618) and “PHP Stale Internal Request-State Vulnerability” (CVE-2026-109619). Both carry CISA’s forensic triage flag, so agencies must check for compromise, not just patch, under BOD 26-04. Known ransomware use: unknown. CISA hasn’t said who was hit | CISA alert, Tenable |
| NVD | CVE-2026-109618: CVSS 3.1 9.8 with low attack complexity (AC:L), against PHP’s high. CVE-2026-109619: 8.1 (AC:H), matching PHP. No NVD CVSS 4.0 score yet; NVD shows PHP’s 9.2 | Vendor pages |
| Distro packages | Fixes out at Ubuntu, Debian, SUSE, Amazon Linux and Red Hat (RHEL 7 ELS to 10, with gaps), not yet for every release. Amazon won’t fix two packages. Last checked 22 and 23 Oct | Vendor trackers, below |
| Date (UTC) | Update |
|---|---|
| 27 Oct 2026, evening | Confirmed the KEV entries against catalog 2026.10.27: added 26 Oct, due 29 Oct, forensic triage required, ransomware use unknown. Added the triage step to “What to do now” and flagged KEV’s identical CWE lists for the two CVEs. Added the first reports of successful exploitation (honeypots) and the Sophos and Palo Alto retractions. KEV details not yet checked against the KEV JSON. |
| 25 Oct 2026, 18:15 | Alpine is fixed: php83 8.3.36-r0, php84 8.4.27-r0 and php85 8.5.12-r0 on 3.23 and 3.24, per secdb. Debian forky is now fixed (8.4.27-1). Added Ubuntu Pro package versions for 14.04 to 20.04 from Ubuntu’s OSV data. |
| 25 Oct 2026, 17:30 | Added NVD’s scores (9.8 for CVE-2026-109618, 8.1 for CVE-2026-109619) and CISA’s SSVC entry. Confirmed neither CVE is in KEV catalog version 2026.10.23. Filled in Red Hat from its VEX data: 18 advisories from RHEL 7 ELS to 10, RHEL 9’s php:8.4 stream not fixed yet, no fix planned for RHEL 6. Amazon’s two CVE pages now agree. |
| 25 Oct 2026, 16:45 | Final sweep. Windows builds are out for all four fixed versions. Docker official images and PHP 8.6.0 RC4 carry the fix. PHP’s 23 Oct informational advisory adds CVE-2026-115560, another reason to keep session upload progress off. Amazon reported an attempt on its honeypots four days before disclosure; added to exploitation status and evidence guidance. CISA issued an alert on 21 Oct. Vendor trackers not rechecked since 23 Oct. |
| 23 Oct 2026, 14:30 | Red Hat shipped fixes for RHEL 10 (RHSA-2026:87070), RHEL 9 (RHSA-2026:87079 and 87072) and RHEL 8’s php:7.4 stream (RHSA-2026:87080), and published bulletin RHSB-2026-013. Added NCSC-NL’s affected-software list to the enterprise section. Debian, SUSE, Alpine, Amazon’s CVE-2026-109618 page, NVD and CISA KEV weren’t rechecked today. |
| 22 Oct 2026, 14:30 | SUSE published SUSE-SU-2026:4843-1 and SUSE-SU-2026:4844-1, with fixes for SLE 15 SP4 to SP7, php7 and Tumbleweed. Amazon Linux 2023’s php8.3 and php8.4 are fixed. Debian sid is fixed. Ubuntu’s two CVE pages now agree. Still no Red Hat advisory; NVD, CISA KEV and Alpine still unchecked. |
| 22 Oct 2026, 04:20 | Distro fixes out at Ubuntu, Debian, SUSE and Amazon Linux; no Red Hat advisory yet; Amazon won’t fix Amazon Linux 2’s php or Amazon Linux 2023’s php8.1. watchTowr reports exploitation attempts, without published evidence. Cloudflare added emergency WAF rules. Windows build for 8.3.36 is out. Flagged scoring and tracker conflicts under “Source problems”. |
| 21 Oct 2026, 14:30 | PHP released 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Filled in both CVEs from PHP’s advisories. The leaked write-up was wrong about both bugs: CVE-2026-109618 is an out-of-memory bug, and session upload progress is only one confirmed path for CVE-2026-109619. Relabelled the interim mitigation, added PHP’s own workarounds, and removed .user.ini, which PHP says not to rely on. Ubuntu has rated both Critical. At 18:03 UTC, added a Windows section: PHP had published a fixed Windows build only for 8.5. Added Suricata and Snort rules for the confirmed CVE-2026-109619 path. |
| 21 Oct 2026 | Added an unconfirmed interim mitigation: turning off session upload progress, plus a WAF rule for hosts you can’t reconfigure. Added rules to block uploads on routes that don’t take them. Checked vendor trackers: no advisories yet. |
| 20 Oct 2026 | First version. PHP’s advance notice is out. No CVE details, affected versions or fixed builds yet. |
What PHP confirmed, and what the leaks got wrong
PHP’s advisory describes two separate bugs. Both vulnerabilities are about request memory that PHP uses while it’s in an invalid state, and both start before any application code runs.
- CVE-2026-109618, RCE via OOM corruption. PHP exits an out-of-memory error by jumping out of the current code. If that happens while request data is only partly set up, request cleanup can then treat the half-built data as valid. Multipart request parsing is one of the ways an attacker can push PHP into that out-of-memory state.
- CVE-2026-109619, RCE via stale global state. Some request-owned state can outlive the memory behind it inside a persistent worker. A later request on the same worker can then use it before normal setup resets it. Exploiting it needs one request that leaves the state behind, typically through a fatal error, an out-of-memory error or a timeout, and a later request on the same worker that reads it. Session upload progress is one confirmed path, and on PHP 8.5 so is
fatal_error_backtraces.
PHP rates mod_php highest risk and says it validated an exploit there. It lists PHP-FPM, php-cgi, FrankenPHP and LiteSpeed as at risk too. This is a table of what we had earlier:
| Claim | Who | Current Status |
|---|---|---|
| Two unauthenticated RCE bugs | watchTowr, leak posts | Correct |
| In PHP core | Leak post on X | Correct. Both fixes are in Core |
| CVSS 10.0 | Leak posts | Wrong. 9.2 for both |
| Disclosure on 22 Oct | watchTowr FAQ, leak posts | Wrong. Released 21 Oct |
| Every version from PHP 5 onward | watchTowr, leak posts | Not stated by PHP. It shipped fixes for 8.2 to 8.5 and source-only backports for 7.4, 8.0 and 8.1. Ubuntu lists its PHP 5 and 7 packages as “Needs evaluation” |
| Only 8.2 and later get fixes | Mallory | Correct for official releases |
| Leaked write-up: both bugs are use-after-frees in session upload progress | Unverified write-up | Wrong. CVE-2026-109618 is an out-of-memory bug. Upload progress is one confirmed path for CVE-2026-109619, not the bug itself. Its CVE numbering doesn’t match PHP’s either |
Accordingly, this is the current status:
CVE-2026-109618: RCE via OOM corruption
- CVSS v4.0 9.2, network, no authentication or user interaction
- Remote code execution as the web worker user
- Validated against mod_php; FPM and FrankenPHP susceptible per PHP; LiteSpeed likely
- Trigger: an out-of-memory error while request data is half set up, reachable through multipart parsing
- CWE-416, CWE-755, CWE-824, CWE-1325, CWE-1419
- Partial workaround: memory_limit=-1 set administratively, and a low post_max_size
Update now
Fixed in 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Update; the workarounds only reduce exposure.
Fixed 21 Oct 2026
CVE-2026-109619: RCE via stale global state
- CVSS v4.0 9.2, network, no authentication or user interaction
- Use-after-free, memory disclosure and remote code execution as the web worker user
- Needs two requests on the same worker: one that leaves stale state behind, one that reads it
- Confirmed paths: session upload progress (on by default) and fatal_error_backtraces on PHP 8.5
- CWE-416, CWE-755, CWE-824, CWE-1325, CWE-1419
- Partial workaround: session.upload_progress.enabled=0 set administratively, then restart workers
Update now
Fixed in 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Update; the workarounds only reduce exposure.
Fixed 21 Oct 2026
Distro and vendor patches. PHP says distros and LTS vendors got the patches in advance. Distros backport the fix into their own package versions and rate severity themselves, so use their trackers, not upstream version numbers, to decide whether a host is patched. PHP fixed both CVEs in the same release, so a fixed package fixes both, even where a vendor’s page for one CVE lags the other.
| Vendor | Their severity | Fixed packages | Not fixed yet | Advisory |
|---|---|---|---|---|
| Ubuntu | Critical. No CVSS published | php8.1 8.1.2-1ubuntu2.28 (22.04), php8.3 8.3.6-0ubuntu0.24.04.13 (24.04), php8.5 8.5.4-0ubuntu1.5 (26.04), php8.5 8.5.9-0ubuntu2.1 (26.10). Ubuntu Pro: php7.4 7.4.3-4ubuntu2.29+esm5 (20.04), php7.2 7.2.24-0ubuntu0.18.04.17+esm14 (18.04), and with the Legacy add-on php7.0 7.0.33-0ubuntu0.16.04.16+esm22 (16.04) and php5 5.5.9+dfsg-1ubuntu4.29+esm18 (14.04) | None listed | USN-8948-1, USN-8948-2 |
| Debian | No urgency set | php8.2 8.2.35-1~deb12u1 (bookworm-security, DLA-4848-1), php8.4 8.4.27-1~deb13u1 (trixie-security, DSA-6574-1), php8.4 8.4.27-1 (forky and sid) | None. The fix is only in the security archive: bookworm and trixie’s base archives still carry php8.2 8.2.32 and php8.4 8.4.24, so check that the security repo is enabled | DSA-6574-1, DLA-4848-1 |
| Red Hat | Important; Moderate for the OpenShift Dev Spaces images. CVSS 3.1 8.1 (AC:H) | RHEL 10: RHSA-2026:87070, 87071; 10.0 EUS: 88561. RHEL 9: default php 8.0 (87151), php:8.2 (87072), php:8.3 (87079); 9.2 to 9.6 EUS and E4S (88659, 88725, 88835, 88720, 88834). RHEL 8: php:7.4 (87080), php:8.2 (87073); 8.4 to 8.8 AUS, EUS and E4S (88565, 88533, 88532). RHEL 7 ELS: CVE-2026-109619 only (88693); its PHP 5.4 isn’t affected by CVE-2026-109618. Red Hat Hardened Images (87414) | RHEL 9’s php:8.4 stream and the OpenShift Dev Spaces udi-rhel9 image: affected, no fix yet. No fix planned: RHEL 6 (out of support) | RHSB-2026-013, RHSA-2026:87070, 87079, 87072, 87080. Full list from Red Hat’s VEX data, 24 Oct |
| SUSE | Important. CVSS 3.1 8.1, CVSS 4.0 9.2 | SLE 15 SP7: php8 8.3.36-150700.3.24.1. SLES 15 SP6 LTSS and SAP 15 SP6: php8 8.2.35-150600.3.39.1. SLE 15 SP4 and SP5 LTSS, SAP and HPC: php8 8.0.30-150400.4.80.1. php7 7.4.33-150400.4.81.1 on SP4 to SP7. Tumbleweed: 8.5.12-1.1 | SLES 16, openSUSE Leap and the bci/php container images: not on the 22 Oct pages (shown as affected on 21 Oct) | SUSE-SU-2026:4843-1, SUSE-SU-2026:4844-1, TID-PHP-RCE-202610 |
| Amazon Linux | Important. CVSS 3.1 8.1 | Amazon Linux 2023: php8.2, php8.3, php8.4, php8.5. Amazon Linux 2 PHP 8.2 extra. Amazon Linux 2027 Preview: php8.5. Both CVE pages agree | No fix planned: php on Amazon Linux 2 core, php8.1 on Amazon Linux 2023 | ALAS2023-2026-3301 to 3304, ALAS2PHP8.2-2026-015, ALAS2027-2026-243 |
| Alpine | None published; secdb doesn’t rate severity | php83 8.3.36-r0, php84 8.4.27-r0 and php85 8.5.12-r0 in the community repo of 3.23 and 3.24. Neither branch ships php82 | Not checked: 3.22 and older branches, and edge | Alpine secdb 3.24 community, 3.23 community |
| Docker official php images | n/a | 8.2.35, 8.3.36, 8.4.27 and 8.5.12, per docker-library/php on 21 Oct | Rebuild on the new base image and check php -v | docker-library/php |
Each tracker above links to CVE-2026-109618. Check the matching page for CVE-2026-109619 too; vendors sometimes publish the two on different days.
Windows
As of 25 Oct 2026, PHP’s Windows downloads have fixed builds for all four branches: 8.5.12, 8.4.27, 8.3.36 and 8.2.35. The 8.4 and 8.2 builds came out a few days after the release, so a Windows server you checked on 21 or 22 Oct may still need updating.
Find every PHP. Windows PHP often ships inside bundles (XAMPP, WampServer, Laragon) or vendor apps that Windows Update never touches. Additionally:
- On 8.2 to 8.4, your options today are moving to 8.5.12 if your app supports it, building from source, or the workarounds below.
- IIS uses the NTS build and Apache with mod_php uses the TS build, per PHP’s downloads page. Use x64.
- Stop the service before replacing files, or Windows keeps them locked: stop the IIS app pool or run
iisreset /stop, or stop the Apache service. Unzip the new build over the PHP folder, keep yourphp.ini, then start it again. A fresh start also clears the worker state CVE-2026-109619 depends on. - Bundles and package managers such as XAMPP, Chocolatey, Scoop and winget can lag behind PHP. Check the version they actually install, or replace the bundle’s PHP folder yourself.
CVE-2026-109618 and CVE-2026-109619: Response
Unconfirmed mitigation: I came across information that these bugs are related to PHP’s session upload progress feature. There is no official confirmation yet, so this may not help. It’s still worth checking as the feature is on by default, few apps use it, and turning it off costs almost nothing.
The setting is session.upload_progress.enabled. It is on by default, and the feature runs on any PHP endpoint that receives a multipart upload, even one that never starts a session. I confirmed that on PHP 8.3.6. What breaks if turned off is the upload progress bars built on this feature. PHP reads uploads before your code runs, so ini_set() can’t change this setting (it returns false). Set it in one of these places:
| PHP | Setting | Tested |
|---|---|---|
| php-fpm, server-wide | session.upload_progress.enabled = Off in the FPM php.ini (for example /etc/php/8.3/fpm/php.ini), then restart php-fpm | Yes |
| php-fpm, one pool | php_admin_flag[session.upload_progress.enabled] = off in the pool file, then restart php-fpm | Yes |
| Shared hosting (FPM or CGI) | session.upload_progress.enabled = Off in a .user.ini in the site root. PHP caches .user.ini for up to user_ini.cache_ttl seconds (300 by default) | Yes |
| Apache mod_php | php_admin_flag session.upload_progress.enabled off in the vhost | No |
| Official Docker php images | Add session.upload_progress.enabled = Off to a file in /usr/local/etc/php/conf.d/ and rebuild | No |
Check what’s set across your PHP config:
grep -rnE 'session\.upload_progress\.(enabled|name)' /etc/php/ /etc/php.ini /etc/php.d/ /etc/php-fpm.d/ /usr/local/etc/php/ 2>/dev/null
If this one liner does not report anything, it means that the feature is on. This ModSecurity rule rejects any request body that contains the upload progress field:
SecRule ARGS_POST_NAMES "@streq PHP_SESSION_UPLOAD_PROGRESS" \
"id:1026109618,phase:2,deny,status:403,log,t:none,msg:'PHP session upload progress field (interim, PHP CVE-2026-109618/109619)'"
This rule needs SecRequestBodyAccess On. PHP_SESSION_UPLOAD_PROGRESS is the default field name; if your session.upload_progress.name is different, use that. I tested it with ModSecurity 2 on Apache 2.4: the upload with the field got a 403 and PHP wrote nothing, while normal uploads and page loads got a 200. A WAF parses multipart bodies differently from PHP, so treat this as a layer, not a fix.
Proof-of-concept code is now public. Every PoC request I reviewed sends a PHP_SESSION_UPLOAD_PROGRESS form field, so the measures above block it. Turning this feature off means the vulnerable code never runs, and the WAF rule rejects the request before it reaches PHP. A variant analysis that triggers the bug another way is possible, so make sure to patch when the fix ships.
Block uploads on routes that don’t need them
This is a second layer, not a substitute for turning the setting off. PHP parses an upload before your code runs, so a route you leave open is still exposed, even behind a login. A rule like this cuts exposure from every PHP file down to a few upload endpoints, which helps most where you can’t change PHP’s config. Multipart isn’t only used for file uploads. Some forms post it with no file at all, so build the allowlist from your own traffic. Log a day of requests with their content type (nginx):
log_format ctype '$time_iso8601 $request_method $uri $status "$content_type"';
access_log /var/log/nginx/ctype.log ctype;
awk 'tolower($0) ~ /"multipart\/form-data/ {print $3}' /var/log/nginx/ctype.log | sort | uniq -c | sort -rn
Then block multipart everywhere except those routes. The WordPress paths below are examples; replace them with your log output. Each rule matches case-insensitively, because PHP accepts multipart/form-data in any letter case, followed by a space, comma or semicolon. nginx (the two map lines go in http {}, the rest in server {}):
map $http_content_type $is_multipart { default 0; "~*^\s*multipart/form-data" 1; }
map $uri $upload_route { default 0; "~^/wp-admin/async-upload\.php$" 1; "~^/wp-admin/media-new\.php$" 1; }
set $mp_block "$is_multipart$upload_route";
if ($mp_block = "10") { return 415; }
Apache 2.4. Use %{HTTP:Content-Type}. Plain %{CONTENT_TYPE} is the response’s content type, and a rule built on it silently matches nothing.
<Location "/">
<If "%{HTTP:Content-Type} =~ m#^\s*multipart/form-data#i && %{REQUEST_URI} !~ m#^/wp-admin/(async-upload|media-new)\.php$#">
Require all denied
</If>
</Location>
ModSecurity 2:
SecRule REQUEST_HEADERS:Content-Type "@rx (?i)^\s*multipart/form-data" \
"id:1026109620,phase:1,deny,status:415,log,t:none,msg:'multipart upload to a route that does not take uploads',chain"
SecRule REQUEST_FILENAME "!@rx ^/wp-admin/(?:async-upload|media-new)\.php$" "t:none,t:normalizePath"
Tested on nginx 1.24 with php-fpm 8.3, and on Apache 2.4.58 with and without ModSecurity 2. Multipart requests to unlisted PHP files were refused in every letter case and separator variant, and PHP’s upload progress handler never ran. Requests to the allowlisted route and normal form posts went through.
I also tested which spellings PHP actually acts on, then built the Suricata and Snort rules to match exactly that set. PHP runs upload progress when the field name is in double quotes, single quotes, unquoted, or written NAME=. It ignores a lowercase field name and spaces around =. It also ignores URL-encoded forms.
Suricata 7:
alert http any any -> $HTTP_SERVERS any (msg:"PHP session upload progress field in multipart POST (CVE-2026-109619 confirmed path)"; flow:established,to_server; http.method; content:"POST"; http.content_type; content:"multipart/form-data"; nocase; startswith; http.request_body; content:"PHP_SESSION_UPLOAD_PROGRESS"; fast_pattern; pcre:"/[\s\x3b](?i:name)=[\x22\x27]?PHP_SESSION_UPLOAD_PROGRESS(?:[\x22\x27\x3b\s]|$)/m"; classtype:attempted-admin; reference:url,www.php.net/archive/2026.php#2026-10-21-5; sid:1026109619; rev:1;)
Snort 2.9:
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"PHP session upload progress field in multipart POST (CVE-2026-109619 confirmed path)"; flow:to_server,established; content:"POST"; http_method; content:"multipart/form-data"; nocase; http_header; content:"PHP_SESSION_UPLOAD_PROGRESS"; http_client_body; fast_pattern; pcre:"/[\s\x3b](?i:name)=[\x22\x27]?PHP_SESSION_UPLOAD_PROGRESS(?:[\x22\x27\x3b\s]|$)/Pm"; classtype:attempted-admin; reference:url,www.php.net/archive/2026.php#2026-10-21-5; sid:1026109619; rev:1;)
Snort 3 (same logic, not tested):
alert http (msg:"PHP session upload progress field in multipart POST (CVE-2026-109619 confirmed path)"; flow:established,to_server; http_method; content:"POST"; http_header:field content-type; content:"multipart/form-data",nocase; http_client_body; content:"PHP_SESSION_UPLOAD_PROGRESS",fast_pattern; pcre:"/[\s\x3b](?i:name)=[\x22\x27]?PHP_SESSION_UPLOAD_PROGRESS(?:[\x22\x27\x3b\s]|$)/m"; classtype:attempted-admin; reference:url,www.php.net/archive/2026.php#2026-10-21-5; sid:1026109619; rev:1;)
Discover PHP unknown versions:
On your Linux hosts, run this:
# PHP binaries bundled outside the usual paths
find /opt /usr/local /srv -xdev -type f \( -name 'php' -o -name 'php-fpm*' -o -name 'php-cgi*' \) -perm -u+x 2>/dev/null | while read -r b; do printf '%s: %s\n' "$b" "$("$b" -v 2>/dev/null | head -1)"; done
find /opt /usr/local /srv -xdev -type f -name 'libphp*.so' 2>/dev/null
# Local container images built on the official php images (they set PHP_VERSION)
docker images -q | sort -u | xargs -r docker image inspect --format '{{.RepoTags}} {{.Config.Env}}' | grep PHP_VERSION
# From outside: hosts that announce PHP and its version
curl -sI https://example.com | grep -i '^x-powered-by'
On Microsoft Windows:
Get-ChildItem 'C:\Program Files','C:\Program Files (x86)' -Recurse -Filter php.exe -ErrorAction SilentlyContinue.
Until a patched version is available the following php.ini settings can help prevent. These settings will not stop the vulnerabilities, but limit exposure. Check them in the ini file FPM loads, such as /etc/php/8.3/fpm/php.ini.
| Setting | Change | Reasoning | Operational Impact |
|---|---|---|---|
expose_php | Off (default is On) | Stops the X-Powered-By: PHP/x.y.z header that tells scanners which build you run | None |
| Loaded extensions | Disable what the app doesn’t use (php -m lists them) | If either bug is in an extension, an unloaded extension can’t be reached. Whether that applies here is unknown until the advisory | Test your application |
disable_functions | exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec | Stops a webshell from running commands | Breaks applications that call these. Memory-corruption exploits have bypassed this before, so it only slows an attacker down |
allow_url_include | Off (by default) | Blocks including code from a URL | None |
CVE-2026-109618 and CVE-2026-109619: Detection
SigmaHQ already has a similar rule, Linux Webshell Indicators (818f7b24). I created https://github.com/mayur-esh/sigma-rules/blob/main/generic/shell-spawned-by-php-worker.yml. This one covers a different case, and you should want to keep both:
- Parent process: SigmaHQ’s rule watches
httpd,apache2,nginx,lighttpd,node,caddyand Java app servers. It doesn’t includephp-fpm,php-cgior FrankenPHP. On an nginx + php-fpm server, the process that spawns commands is php-fpm, so that rule never sees them. - Child process: SigmaHQ’s rule looks for recon tools such as
whoami,unameandcat. PHP’sexec(),system(),shell_exec(),passthru()andpopen()run commands through/bin/sh -c, so the direct child of PHP issh, andwhoamishows up as a grandchild. This rule alerts on theshitself, pluscurl,wgetandnc.
The two child lists don’t overlap, so running both won’t double-alert.
auditd: SigmaHQ already has a rule for this, Webshell Remote Command Execution (c0d3734d), so use its audit config as is. It logs every program the web user starts, for 64-bit and 32-bit calls, and matches on euid, which also catches code that switched to the web user. UID 33 is www-data on Debian and Ubuntu; change it for your distro. All four lines loaded into a live kernel with auditctl -R.
-a always,exit -F arch=b32 -S execve -F euid=33 -k detect_execve_www
-a always,exit -F arch=b64 -S execve -F euid=33 -k detect_execve_www
-a always,exit -F arch=b32 -S execveat -F euid=33 -k detect_execve_www
-a always,exit -F arch=b64 -S execveat -F euid=33 -k detect_execve_www