On October 20, details of two critical flaws in PHP leaked online, two days before
their planned disclosure. Known as PHP2Shell, CVE-2026-109618 and CVE-2026-109619
their planned disclosure. Known as PHP2Shell, CVE-2026-109618 and CVE-2026-109619
let an unauthenticated attacker run code on a web server; both are rated 9.2 out of
10. The PHP project shipped fixed releases the next day -- 8.5.12, 8.4.27, 8.3.36 and
8.2.35 -- with a blunt message: "There are no complete mitigations, updating is the
only safe choice." Exploitation in the wild began the same day.
A large percentage of web servers are affected, according to some estimates on the
order of 70% (Wiz, Endor Labs); PHP runs about 70% of the websites whose server-
order of 70% (Wiz, Endor Labs); PHP runs about 70% of the websites whose server-
side language is known (W3Techs). In watchTowr's words, "Every PHP installation
that serves web requests is in scope."
When the fix ships, every operations team asks: which of our servers still run the
old version? The answer is only as good as your monitoring. This post shows how to
test that monitoring before the next advisory, with a fleet of simulated PHP servers
old version? The answer is only as good as your monitoring. This post shows how to
test that monitoring before the next advisory, with a fleet of simulated PHP servers
from MIMIC Simulator.
1. Know What Your Monitoring Actually Reads
Scanners and monitoring platforms learn a server's PHP version through two
channels:
- HTTP response headers: By default, PHP announces its version in an
X-Powered-By header, and Apache can add it to its Server header. A scanner
reads them with one request -- and so can an attacker. To see what one of your
own servers announces:
curl -sS -o /dev/null -D - https://your-server/index.php | grep -i -E '^(server|x-powered-by):'
- SNMP inventory: Network management platforms read installed and running
software from the HOST-RESOURCES-MIB tables. This channel works where headers
are hidden, on any server that runs an SNMP agent.
Each channel has blind spots. Learn them before an advisory, not during one.
through the MIMIC SNMP Simulator, and reports whatever PHP version you give
it. A thousand servers is a modest lab -- our August post built ten thousand
devices -- and you choose the mix: servers patched on day one, servers on a
vulnerable 8.x release, and a tail of PHP 7 and PHP 5.
One simulated server, before its patch:
channels:
- HTTP response headers: By default, PHP announces its version in an
X-Powered-By header, and Apache can add it to its Server header. A scanner
reads them with one request -- and so can an attacker. To see what one of your
own servers announces:
curl -sS -o /dev/null -D - https://your-server/index.php | grep -i -E '^(server|x-powered-by):'
- SNMP inventory: Network management platforms read installed and running
software from the HOST-RESOURCES-MIB tables. This channel works where headers
are hidden, on any server that runs an SNMP agent.
Each channel has blind spots. Learn them before an advisory, not during one.
2. Build a Web Server Fleet That Reports Any Version
Each simulated server answers HTTP through the MIMIC Web Simulator and SNMPthrough the MIMIC SNMP Simulator, and reports whatever PHP version you give
it. A thousand servers is a modest lab -- our August post built ten thousand
devices -- and you choose the mix: servers patched on day one, servers on a
vulnerable 8.x release, and a tail of PHP 7 and PHP 5.
One simulated server, before its patch:
$ curl -I http://web-01/
HTTP/1.1 200 OK
Server: Apache/2.4.62 (Win64) PHP/8.3.20
X-Powered-By: PHP/8.3.20
$ snmpwalk -v2c -c public web-01 HOST-RESOURCES-MIB::hrSWInstalledName
...
HOST-RESOURCES-MIB::hrSWInstalledName.17 = STRING: "PHP 8.3.20"
HOST-RESOURCES-MIB::hrSWInstalledName.18 = STRING: "Apache HTTP Server 2.4.62"
Point your scanner and NMS at the fleet, and you know the right answer before they
report it: exactly which servers are vulnerable, because you made them that way.
As with the scenarios in our September post, it is a dry run your tools are free to fail.
3. Prove Your Tools Count the Servers They Can't Read
A monitor that alerts on a bad version says nothing about a server that reports noversion. If none of your 100 servers can be read, the dashboard stays green -- and
the true answer to "which servers still run old PHP?" is "we don't know."
This is common. Arctic Wolf's 2026 attack surface report finds 17% of IT assets
invisible to vulnerability management tooling, and nearly one in five at end of life.
In a Trend Micro survey, 74% of organizations had experienced security incidents
due to unknown or unmanaged assets. And the old servers are often the important
ones: TuxCare writes of PHP 7.4 still running "under a legacy billing app (and
there are plenty of them)," and PHP 5.3.3 -- released in 2010 -- is still among the
ten most common PHP versions websites announce in their headers (Web Almanac).
Your discovery tools find the servers on your network; a MIMIC lab with a known
count shows how well. Build exactly N servers, make some hard to read, and check:
- Ground truth: does the tool report all N, with the hard ones listed as
unknown?
- Discovery gaps: servers that answer only HTTP or ping, use another SNMP
community or version, or answer nothing on the expected port -- which ones
does it find?
- Classification: does an unreadable server show as unknown, drop out of the
report, or count as patched?
- Scale: does a sweep of thousands of addresses finish, or do timeouts quietly
drop hosts?
- Regression: after an upgrade or configuration change, does the lab give the
same count?
- Process: when the tool flags the PHP 5 billing server nobody can switch off,
does a ticket reach an owner, with the exception on record?
In the lab each is a configuration choice -- a drill you could never run in
production without switching off monitoring on real servers.
Your discovery tools find the servers on your network; a MIMIC lab with a known
count shows how well. Build exactly N servers, make some hard to read, and check:
- Ground truth: does the tool report all N, with the hard ones listed as
unknown?
- Discovery gaps: servers that answer only HTTP or ping, use another SNMP
community or version, or answer nothing on the expected port -- which ones
does it find?
- Classification: does an unreadable server show as unknown, drop out of the
report, or count as patched?
- Scale: does a sweep of thousands of addresses finish, or do timeouts quietly
drop hosts?
- Regression: after an upgrade or configuration change, does the lab give the
same count?
- Process: when the tool flags the PHP 5 billing server nobody can switch off,
does a ticket reach an owner, with the exception on record?
In the lab each is a configuration choice -- a drill you could never run in
production without switching off monitoring on real servers.
4. Test the Hard Cases
The fleet is yours to misconfigure, so give your tools the servers that trip them up: - Hidden banners: Harden a server -- expose_php off, ServerTokens Prod -- and
the header channel goes quiet. That is the usual case: the Web Almanac
finds an X-Powered-By header on about a quarter of sites, and a version in
just over a quarter of those. Does your NMS still find the PHP package over
SNMP?
the header channel goes quiet. That is the usual case: the Web Almanac
finds an X-Powered-By header on about a quarter of sites, and a version in
just over a quarter of those. Does your NMS still find the PHP package over
SNMP?
- Version in one header only: With PHP running as PHP-FPM, Apache's Server
header names no PHP version; only X-Powered-By carries it. Does your scanner
read both?
- Vendor backports: "Vendors sometimes backport fixes without changing the
upstream version number" (Datadog). A server can report an old number and
be fixed. Does your tool let you record the exception?
- Channels that disagree: Update the package but not the running service, and
SNMP and HTTP report different versions. Which one does your tool trust?
- Branches with no official fix: More than a third of PHP sites run PHP 7 or
older (W3Techs), and the PHP project no longer releases updates for 8.1
and older. Those servers need an alert of their own, for as long as they run.
5. Patch the Lab and Watch the Alerts Clear
Finding the vulnerable servers is half the job; the other half is knowing when youare done. In the lab, a patch is one command. Here is the same server a few
seconds later, on the fixed release, with no restart:
Server: Apache/2.4.62 (Win64) PHP/8.3.36
X-Powered-By: PHP/8.3.36
HOST-RESOURCES-MIB::hrSWInstalledName.17 = STRING: "PHP 8.3.36"
Patch a hundred servers at once, or in waves the way your team would, and watch
your tools: do the alerts clear, does the inventory update, does the management
report reach zero -- and stay there?
6. Under the Hood: How the Lab Works
Each simulated web server is a MIMIC agent with two protocols turned on: SNMP and WEB.- Start from a real server. We began with a recorded Windows server that already
answers the HOST-RESOURCES-MIB software tables.
- Add the web front end. The WEB module answers HTTP from a rule file. Ours has
one rule for GET and one for HEAD, and each hands the headers to a short Tcl
procedure that writes the Server and X-Powered-By lines from a per-server PHP
version variable.
one rule for GET and one for HEAD, and each hands the headers to a short Tcl
procedure that writes the Server and X-Powered-By lines from a per-server PHP
version variable.
- Add the inventory. Through MIMIC's Python API we added PHP and Apache to the
installed-software table, and httpd.exe and php-cgi.exe to the
running-software table.
installed-software table, and httpd.exe and php-cgi.exe to the
running-software table.
- Patch at runtime. One small script sets the version variable and the inventory
entry together, and both channels report the new version on the next request.
The same rule file serves every server; only the version differs. Hiding a banner
is a one-line change to the procedure, and making the channels disagree means
updating one and leaving the other.
question will again be "which servers still run the old version?" -- and your scanner,
entry together, and both channels report the new version on the next request.
The same rule file serves every server; only the version differs. Hiding a banner
is a one-line change to the procedure, and making the channels disagree means
updating one and leaving the other.
The Next Advisory
PHP2Shell will not be the last critical PHP flaw. When the next one lands, thequestion will again be "which servers still run the old version?" -- and your scanner,
NMS and inventory will answer it. A lab with a known answer shows you today
whether they will get it right.
To set up a lab like this for your own tools, tell us what you run. Check this short
To set up a lab like this for your own tools, tell us what you run. Check this short
Youtube video about MIMIC Simulator: