PHP2Shell: What Provides Protection and Why Unpatched Websites Will Remain

Contents
- Updated situation: attempts before public disclosure
- What PHP2Shell means
- Which updates provide protection?
- From updated packages to protected services
- The consequences of a missing update
- Will administrators actually install the updates?
- How many websites will remain at risk?
- Linux operations: inventory the runtime actually in use
- Updating PHP 8.4 with APT
- Updating PHP 8.5 with APT
- Updated packages do not prove updated running processes
- PHP 7.4: a security backport instead of unchanged 7.4.33
- Investigation: correlate multiple signals
- Questions and answers
- Sources
As of 22 October 2026. PHP2Shell affects the runtime beneath a large part of the web. Two critical vulnerabilities can allow unauthenticated remote code execution under suitable conditions. Security updates have been available since 21 October. The practical question is now which internet-facing installations are actually running corrected PHP. [1] [6]
Updated situation: attempts before public disclosure
AWS reports a PHP2Shell exploitation attempt on 17 October 2026 in its own MadPot telemetry, before public disclosure. For additional observed attempts, success against real-world systems remained uncertain. This is documented warning evidence, not a global count of compromised systems. Investigations of potentially affected installations should therefore reach back before the publication date. AWS: first-hand observations and limitations
What PHP2Shell means
The identifiers are CVE-2026-109618 and CVE-2026-109619. The first involves invalid internal memory state following an out-of-memory condition. The second involves internal references surviving after the underlying memory has been freed. Subsequent processing can turn either condition into a security failure. Both flaws are inside PHP itself. [3] [4]
“Every version since PHP 5” conveys the exceptional reach, but does not replace package verification. Old PHP 5 and 7 branches require scrutiny alongside PHP 8. cPanel lists older packages and specific corrected vendor revisions. Moving from PHP 7 to an arbitrary PHP 8 release is therefore insufficient evidence of protection. [7]
Apache with mod_php presents a particularly serious case: a demonstrated attack path operates before application code starts. PHP-FPM is not a safe exemption; the PHP Foundation also reports confirmed code execution through application-dependent paths. A WordPress login cannot reliably protect against a flaw below the application layer. [2]
Which updates provide protection?
The corrected official releases are 8.2.35, 8.3.36, 8.4.27 and 8.5.12, or a later corrected release in the corresponding branch. PHP 8.1 and earlier no longer receive regular PHP-project releases. Exceptional minimal source backports exist for 7.4, 8.0 and 8.1, while distributions and LTS vendors may supply corrected packages. [1]
An old base version does not necessarily mean an absent patch. The complete package revision and confirmation covering both CVEs matter. Conversely, a “PHP 8” label establishes no protection. [7]

