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.

Current information: CVE-2026-109618 and CVE-2026-109619
| Item | Status | Source |
|---|---|---|
| CVE IDs | CVE-2026-109618, CVE-2026-109619. PHP hasn’t named them yet | watchTowr |
| Severity | CVSS v4.0 9.2, both. Vector not published | PHP |
| Fixed builds | Pending, due 21 Oct 2026 14:00 UTC | PHP |
| Vendor workarounds | None | PHP |
| Exploited in the wild | None reported. Not in CISA KEV | CISA KEV |
| Public PoC | None | None |
| NVD record | Not published | NVD API |
PHP has said four things (advance notice, php.announce). Everything else can be treated as a speculation and is a leak, a relay of a leak, or a guess.
- There are two critical vulnerabilities.
- Both score CVSS v4.0 9.2.
- The fix ships Wednesday, 21 October 2026 at 14:00 UTC.
- PHP may release earlier if details go public or exploitation shows up.
Accordingly, this is the current status:
CVE-2026-109618
- CVSS v4.0 9.2 (PHP)
- Reported as RCE (watchTowr, unconfirmed)
- Type / CWE: pending
- Precondition: pending
- Affected versions: pending
- Exploited in the wild: none reported
- CISA KEV: not listed
Patch on release
No fix, no workaround. Every current build is unpatched.
Fix due 21 Oct 14:00 UTC
CVE-2026-109619
- CVSS v4.0 9.2 (PHP)
- Reported as RCE (watchTowr, unconfirmed)
- Type / CWE: pending
- Precondition: pending
- Affected versions: pending
- Exploited in the wild: none reported
- CISA KEV: not listed
Patch on release
No fix, no workaround. Every current build is unpatched.
Fix due 21 Oct 14:00 UTC
Distros backport the fix into their own package versions and rate severity themselves, so their rating can differ from PHP’s 9.2. Use these trackers, not upstream version numbers, to decide whether a host is patched. As of 20 Oct, none of these trackers had a page for CVE-2026-109618 that I could find.
| Vendor | Severity | Fixed Versions | Advisory | Tracker |
|---|---|---|---|---|
| Ubuntu | Pending | Pending | Pending | CVE-2026-109618 |
| Debian | Pending | Pending | Pending | CVE-2026-109618 |
| Red Hat (RHEL) | Pending | Pending | Pending | CVE-2026-109618 |
| SUSE | Pending | Pending | Pending | CVE-2026-109618 |
| Alpine | Pending | Pending | Pending | Alpine secdb |
| Amazon Linux | Pending | Pending | Pending | ALAS |
| Docker official php images | n/a | Pending | n/a | Docker Hub 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.
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.
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
Leave a Reply
You must be logged in to post a comment.