Security advisory · Disclosed October 21, 2026
PHP remote code executionphp2shell check
CVE-2026-109618 and CVE-2026-109619 let attackers run code on PHP web servers without authentication. Check your site's exposure by address and find the fix for your environment.
- CVSS v4.0
- 9.2 Critical(act now)
- Attack
- Remote · No authentication
Based on information public as of October 22, 2026
Result
Confirm on the server
Response headers show only part of the picture. To check every PHP install and its settings, run the check script on the server. The script only reads. It does not change settings or connect to the network.
- Run as root.
- Set
WEB_ROOTto your actual web root path.- Example:
/var/www/html
- Example:
- The command below displays the results in English.
WEB_ROOT=/var/www/html
sudo bash php2shell-check.sh --lang en "$WEB_ROOT"- For containers, run the script inside the container. If bash is missing (for example on Alpine), install the bash package (apk add bash), or skip installing and paste the php -v output into the box below.
docker cp php2shell-check.sh <container>:/tmp/
docker exec -it <container> bash /tmp/php2shell-check.sh --lang en- Run in PowerShell as Administrator. IIS site paths are checked automatically.
- The command below displays the results in English.
powershell -NoProfile -ExecutionPolicy Bypass -File .\php2shell-check.ps1 -Lang en- Run with
sudofrom the account that installed PHP. PHP installed in that account, such as Homebrew or MAMP, is run with that account's rights, not as root. - Set
WEB_ROOTto your actual web root path.- Built-in Apache:
/Library/WebServer/Documents - Homebrew:
$(brew --prefix)/var/www - MAMP:
/Applications/MAMP/htdocs
- Built-in Apache:
- The command below displays the results in English.
WEB_ROOT=/Library/WebServer/Documents
sudo bash php2shell-check.sh --lang en "$WEB_ROOT"- PHP installed with Homebrew, MacPorts, MAMP, XAMPP or Laravel Herd is found automatically. If a PHP is missing from the output, give its path with
--php.
sudo bash php2shell-check.sh --lang en --php /path/to/phpReading the script output
- Vulnerable
- Lower than the fixed version. Update required.
- End of life
- The branch is no longer supported. Move to a newer version.
- Needs review
- Could not be judged automatically. Read the reason shown and check manually.
- Patched
- At or above the fixed version.
Exit codes: 0 all patched, 1 vulnerable, needs review or settings unreadable, 2 no PHP found, 3 end of life only
Affected and fixed versions
PHP in the ranges below must be updated. Any PHP that handles web requests is affected (PHP-FPM, Apache mod_php, CGI/FastCGI).
| PHP version | Vulnerable versions | Fixed versions |
|---|---|---|
| PHP 8.5 | Below 8.5.12 | 8.5.12 or later |
| PHP 8.4 | Below 8.4.27 | 8.4.27 or later |
| PHP 8.3 | Below 8.3.36 | 8.3.36 or later |
| PHP 8.2 | Below 8.2.35 | 8.2.35 or later |
| PHP 8.1 and older | All | No fixed release (end of life) |
- PHP 8.6.0 includes the fix from RC4. It is a development release, so do not use it in production.
- PHP 8.2 reaches end of life on December 31, 2026. Even if you update to 8.2.35, plan a move to a newer branch.
Distribution backports
- Ubuntu, Debian and RHEL-based distributions, cPanel and managed hosting sometimes apply security fixes without changing the version number (backports). For example, PHP on Ubuntu 24.04 shows
8.3.6but may already include the fix. - The version number in the table above can therefore mislead. Confirm with the package revision and the distribution security notice (Ubuntu USN, Debian DSA, Red Hat RHSA).
- Commands that search the package changelog for the CVE numbers are in the "1. Check" tab of the response guide below. The server check script also shows a note when it finds signs of a backport.
Response guide
Pick a stage and your server environment to see only the commands and settings you need.
Response headers show only part of the picture. Servers that hide their version, internal servers and servers with several PHP installs must be checked on the server itself.
Cloudbric server check script
Finds every PHP install (PHP-FPM, CGI, CLI, Apache mod_php) and checks its version, related settings, overrides, and processes not restarted after an update. Read-only, no network connections. Run a site check to download it from the result page.
Check with commands
Examples use PHP 8.3. Adjust version numbers to your server. The CLI and the web (PHP-FPM, mod_php) can use different settings.
php -v
php-fpm8.3 -v # FPM binary name differs per version
dpkg -l | grep -E '^ii\s+(php|libapache2-mod-php)'
apt changelog php8.3-common | grep -iE 'CVE-2026-10961[89]'Examples use PHP 8.3. Adjust version numbers to your server. The CLI and the web (PHP-FPM, mod_php) can use different settings.
php -v
php-fpm -v
rpm -qa 'php*'
rpm -q --changelog php-common | grep -iE 'CVE-2026-10961[89]'Examples use PHP 8.3. Adjust version numbers to your server. The CLI and the web (PHP-FPM, mod_php) can use different settings.
/opt/cpanel/ea-php83/root/usr/bin/php -v
rpm -qa 'ea-php*' | sortExamples use PHP 8.3. Adjust version numbers to your server. The CLI and the web (PHP-FPM, mod_php) can use different settings.
docker exec <container> php -v
docker image ls --format '{{.Repository}}:{{.Tag}}' | grep phpExamples use PHP 8.3. Adjust version numbers to your server. The CLI and the web (PHP-FPM, mod_php) can use different settings.
php -v
php --ini
Get-ChildItem C:\ -Recurse -Filter php-cgi.exe -ErrorAction SilentlyContinue | Select-Object FullNameExamples use PHP 8.3. Adjust version numbers to your server. The CLI and the web (PHP-FPM, mod_php) can use different settings.
php -v
"$(brew --prefix php)/sbin/php-fpm" -v # versioned formula: brew --prefix php@8.3
brew list --versions | grep -E "^php(@| )"
brew services list | grep phpOn shared hosting or managed services you often cannot change PHP or its settings yourself. Ask your provider whether the fixed versions (8.5.12, 8.4.27, 8.3.36, 8.2.35 or later, or backports) are applied and when.
Done when
Ticks are not saved.
Updating is the only complete fix. It takes effect only after every worker is restarted.
Back up configuration files first (or take a snapshot), and apply on a test server first if you can. Restart when traffic is low.
sudo apt update
sudo apt install --only-upgrade 'php*'
# restart every worker (required)
sudo systemctl restart apache2 # mod_php
sudo systemctl restart php8.3-fpm # each FPM version in use
php -vsudo dnf upgrade 'php*'
# Remi multi-version: sudo dnf upgrade 'php83-php*'
# restart every worker (required)
sudo systemctl restart php-fpm httpd
php -v# WHM > Software > EasyApache 4, or:
sudo dnf upgrade 'ea-php*' # CentOS 7: yum
# restart PHP-FPM and Apache (required)
sudo /scripts/restartsrv_apache_php_fpm
sudo /scripts/restartsrv_httpd# Dockerfile: use a fixed tag at or above the fixed version
# FROM php:8.3.36-fpm
docker build --pull -t my-app:patched .
docker compose up -d --force-recreate
docker exec <container> php -v# 1. Download the fixed build from https://windows.php.net/download/
# (same thread safety and architecture as the current one)
# 2. Replace the PHP folder, keep your php.ini
iisreset # IIS
Restart-Service Apache2.4 # Apache (XAMPP: restart from the control panel)
php -vbrew update
brew upgrade php # versioned formula: brew upgrade php@8.3
# restart every worker (required)
brew services restart php # php@8.3: brew services restart php@8.3
brew services restart httpd # Homebrew Apache (built-in Apache: sudo apachectl -k restart)
php -v
# MAMP / XAMPP: update the app, then restart its serversOn shared hosting or managed services you often cannot change PHP or its settings yourself. Ask your provider whether the fixed versions (8.5.12, 8.4.27, 8.3.36, 8.2.35 or later, or backports) are applied and when.
PHP 8.1 and older have no official fix. Use backported packages from your distribution or LTS vendor, or move to 8.2 or later. When the branch changes, check framework, CMS plugin and extension compatibility first.
Done when
Ticks are not saved.
A partial measure that reduces exposure until you update. The official PHP announcement says these settings alone do not make an installation safe.
PHP settings file
; /etc/php/8.3/fpm/conf.d/99-php2shell.ini
; /etc/php/8.3/apache2/conf.d/99-php2shell.ini
; CVE-2026-109618: pin it so applications cannot override it
memory_limit = -1
post_max_size = 8M ; 32M if file uploads are required
; keep upload_max_filesize below post_max_size
; CVE-2026-109619
session.upload_progress.enabled = 0
; PHP 8.5 only
fatal_error_backtraces = 0PHP-FPM pool settings (pinned values)
Pool files must use the php_admin_value/php_admin_flag form. Plain php.ini lines stop PHP-FPM from starting. Add the fatal_error_backtraces line on PHP 8.5 only.
; /etc/php/8.3/fpm/pool.d/www.conf
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_flag[session.upload_progress.enabled] = off
; PHP 8.5 only
php_admin_flag[fatal_error_backtraces] = offCheck syntax before restarting
sudo php-fpm8.3 -t && sudo apachectl configtestPHP settings file
; /etc/php.d/99-php2shell.ini
; /etc/opt/remi/php83/php.d/99-php2shell.ini
; CVE-2026-109618: pin it so applications cannot override it
memory_limit = -1
post_max_size = 8M ; 32M if file uploads are required
; keep upload_max_filesize below post_max_size
; CVE-2026-109619
session.upload_progress.enabled = 0
; PHP 8.5 only
fatal_error_backtraces = 0PHP-FPM pool settings (pinned values)
Pool files must use the php_admin_value/php_admin_flag form. Plain php.ini lines stop PHP-FPM from starting. Add the fatal_error_backtraces line on PHP 8.5 only.
; /etc/php-fpm.d/www.conf
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_flag[session.upload_progress.enabled] = off
; PHP 8.5 only
php_admin_flag[fatal_error_backtraces] = offCheck syntax before restarting
sudo php-fpm -t && sudo apachectl configtestPHP settings file
; /opt/cpanel/ea-php83/root/etc/php.d/99-php2shell.ini
; CVE-2026-109618: pin it so applications cannot override it
memory_limit = -1
post_max_size = 8M ; 32M if file uploads are required
; keep upload_max_filesize below post_max_size
; CVE-2026-109619
session.upload_progress.enabled = 0
; PHP 8.5 only
fatal_error_backtraces = 0PHP-FPM pool settings (pinned values)
Pool files must use the php_admin_value/php_admin_flag form. Plain php.ini lines stop PHP-FPM from starting. Add the fatal_error_backtraces line on PHP 8.5 only.
WHM > MultiPHP Manager > PHP-FPM settings
PHP settings file
; /usr/local/etc/php/conf.d/99-php2shell.ini
; COPY 99-php2shell.ini /usr/local/etc/php/conf.d/
; CVE-2026-109618: pin it so applications cannot override it
memory_limit = -1
post_max_size = 8M ; 32M if file uploads are required
; keep upload_max_filesize below post_max_size
; CVE-2026-109619
session.upload_progress.enabled = 0
; PHP 8.5 only
fatal_error_backtraces = 0PHP-FPM pool settings (pinned values)
Pool files must use the php_admin_value/php_admin_flag form. Plain php.ini lines stop PHP-FPM from starting. Add the fatal_error_backtraces line on PHP 8.5 only.
; /usr/local/etc/php-fpm.d/zz-php2shell.conf
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_flag[session.upload_progress.enabled] = off
; PHP 8.5 only
php_admin_flag[fatal_error_backtraces] = offCheck syntax before restarting
docker exec <container> php-fpm -tPHP settings file
; php.ini shown by: php --ini
; CVE-2026-109618: pin it so applications cannot override it
memory_limit = -1
post_max_size = 8M ; 32M if file uploads are required
; keep upload_max_filesize below post_max_size
; CVE-2026-109619
session.upload_progress.enabled = 0
; PHP 8.5 only
fatal_error_backtraces = 0PHP settings file
; /opt/homebrew/etc/php/8.3/conf.d/99-php2shell.ini
; /usr/local/etc/php/8.3/conf.d/99-php2shell.ini (Intel Mac)
; CVE-2026-109618: pin it so applications cannot override it
memory_limit = -1
post_max_size = 8M ; 32M if file uploads are required
; keep upload_max_filesize below post_max_size
; CVE-2026-109619
session.upload_progress.enabled = 0
; PHP 8.5 only
fatal_error_backtraces = 0PHP-FPM pool settings (pinned values)
Pool files must use the php_admin_value/php_admin_flag form. Plain php.ini lines stop PHP-FPM from starting. Add the fatal_error_backtraces line on PHP 8.5 only.
; /opt/homebrew/etc/php/8.3/php-fpm.d/www.conf
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_flag[session.upload_progress.enabled] = off
; PHP 8.5 only
php_admin_flag[fatal_error_backtraces] = offCheck syntax before restarting
"$(brew --prefix php)/sbin/php-fpm" -tOn shared hosting or managed services you often cannot change PHP or its settings yourself. Ask your provider whether the fixed versions (8.5.12, 8.4.27, 8.3.36, 8.2.35 or later, or backports) are applied and when.
- Settings take effect only after every worker is restarted.
- Make sure
.htaccess,.user.iniandini_set()in the application do not change the values back. - Do not limit memory with
RLIMIT_AS/RLIMIT_DATA, such as ApacheRLimitMEM. It can trigger the vulnerable path. memory_limit = -1removes the memory cap. Pair it with a container or cgroup memory limit and a suitable number of FPM workers.- Lower upload limits can break large uploads, and turning off
session.upload_progressonly disables upload progress indicators.
Further risk reduction
- Limit request size at the web server or WAF, and block multipart requests if you do not need them.
- Lower input limits such as
max_input_nesting_level,max_input_varsandmax_file_uploads. - If performance allows, set PHP-FPM
pm.max_requests = 1so each request gets a fresh worker. - Move admin pages and internal PHP apps behind a VPN or an IP allowlist.
Done when
Ticks are not saved.
If response headers or error pages show versions such as PHP/8.3.6, attackers can easily pick out vulnerable servers. Hiding versions does not replace patching, but it makes you a less obvious target.
Apache
# Ubuntu/Debian: /etc/apache2/conf-available/security.conf
# RHEL family: a new .conf file under /etc/httpd/conf.d/
# hide version and OS in the Server header
ServerTokens Prod
# remove server details from error pages
ServerSignature Off
# requires mod_headers (a2enmod headers)
Header always unset X-Powered-ByNginx
# http or server block
server_tokens off;
fastcgi_hide_header X-Powered-By;
proxy_hide_header X-Powered-By;PHP
expose_php = Off ; remove X-Powered-By: PHP/x.y.z
display_errors = Off ; keep paths and code out of error messages
log_errors = On ; send errors to the log instead- Once versions are hidden, this page shows 'Version hidden'. Use the server check script for later checks.
- Product names such as
Server: nginxnormally remain. Check that only the version is gone withcurl -I https://your-site/. - Do not leave
phpinfo()pages on production servers. They expose the version and every setting.
Find phpinfo() calls in the web root
sudo grep -rlE --include='*.php' 'phpinfo[[:space:]]*\(' /var/wwwDone when
Ticks are not saved.
Watch for attack attempts until updates are complete. Clean logs do not prove a server is safe (official advice).
- Unusually large POST or multipart requests, especially ones with a
PHP_SESSION_UPLOAD_PROGRESSfield - Repeated crashes (segfaults) of PHP workers and out-of-memory errors (
Allowed memory size ... exhausted) - New PHP files in the web root, unexpected cron jobs, and outbound connections made by the web server account
Search your logs
# PHP-FPM worker crashes and out-of-memory errors
sudo grep -iE 'SIGSEGV|segfault' /var/log/php*-fpm.log
sudo journalctl -k | grep -i segfault
sudo grep -rh 'Allowed memory size' /var/log/apache2/ /var/log/nginx/ 2>/dev/null | tail
# PHP files created in the web root in the last 14 days
sudo find /var/www -name '*.php' -mtime -14 -lsDone when
Ticks are not saved.
Vulnerability summary
On October 21, 2026, the PHP project disclosed two vulnerabilities and released security updates. Together they are called php2shell.
CVE-2026-109618State corruption during out-of-memory handling- If out-of-memory handling interrupts PHP while request data is only partly initialized, request cleanup can treat the invalid data as valid, which can lead to remote code execution.
CVE-2026-109619Stale global state reused by the worker- Internal state from one request survives after its memory is freed, and a later request on the same worker uses it (use-after-free). The
session.upload_progressfeature is a related path.
Scope
- Any PHP that handles web requests: PHP-FPM, Apache mod_php, CGI/FastCGI
- Reachable before application code runs, so sites without login features are affected too
- Network devices, appliances and management consoles that embed PHP (vendor patches needed)
- CLI-only PHP is not exposed through the web, but should be updated too
Close the gap until you can update, with Cloudbric WAF+
Live services often can't be updated right away. Cloudbric WAF+ applies security rules that detect and block php2shell (CVE-2026-109618, CVE-2026-109619) attack attempts, reducing your web services' exposure while you prepare the update.
A WAF does not replace patching. If your origin server can be reached directly, it is not protected, so restrict origin access and update as well.
FAQ
Does 'Version hidden' mean my site is safe?
No. It means the response headers do not reveal a PHP version. A server that hides its version can still be vulnerable, so check it with the server check script.
Can I test with exploit code (PoC) found online?
We advise against it. The official PHP announcement and advisories contain no exploit code. Code circulating as a 'php2shell PoC' is hard to trace and may contain malware, so do not run it on production servers or work PCs. Check exposure with a non-intrusive check like this page.
What does the phpinfo exposure check do?
When you turn on this option in the check form, we also send GET requests in turn to well-known phpinfo paths in the web root (/phpinfo.php, /info.php, /test.php, /i.php, /php_info.php), in addition to the header check. If a phpinfo page is found, you get the real PHP version and the settings applied to the web, even when headers hide the version. An exposed phpinfo page is itself an information leak, so we recommend deleting it. Because unpublished paths are requested, the site's security devices or logs may record this as path probing. Turn it on only for sites you manage.
My distribution package version is lower than the fixed version.
Ubuntu, Debian, and RHEL-based distributions sometimes backport security fixes without changing the version number. Check your distribution's security notice for the package version that fixes these CVEs. For each PHP it checks, the script looks for the CVE numbers in the changelog of the package that PHP belongs to and shows a note, and it tells you when a PHP was not installed from a package, so distro backports do not apply to it.
The script flags only the CLI entry.
The CLI does not handle web requests, so it is not exposed through the web. It is still safer to update every PHP install on the same server.
How do I check internal servers or containers?
Even if the address cannot be reached from outside, you can download the script from the result page. For containers, run the script inside the container.
What do you store?
We keep the address you entered, the target server's response details (IP, status code, headers), the verdict, the page language, the visitor's country, and whether the script was downloaded or the contact link clicked, for one year for internal statistics. The requester's IP address and browser details are not stored in the check records. Web server access logs keep IP and browser details for 90 days, but they do not contain result links or check record IDs, so they cannot be joined to check records. Clicking Contact us passes only the verdict type (for example, vulnerable) to the Cloudbric website. This page does not use cookies. With the phpinfo option on, we also keep the path found, PHP version, SAPI and related settings, but not the page content.
Where do the check requests come from?
Check requests use the User-Agent 'Cloudbric-php2shell-check/1.0 (+https://php2shell.cloudbric.com)'. We send one HEAD request. If it is refused, we send one GET request and do not read the response body. With the phpinfo option on, we also send GET requests to 5 well-known paths and read only the first 512 KB of each response.