From updated packages to protected services
- Inventory: Domains, PHP pools, containers, forgotten test installations and embedded administration tools all belong in scope.
- Evidence: Each runtime needs a recorded package source, revision and confirmation that both CVEs are addressed.
- Running instances: PHP workers must load the corrected build. Custom containers require an updated image and redeployment; updating the host does not modify an existing container image. [8]
- Web verification: The command line may use a different PHP installation from the website. Mapping each domain to the active runtime, followed by functional checks, completes the operational verification.
Where immediate patching remains impossible, ENISA recommends considering isolation of exposed applications. A static maintenance page served before PHP can stop requests reaching the vulnerable runtime. A maintenance plugin running inside that same PHP application does not reliably provide that separation. [5]
Official configuration mitigations only offer temporary reductions in exposure. For example, session.upload_progress.enabled=0 blocks specific confirmed session paths, not every possible variant. Changes to memory limits are not a universal repair either. Unlimited PHP memory can introduce availability risks; appropriate limits outside PHP require careful coordination. None of these measures replaces patching. [3] [4]
The consequences of a missing update
The starting point is code execution with the web worker account’s permissions, not automatic root access. Depending on permissions and accessible secrets, consequences can include stolen database credentials, exposed customer records, altered content, planted malware and abused API keys. Reachable internal services may become further targets where network access and credentials permit. [3]
For an online shop, a modified payment page can be more damaging than an obvious outage. Investigation, recovery, downtime and loss of trust add further costs. A later patch does not remove an existing backdoor. Concrete intrusion indicators call for isolation, evidence preservation, credential review and recovery from a trusted state.
Will administrators actually install the updates?
Assessment: A rapid initial reduction in exposed installations is plausible; complete coverage within days is not. Centrally operated platforms can update large numbers of websites together. Individual servers depend on ownership, package availability, compatibility and controlled restarts. Google Cloud explicitly distinguishes automatically updated runtimes from services requiring redeployment. [8]
The remaining population is likely to be persistent: abandoned projects, old agency deployments, containers without maintenance processes and applications tied to unsupported branches. Delays do not automatically indicate administrator negligence. Missing budgets, unclear responsibilities and untested legacy dependencies can obstruct a technically straightforward patch. A specific global update rate would currently be speculation.
How many websites will remain at risk?
On 22 October, W3Techs reports PHP on 69.3% of websites with a known server-side programming language. Within the detected PHP population, PHP 7 accounts for 26.9% and PHP 5 for 7.7%, totaling 34.6%. These figures describe adoption and legacy usage, not measured unpatched exposure. [9]
No reliable absolute remainder follows from those figures. Vendor backports, configuration differences, hidden version information and multiple websites sharing a server prevent direct conversion. Individual web pages, domains and servers also represent different counting units.
A purely illustrative calculation: in a population of one million genuinely affected installations, a successful 95% update rate would leave 50,000 unpatched. This is neither a measurement nor a forecast. The arithmetic simply explains why even a high completion rate can leave a substantial remainder at internet scale. Verified patch status matters more for a particular website than a global percentage.
Linux operations: inventory the runtime actually in use
The following package examples apply to Debian/Ubuntu systems using APT with existing trusted repositories. The relevant PHP branch must already be installed. RPM systems, hosting panels and custom containers require the corresponding vendor workflow. Adding an arbitrary third-party repository is not a reliable repair strategy.
A recoverable backup, a mapping of domains to PHP pools and a short functional test plan belong before the change. Package installation may restart services itself, so the maintenance window starts before the installation command. This inventory covers the CLI and services, but does not yet establish the web handler:
cat /etc/os-release
command -v php
php -v
php --ini
dpkg-query -W -f='${binary:Package}\t${Version}\n' 'php*' 'libapache2-mod-php*' 2>/dev/null
systemctl list-units --type=service --all | grep -E 'php.*fpm|apache2|nginx'
ps -eo pid,lstart,comm | grep -E 'php|apache2'A domain can use PHP 8.4 through FPM while the shell command php points to 8.5. Additional copies can exist in containers, hosting directories and forgotten test services. Evidence for each installation needs the package source, complete revision, service or container and associated websites.
Updating PHP 8.4 with APT
This workflow only upgrades installed 8.4 packages, including existing extensions and any installed Apache module. Simulation changes no packages. The candidate must contain PHP 8.4.27 or a vendor-confirmed backport addressing both CVEs. Debian 13 documents 8.4.27-1~deb13u1 as fixed. APT documentation · Debian: CVE-2026-109618 · Debian: CVE-2026-109619
set -euo pipefail
branch=8.4
sudo apt-get update
mapfile -t php_packages < <(
dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\n' \
"php${branch}-*" "libapache2-mod-php${branch}" 2>/dev/null |
awk '$1 == "ii" { print $2 }'
)
test "${#php_packages[@]}" -gt 0
apt-cache policy "${php_packages[@]}"
sudo apt-get -s install --only-upgrade "${php_packages[@]}"Installation follows review of the plan. Unexpected removals, branch changes or vulnerable candidates are reasons to stop. The next block belongs in the same Bash session and assumes an existing FPM service:
sudo apt-get install --only-upgrade "${php_packages[@]}"
sudo /usr/sbin/php-fpm8.4 -t
sudo systemctl restart php8.4-fpm
systemctl is-active php8.4-fpm
/usr/bin/php8.4 -v
/usr/sbin/php-fpm8.4 -vUpdating PHP 8.5 with APT
An existing 8.5 deployment uses the same workflow with a corrected candidate starting at 8.5.12 or a confirmed vendor backport. Debian 13’s standard repository does not provide PHP 8.5 simply because a newer PHP branch exists. Without a suitable candidate from the already approved source, this workflow stops before installation.
set -euo pipefail
branch=8.5
sudo apt-get update
mapfile -t php_packages < <(
dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\n' \
"php${branch}-*" "libapache2-mod-php${branch}" 2>/dev/null |
awk '$1 == "ii" { print $2 }'
)
test "${#php_packages[@]}" -gt 0
apt-cache policy "${php_packages[@]}"
sudo apt-get -s install --only-upgrade "${php_packages[@]}"Continuation again requires a reviewed plan, the same Bash session and an existing FPM service.
sudo apt-get install --only-upgrade "${php_packages[@]}"
sudo /usr/sbin/php-fpm8.5 -t
sudo systemctl restart php8.5-fpm
systemctl is-active php8.5-fpm
/usr/bin/php8.5 -v
/usr/sbin/php-fpm8.5 -vApache with mod_php instead requires an updated Apache module and an orderly Apache restart after a successful configuration check. On Debian the check is sudo apache2ctl configtest. A PHP package update in a pure FPM setup does not require a blanket nginx restart. Separate FPM masters, persistent CLI services and containers need individual treatment. Restarts can interrupt active requests; redundant deployments should be rolled out sequentially.
Updated packages do not prove updated running processes
Linux can keep a process running from an old executable after replacement. A fresh php -v then reports the corrected release while an existing worker continues with the old build. This read-only check identifies PHP processes retaining a deleted executable:
sudo bash <<'BASH'
for link in /proc/[0-9]*/exe; do
target=$(readlink "$link" 2>/dev/null) || continue
case "$target" in
*php*"(deleted)"*)
pid=${link#/proc/}
pid=${pid%/exe}
printf 'PID %s: %s\n' "$pid" "$target"
;;
esac
done
BASHA match calls for version and service verification, not an automatic intrusion diagnosis. An empty result does not establish complete protection either: loaded Apache modules, libraries and separately embedded runtimes need their own checks. Active workers matter more than package listings. Login, uploads, forms, queues and scheduled jobs belong in application-specific functional checks. Public phpinfo() pages are unsuitable for this task; protected internal diagnostics or active-process mapping are preferable.
PHP 7.4: a security backport instead of unchanged 7.4.33
PHP 7.4 remains end-of-life. Migration to a supported branch or a verified LTS patch from the existing supplier is preferable. For an unavoidable transition period, the PHP project published a minimal source backport covering both PHP2Shell identifiers. This does not restore general support or promise fixes for unrelated vulnerabilities.
The security commit verified on 22 October is 712e60f9edfd03f0960fc4fc3c2c375f42b90aa2. The workflow pins that exact source revision. Rebuilding the unchanged 7.4.33 release does not include this correction. PHP: specific security commit
The example produces a minimal CLI/FPM build in a separate compatible Debian/Ubuntu build environment with Git and CA certificates already available. All blocks run in the same Bash session. This is not an installation executed or fully validated on a production server for this article. Old PHP sources are not compatible with arbitrary current system libraries.
set -euo pipefail
workdir=$(mktemp -d "$HOME/php74-php2shell.XXXXXX")
cd "$workdir"
git init php-src
cd php-src
git remote add origin https://github.com/php/php-src.git
commit=712e60f9edfd03f0960fc4fc3c2c375f42b90aa2
git fetch --depth=1 origin "$commit"
git checkout --detach FETCH_HEAD
test "$(git rev-parse HEAD)" = "$commit"
git show -s --format='%H%n%s%n%b' HEADThe minimum tools and isolated installation prefix follow below. --disable-all keeps the example small; the result does not automatically include every extension required by WordPress or another application. The configured user and group www-data must exist in the target environment. PHP: Unix installation · PHP: FPM build options
sudo apt-get update
sudo apt-get install --no-install-recommends \
build-essential autoconf bison re2c pkg-config \
libxml2-dev libsqlite3-dev
./buildconf --force
prefix=/opt/php74-php2shell-712e60f9
test ! -e "$prefix"
./configure \
--prefix="$prefix" \
--with-config-file-path="$prefix/etc" \
--with-config-file-scan-dir="$prefix/etc/conf.d" \
--disable-all --enable-cli --enable-fpm --enable-session \
--with-fpm-user=www-data --with-fpm-group=www-data \
--without-pear
make -j2
NO_INTERACTION=1 REPORT_EXIT_STATUS=1 make testFailed tests or unexplained skips of relevant tests need investigation. Installation into the new prefix follows an accepted test run; this does not replace /usr/bin/php or distribution packages:
sudo make install
sudo install -d -m 0755 "$prefix/etc/conf.d"
sudo install -m 0644 php.ini-production "$prefix/etc/php.ini"
"$prefix/bin/php" -n -v
"$prefix/bin/php" --ini
"$prefix/sbin/php-fpm" -v
git rev-parse HEADThe output can still carry a 7.4 version number. Evidence of the fix consists of the verified source commit, build records and the artifact actually running. Production use still requires the necessary extensions and their tests, such as database drivers, mbstring, XML, cURL or ZIP. Development libraries, ABI and phpize/php-config must match this specific build.
FPM configuration, pool accounts and filesystem permissions then require separate review. A dedicated socket and service prevent accidental mixing with the existing runtime. Only after a successful configuration test using the new php-fpm -t -y /path/to/php-fpm.conf does the web handler switch to that pool. A successful CLI command changes no website. If rollout fails, fallback must not silently restore public access to a vulnerable runtime; isolation remains the interim option.
Investigation: correlate multiple signals
Datadog reproduced the flaws in its laboratory. Successful code execution need not crash a worker or produce a suspicious HTTP status. HTTP 200 therefore provides no assurance. Suspicious form submissions, memory errors and unexpected child processes become meaningful through correlation. A single memory-exhaustion error alone does not prove an attack. Datadog Security Labs: original analysis
Investigation combines web-server/proxy logs, PHP errors, process events and unexpected file changes. Existing evidence should be preserved before cleanup. Indiscriminate recording of complete form bodies would introduce passwords and personal data into logs; metadata and controlled forensic collection are preferable. A WAF can constrain requests but cannot fix PHP. Completion needs two distinct findings: a corrected runtime is active, and possible prior compromise has been investigated.
Questions and answers
Is a WordPress update sufficient?
No. Updating WordPress does not replace a corrected PHP runtime. Application updates and server packages require separate verification.
Does a working website establish safety?
No. Ordinary requests can succeed on a vulnerable runtime. The absence of visible changes also cannot exclude an earlier intrusion.
Useful evidence combines the package revision, vendor confirmation covering both CVEs and verification of active web processes. Signs of compromise require a separate investigation.
Sources
- PHP: Security advisory, 21 October 2026
- PHP Foundation: security release and background
- PHP: CVE-2026-109618
- PHP: CVE-2026-109619
- ENISA: joint statement
- NHS England: PHP2Shell
- cPanel: corrected package revisions
- Google Cloud: runtime updates and redeployment
- W3Techs: PHP usage, 22 October 2026
Additional primary sources and documentation
- PHP: pinned PHP 7.4 security backport
- PHP: Unix installation
- PHP: FPM compilation options
- APT: apt-get options and simulation
- Debian: CVE-2026-109618
- Debian: CVE-2026-109619
- AWS: MadPot observations before disclosure
- Datadog Security Labs: reproduction, detection and remediation
Further analysis and reporting
The additionally suggested Prophet Security article was unavailable during research and was not used as evidence.