At 21:55 on 21 October, an hour before a scheduled software patch, Japan's Prime Minister asked the country's website owners to stay up and install it. Sanae Takaichi's post on X named PHP, the language behind about seven in ten websites, gave the release time, and said that if a site could not be patched promptly, switching it off was an effective way to reduce the risk. Banks paused online banking, game companies took their services down for maintenance, and hosting companies ran emergency maintenance overnight. This article explains why a head of government spoke about a patch, what the two flaws are, how much of Japan they reach, and what to do now.

Figure 1. The Prime Minister's post (@takaichi_sanae), 21 October 2026, 21:55 JST, as shown in X's automatic English translation. Screenshot supplied by a reader; profile photo and X logo removed. The translation uses the former English name of the National Cybersecurity Office.
The original Japanese reads:
皆様に、今夜に対応して頂きたい大至急のお願いです。
多くのウェブサイトで使用されているPHPに関し、PHPのセキュリティチームが、重大な2件の脆弱性に関する注意喚起を行っています。
公開しているウェブサイトでPHPを使用しているか確認し、今夜、10月21日午後11時(日本時間)に公開される修正パッチについて、可及的速やかに適用できるよう準備をお願いします。
修正パッチを速やかに適用することが困難な場合は、例えばウェブサイト等を一時的に停止させることも、リスクを低減させるための有効策の一つと考えられます。
The post ends by pointing readers to the National Cybersecurity Office's alert account, @cyber_forecast. Takaichi on X (Japanese) · Nikkei (Japanese) · Impress Watch (Japanese)
Why a Prime Minister's post matters
Governments warn about vulnerabilities every week, through agencies and in language written for security teams. This post was different in four ways.
- It spoke to owners, not engineers. “Check whether your public website uses PHP” is a question for the shop, clinic, school or local office whose site was built by someone else and now sits on a rental server. Those owners do not read JPCERT/CC or IPA alerts. They do read the Prime Minister, and that night they called their web agencies and hosting companies.
- It gave permission to go offline. The National Cybersecurity Office had posted the same advice eleven minutes earlier, at 21:44: if the patch cannot be applied promptly, suspending the site temporarily is an effective way to reduce the risk. NCO on X (Japanese) Taking a revenue-earning site down is a management decision. With the head of government on record, a bank, a retailer or a city office could defend the outage to customers and auditors. CERT-EU, the EU institutions' response team, gave similar advice the evening before. The Next Web
- It was about hours, not days. The details had leaked before the release and Cloudflare was already reporting attack attempts (timeline below). Asking for action “tonight” reflected a judgement that the usual patch-within-days rhythm was too slow.
- It fits a policy arc. Takaichi chaired the ruling party's cyber security headquarters, which in 2019 called for active cyber defence and a single cyber agency. When the National Cybersecurity Office opened on 1 July 2025 she wrote that she was “deeply moved” that the country finally had a control tower. Takaichi on X, 1 July 2025 (Japanese) The active cyber defence law came into force on 1 October 2026, and on 8–9 October a minister and the party's cyber chief called the wave of breaches critical, an “emergency in cyberspace”, as we reported. On 21 October that structure faced something different: a single technical deadline, set by someone else, a few hours away.
Other politicians repeated it: Yuichiro Tamaki of the Democratic Party for the People posted the same warning at 22:20. Tamaki on X (Japanese)
What the post was not. It was a request, not an order: the office cannot compel private companies to patch or to shut down. And the people it reached most easily, users of websites, have nothing to patch. The flaws are in servers; visitors can only wait for site owners to act.
What happened, hour by hour

Figure 2. Twenty hours from advance notice to patch, 21 October 2026 (JST). Times for posts on X are derived from the posts' IDs. Original graphic for this article.
- 21 October, 04:52 JST (19:52 UTC on 20 October). The PHP security team gave advance notice of two critical vulnerabilities, both rated 9.2 under CVSS v4.0, with a release set for 21 October at 14:00 UTC (23:00 JST). It reserved the right to release earlier if details became public or it saw exploitation. The PHP Foundation on X · php.announce · oss-security
- 20 October (UTC). A post on Hacker News announced two upcoming PHP remote code execution disclosures and was later taken down. Sansec reported a leaked screenshot of an exploit tool called PHP2Shell, which gave the flaws their name. Sansec · Huntress
- 21 October, during the day. IPA asked organisations to take stock of everything that uses PHP and be ready to patch as soon as the release appeared. It noted that end-of-life PHP is still in use and that it had not yet seen attacks. IPA (Japanese)
- 21 October, 12:02 JST (03:02 UTC). Cloudflare says its managed WAF rules protected free and paid customers by default from this time. It later reported attack traffic against customers, with attackers changing payloads to get around the rules. Its CTO said early attacks were emerging. Cloudflare
- 21 October, afternoon and evening. Services began precautionary suspensions. Shizuoka Bank paused internet banking, its website, group sites and its app; the 77 Bank and Daishi Hokuetsu Bank paused some services. Shizuoka Bank and the 77 Bank said no data leak had been found. Nikkei: Shizuoka Bank (Japanese) · Nikkei: 77 Bank (Japanese) · Niigata Nippo: Daishi Hokuetsu Bank (Japanese)
- 21 October, 21:44 and 21:55 JST. The National Cybersecurity Office, then the Prime Minister, said a temporary shutdown was an effective option.
- 21 October, about 22:40 JST. PHP released 8.5.12, 8.4.27, 8.3.36 and 8.2.35, slightly ahead of schedule, according to Japanese reports. Posts linking the advisory were circulating by 22:57. ITmedia NEWS (Japanese)
- 21–22 October, overnight. Cygames, pixiv, niconico, DMM.com, BASE and others ran emergency maintenance or paused services, as did the rental-server companies Sakura Internet, XServer and GMO Pepabo. No damage had been reported. Impress Watch (Japanese) · Game Watch (Japanese) · Nikkei: Sakura Internet (Japanese)
What the two flaws are
| CVE-2026-109618 | CVE-2026-109619 | |
|---|---|---|
| PHP advisory | GHSA-w8hw-w9fx-j79w, “RCE via OOM corruption” | GHSA-6wmf-4rv5-p3rp, “RCE via stale global state” |
| What goes wrong | Running out of memory while a request starts leaves request memory partly set up; clean-up then trusts it | State from one request survives after its memory is freed, and a later request on the same worker uses it |
| Severity | CVSS v4.0 9.2, critical | CVSS v4.0 9.2, critical |
| Fixed in | 8.5.12, 8.4.27, 8.3.36, 8.2.35 | 8.5.12, 8.4.27, 8.3.36, 8.2.35 |
Both allow unauthenticated remote code execution over HTTP. Four points decide how exposed you are:
- Every PHP that serves web requests is in scope. PHP says all installations serving web requests are at risk; NCSC-NL lists PHP-FPM, Apache's mod_php, php-cgi, FrankenPHP and LiteSpeed. The risk is highest for mod_php, where an attack was demonstrated before any application code runs. A login page, a firewall rule in your app or a patched WordPress plugin does not help: the flaw is in the PHP engine.
- There is no complete mitigation. PHP's announcement says updating is the only safe choice. The interim settings below close the confirmed attack paths, not every variant.
- PHP 8.1 and older get no official release. Source-only backports for 7.4, 8.0 and 8.1 have been reported; Linux distributions decide whether to ship them. Whether PHP 5 is affected is reported (CERT-EU and Sansec say yes) but not stated in PHP's advisories.
- Configuration matters for CVE-2026-109618. CrowdStrike's analysis is that the vulnerable condition is roughly a
memory_limitbelow four timespost_max_size. PHP's stock defaults do not meet it; common production pairs such as 64 MB uploads with a 128 MB memory limit do. CrowdStrike
Is it being exploited? The reports disagree. Cloudflare saw attack traffic and payload changes; watchTowr lists exploitation seen in its honeypots. NCSC-NL had no indication of active exploitation on 21 October but noted several public proof-of-concept exploits and expected large-scale exploitation soon. Huntress had seen none in its customer base. CISA's Known Exploited Vulnerabilities catalogue, version 2026.10.21 (released 14:04 UTC on 21 October), did not list either CVE when we checked. NCSC-NL · watchTowr
How much of Japan it reaches
Our analysis. Three numbers set the scale.
- About seven in ten websites. PHP runs on 70.5% of the websites whose server-side language W3Techs can identify (August 2026). Of those PHP sites, 29.4% run PHP 7 and 8.0% run PHP 5, neither of which gets an official fix. W3Techs
- About 142,000 web services in Japan announce PHP in their headers. We counted, without scanning, the web services in Japan that announce PHP in their
X-Powered-Byheader, using an internet-measurement dataset collected over recent weeks. Of the 136,489 that give a version we could match, 100,633 (73.7%) run a branch with no official fix (8.1 or older). Only 26 already announced one of the fixed versions. The count misses every site that hides its version, so it is a floor, not a census; and servers that announce their version tend to be the less carefully configured ones, so the share of old branches is probably higher here than across all servers. - Japan was the main target the last time. CVE-2024-4577, a PHP-CGI flaw on Windows that was worse on Japanese-language systems, was exploited from January 2025 mainly against Japanese organisations in technology, telecoms, entertainment, education and e-commerce. The attacker installed Cobalt Strike, stole credentials and deleted logs. Cisco Talos CISA lists that flaw as used by ransomware.

Figure 3. Web services in Japan announcing a PHP version, by branch. Branches 8.2 to 8.5 have an official fix; 8.1 and older do not. Counted from an internet-measurement dataset on 22 October 2026 (JST), passively and without scanning; one service is one IP address and port. Original graphic for this article.
| Branch | Services announcing it | Official fix? |
|---|---|---|
| PHP 8.5 | 3,341 | Yes: 8.5.12 |
| PHP 8.4 | 3,561 | Yes: 8.4.27 |
| PHP 8.3 | 20,594 | Yes: 8.3.36 |
| PHP 8.2 | 8,360 | Yes: 8.2.35 |
| PHP 8.1 | 5,412 | No |
| PHP 8.0 | 6,570 | No |
| PHP 7.4 | 26,909 | No |
| PHP 7.3 | 8,818 | No |
| PHP 7.2 | 8,152 | No |
| PHP 7.1 | 3,056 | No |
| PHP 7.0 | 2,724 | No |
| PHP 5.6 | 12,829 | No |
| PHP 5.5 | 4,278 | No |
| PHP 5.4 | 8,582 | No |
| PHP 5.3 | 10,997 | No |
| PHP 5.2 | 2,306 | No |
| Total | 136,489 | 100,633 (73.7%) with no official fix |
Read these numbers with care. A header shows what the server announces, not what it runs: Ubuntu, Debian and Red Hat fix old branches without changing the version number, so a “7.4.3” server may be patched, and a server that hides its header may not be. The dataset is crawled over weeks, so it shows the position before the release, not after it.
Where PHP hides. Beyond your own web servers, check:
- Rental and managed hosting. Providers patch the runtime, but a site set to an old PHP version in the control panel may still be on an end-of-life build. Read your provider's notice.
- Products you bought. E-commerce (EC-CUBE), content management (WordPress), groupware, web mail, monitoring consoles, NAS boxes and network appliances often ship their own PHP. NCSC-NL keeps a list of products with a PHP dependency.
- Containers. An image built on an old
php:base stays vulnerable until it is rebuilt and redeployed.
The attacker's playbook
How a red team or an organised group would use PHP2Shell, mapped to MITRE ATT&CK. It is a model, not an attribution.
| Step | What it looks like | ATT&CK |
|---|---|---|
| Find targets | Pull hosts that announce PHP from search engines and scan data; spray any .php path. No application knowledge needed |
T1595.002 Vulnerability Scanning; T1596 Search Open Technical Databases |
| Get in | One or a few crafted HTTP requests, typically multipart form posts, before any application code or login runs | T1190 Exploit Public-Facing Application |
| Run commands | Shell commands as the web server account; PowerShell on PHP running on Windows | T1059.004 Unix Shell; T1059.001 PowerShell |
| Stay in | Drop a web shell, add auto_prepend_file to a .user.ini or php.ini, add a cron job or an SSH key |
T1505.003 Web Shell; T1053.003 Cron; T1098.004 SSH Authorized Keys |
| Hide | Change payloads to slip past WAF rules (Cloudflare saw this); the server still answers 200; delete logs, as in the 2025 Japan campaign | T1027 Obfuscated Files or Information; T1070.002 Clear Linux or Mac System Logs |
| Cash in | Read database passwords and cloud keys from wp-config.php and .env; install miners; take data; move inside |
T1552.001 Credentials In Files; T1496 Resource Hijacking; T1005 Data from Local System |
What a patient group would do differently. Mass exploitation creates noise; a targeted actor uses it as cover. It would plant quiet persistence that survives the patch (a prepend file or a cron job, not a noisy shell), take the cloud keys from configuration files and leave. Patching closes the door; it does not remove anyone already inside. That is why the hunt below starts on 20 October, when the details leaked.
The defender's playbook
1. Tonight: patch or isolate
- Find every PHP. Your web servers, hosted sites, containers, and products that bundle PHP. Ask your hosting company and vendors in writing (template below).
- Update and restart. Install 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or your distribution's fixed package. Restart every PHP-FPM pool and Apache instance: a worker that keeps running keeps the old code. Rebuild and redeploy container images.
- If you cannot update tonight, apply PHP's interim settings at server level, not with
ini_set()or a per-directory.user.ini, and restart:session.upload_progress.enabled = 0closes the session upload progress path;- on PHP 8.5,
fatal_error_backtraces = 0closes the fatal-error backtrace path; - if an application does not take uploads, block or tightly limit
multipart/form-datarequests at the reverse proxy or WAF; - turn on your WAF vendor's emergency rules if it has published them (Cloudflare has).
- If none of that is possible, take it offline, as the government advised. Start with internet-facing mod_php servers, end-of-life branches and anything that holds personal data.
- End-of-life branches have no official release. Upgrade to 8.2 or later, use a distribution or vendor that ships a fixed package, or isolate the server until you can.
Check what a server runs. The command-line PHP may read a different php.ini from PHP-FPM or Apache, so check the one that serves the web:
# What PHP does this server run, and are the interim settings in force?
php -v
php -i | grep -E '^(session.upload_progress.enabled|memory_limit|post_max_size|fatal_error_backtraces) '
# The PHP that serves the web can differ from the command line: check PHP-FPM and Apache too.
for f in /usr/sbin/php-fpm*; do [ -x "$f" ] && "$f" -v; done
apachectl -M 2>/dev/null | grep -i php
# Distribution packages: compare with your distribution's advisory, not with php.net.
dpkg -l 'php*' 2>/dev/null | grep '^ii' || rpm -qa 'php*'
2. This week: hunt back to 20 October
- The only trace on the server may be one error line. CrowdStrike says a successful attempt can leave just a memory-exhaustion fatal error, ending “in Unknown on line 0”, in the web server's error log, while the response is still HTTP 200 and the application log is clean. Search for it (queries below).
- Worker crashes. Bursts of PHP-FPM children exiting on signal 11, or Apache children with segmentation faults, around the same time.
- The web server starting programs.
php-fpm,apache2orhttpdstarting a shell,curl,wget,pythonorperl. - New PHP files and changed settings. New
.phpfiles under the web root written by the web server account;auto_prepend_fileorauto_append_filein any.user.iniorphp.ini; new cron entries and SSH keys. - If you find something, isolate the server, keep its logs and memory before you rebuild (DFIR Part 2 covers the order), and rotate every secret in its configuration files.
3. Which teams should act
| Team | Exposure to review | Next step | Evidence needed |
|---|---|---|---|
| IT Operations | Web servers, containers, hosted sites | Patch or apply interim settings; restart workers; take offline what cannot be fixed | Asset list with PHP version, patch time, restart time |
| Third-party Risk | Hosting companies, web agencies, products that bundle PHP | Send the vendor questions below; track answers | Written answers, dates |
| SIEM / SOC | Web server error logs, WAF logs | Run the error-log queries from 20 October; alert on the signature | Query results, alert rule |
| EDR | Linux and Windows web servers | Hunt for web server processes starting shells and writing PHP files | Process and file events |
| Legal / Privacy | Sites that hold personal data | Decide whether a precautionary shutdown needs a customer notice; prepare for a report to the Personal Information Protection Commission if access is found | Decision record, notice text |
Hunting: PHP2Shell on your web servers
No vendor had published confirmed indicators of compromise when we checked; Huntress and Equixly said the same. The queries below look for the behaviour the attack and its aftermath produce. All are illustrative and were not run against live data. Table and field names were checked against vendor documentation on 22 October 2026 (JST). Set the start date to 20 October, when the details leaked.
1. The web server starting a shell or a download tool. Microsoft Defender XDR advanced hunting, DeviceProcessEvents, Linux servers with Defender for Endpoint.
// Illustrative; not run against live data. Microsoft Defender XDR advanced hunting, DeviceProcessEvents (Linux web servers with Defender for Endpoint).
// PHP2Shell (CVE-2026-109618, CVE-2026-109619): after exploitation, the PHP worker or web server starts a shell or a download tool.
// Expect noise from applications that call sendmail, convert, git or similar; list them in KnownGood after review.
let Since = datetime(2026-10-20);
let WebServers = dynamic(["REPLACE_WITH_WEB_SERVER_NAME"]);
let KnownGood = dynamic(["REPLACE_WITH_EXPECTED_COMMAND_LINE"]);
DeviceProcessEvents
| where Timestamp > Since
| where DeviceName has_any (WebServers)
| where InitiatingProcessFileName matches regex @"^(php(-fpm|-cgi)?[0-9.]*|apache2|httpd|lsphp|frankenphp)$"
| where FileName in~ ("sh", "bash", "dash", "busybox", "curl", "wget", "python", "python3", "perl", "nc", "ncat", "socat", "base64", "whoami", "id", "uname")
| where not(ProcessCommandLine has_any (KnownGood))
| summarize Runs = count(), FirstSeen = min(Timestamp), LastSeen = max(Timestamp), Commands = make_set(ProcessCommandLine, 20)
by DeviceName, InitiatingProcessFileName, FileName, AccountName
| order by FirstSeen asc
| take 500
2. The error line the attack leaves. Microsoft Sentinel, Syslog, for web servers that forward their Apache, Nginx or PHP-FPM error logs through syslog. If your error logs go to a custom table, change the table and column names.
// Illustrative; not run against live data. Microsoft Sentinel, Syslog table, for web servers that forward Apache, Nginx or PHP-FPM error logs through syslog.
// PHP2Shell (CVE-2026-109618): a successful attempt may leave only a memory-exhaustion fatal error ending "in Unknown on line 0",
// raised while the request starts, before any script runs. Worker crashes (signal 11) around the same time strengthen the signal.
let Since = datetime(2026-10-20);
let WebServers = dynamic(["REPLACE_WITH_WEB_SERVER_NAME"]);
Syslog
| where TimeGenerated > Since
| where Computer has_any (WebServers)
| where (SyslogMessage has "Allowed memory size" and SyslogMessage contains "in Unknown on line 0")
or SyslogMessage has "exited on signal 11"
or SyslogMessage has "Segmentation fault"
| extend Signal = iff(SyslogMessage has "Allowed memory size", "startup memory fatal", "worker crash")
| summarize Events = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), Sample = take_any(SyslogMessage)
by Computer, Signal, bin(TimeGenerated, 10m)
| order by FirstSeen asc
| take 500
3. The same error line in Splunk for web server error logs.
index="REPLACE_WITH_WEB_INDEX" sourcetype="REPLACE_WITH_ERROR_LOG_SOURCETYPE" earliest="10/20/2026:00:00:00"
(("Allowed memory size" "Unknown on line 0") OR "exited on signal 11" OR "Segmentation fault")
| eval signal=if(like(_raw, "%Allowed memory size%"), "startup memory fatal", "worker crash")
| bin _time span=10m
| stats count AS events earliest(_time) AS first_seen latest(_time) AS last_seen values(source) AS sources BY _time host signal
| sort 0 first_seen
Download the examples: process KQL · error-log KQL · error-log SPL.
Other platforms. CrowdStrike Falcon (LogScale / Next-Gen SIEM), ProcessRollup2 on Linux. CrowdStrike also ships a correlation rule template, “Generic - Web - PHP Memory Exhaustion During Request Startup”, for the error-log signal.
// Illustrative; not run against live data. CrowdStrike Falcon, ProcessRollup2 events from Linux web servers.
// The web server or PHP worker starting a shell or a download tool. Map aid to your host inventory before triage.
#event_simpleName=ProcessRollup2 event_platform=Lin
| in(field="aid", values=["REPLACE_WITH_WEB_SERVER_AID_1", "REPLACE_WITH_WEB_SERVER_AID_2"])
| ParentBaseFileName=/^(php(-fpm|-cgi)?[0-9.]*|apache2|httpd|lsphp|frankenphp)$/i
| FileName=/^(sh|bash|dash|busybox|curl|wget|python3?|perl|nc|ncat|socat|base64|whoami|id|uname)$/i
| groupBy([aid, ParentBaseFileName, FileName, CommandLine], function=[count(as=Runs), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)])
| sort(FirstSeen, order=asc, limit=500)
Sumo Logic, web server error logs:
_sourceCategory="REPLACE_WITH_ERROR_LOG_SOURCE_CATEGORY"
| where (_raw matches "*Allowed memory size*" and _raw matches "*Unknown on line 0*") or _raw matches "*exited on signal 11*" or _raw matches "*Segmentation fault*"
| if(_raw matches "*Allowed memory size*", "startup memory fatal", "worker crash") as signal
| timeslice 10m
| count as events by _timeslice, _sourceHost, signal
| sort by _timeslice asc
No result does not mean you were not attacked. Many servers do not forward error logs at all, memory errors are often filtered out as noise, and an attacker with a shell can delete local logs. Corelight has published Zeek and Suricata detections for the request side. DeviceProcessEvents · Syslog table · Corelight
Detection rules: Microsoft Sentinel and CrowdStrike
The hunts above look back once. The rules below run on a schedule and raise an alert, so the next attempt is caught while it happens. Together they follow the attack from the first request to persistence that survives the patch.
All are illustrative and were not run against live data. Every KQL query was analysed with Microsoft's own query engine library (Kusto.Language) against the table schemas in Microsoft's documentation, which catches unknown columns and syntax errors. Each Sentinel file was also checked against the template rules Microsoft publishes for analytics rules. The CQL was checked against CrowdStrike's published event names and query functions, but not run.
| What it catches | Rules | Data it needs | ATT&CK | Severity |
|---|---|---|---|---|
| The exploit's only trace on the server: a memory fatal raised before any script runs | Sentinel 01 · CrowdStrike 05 | Web server error logs | T1190 | Medium |
| Bursts of crashed PHP workers while an exploit is tried and tuned | Sentinel 02 | Web server error logs | T1190, T1499.004 | Medium |
| The web server starting a shell, a download tool or a reconnaissance command | Sentinel 03 · CrowdStrike 01 | EDR process events | T1059.004, T1505.003 | High |
A web shell, .user.ini, .htaccess or php.ini written by the web server |
Sentinel 04 · CrowdStrike 02 | EDR file events | T1505.003 | Medium |
| PHP running a script from an uploads, cache or temporary folder | CrowdStrike 03 | Falcon PHP visibility on Linux | T1505.003 | High |
| The web server reaching a public address it has not contacted before | Sentinel 05 · CrowdStrike 04 | EDR network events | T1105, T1071.001 | Medium |
| Cron, systemd, SSH keys or accounts changed from the web server's side | Sentinel 06 · CrowdStrike 06 | EDR process and file events | T1053.003, T1098.004, T1543.002 | High |
| An attempt followed by a shell on the same server within 30 minutes | Sentinel 07 | Error logs and EDR | T1190, T1059.004 | High |
| Which servers still run PHP below the fixed release, or with no fix at all | Defender query | Defender Vulnerability Management |
Deploying them.
- Microsoft Sentinel. Each file is an analytics rule in the YAML format of Microsoft's Sentinel repository. Import it through a repository connection, or create a scheduled rule and copy the query, frequency, look-back, entity mappings and tactics from the file. Replace every
REPLACE_WITH_value first. Rules 03 to 07 need the Microsoft Defender XDR connector streaming theDevice*tables; rules 01, 02 and 07 need your web servers' error logs collected intoSyslogby the Azure Monitor Agent. - CrowdStrike Falcon. Save each CQL query as a Next-Gen SIEM correlation rule (every 15 minutes over the last 20 is a sensible start), or run it in Event Search. Rule 03 needs the Linux sensor's PHP visibility, which records
PhpExecuteScript; rule 05 needs web server error logs ingested as third-party data. CrowdStrike also ships its own template for the error-log signal, “Generic - Web - PHP Memory Exhaustion During Request Startup”. - Run them quietly first. Leave each rule for a week with alerts going to a test queue, record what fires and why, and add your applications' normal behaviour to the
KnownGood,WebAccountsand path exclusions before you alert on them.
Microsoft Sentinel analytics rules
01. PHP memory fatal at request startup. The one line CrowdStrike says a successful attempt may leave: a memory fatal “in Unknown on line 0”, raised before any script runs. Ordinary script memory errors name a file and a line, so this is rarely noise.
id: e0d3171b-48a9-5604-b1e4-653cc3e65f55
name: PHP2Shell - PHP memory fatal at request startup
description: |
'A web server logged a PHP memory-exhaustion fatal error raised "in Unknown on line 0", before any script ran: the trace CrowdStrike describes for attempts on CVE-2026-109618. Needs web server error logs in Syslog.'
severity: Medium
requiredDataConnectors:
- connectorId: SyslogAma
dataTypes:
- Syslog
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
query: |
// PHP2Shell (CVE-2026-109618): a memory fatal raised while the request starts, before any application code runs.
// Ordinary script memory errors name a file and a line; these name "Unknown" and line 0.
Syslog
| where SyslogMessage has "Allowed memory size" and SyslogMessage contains "in Unknown on line 0"
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Events = count(), Sample = take_any(SyslogMessage) by Computer, ProcessName
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
eventGroupingSettings:
aggregationKind: AlertPerResult
version: 1.0.0
kind: Scheduled
02. Burst of PHP worker crashes. Memory-corruption exploits often crash workers while they are being tried. Five crashes in ten minutes on one server is the starting threshold; lower it for small sites.
id: 2c646486-7a9c-5ba7-a7e1-61950d5272f6
name: PHP2Shell - Burst of PHP worker crashes
description: |
'Five or more PHP-FPM or Apache worker crashes (signal 11, segmentation fault) on one server within ten minutes. Memory-corruption exploits such as CVE-2026-109619 often crash workers while they are being tuned.'
severity: Medium
requiredDataConnectors:
- connectorId: SyslogAma
dataTypes:
- Syslog
queryFrequency: 15m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- Impact
relevantTechniques:
- T1190
- T1499.004
query: |
// PHP2Shell (CVE-2026-109619): bursts of crashed PHP workers while an exploit is tried and tuned.
// PHP-FPM logs "child ... exited on signal 11 (SIGSEGV)"; Apache logs "child pid ... exit signal Segmentation fault (11)" (AH00052).
let Threshold = 5;
Syslog
| where SyslogMessage has "exited on signal 11" or SyslogMessage has "Segmentation fault"
| where ProcessName has_any ("php", "apache", "httpd") or SyslogMessage has_any ("php-fpm", "AH00052")
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Crashes = count(), Sample = take_any(SyslogMessage) by Computer, bin(TimeGenerated, 10m)
| where Crashes >= Threshold
| project StartTime, EndTime, Computer, Crashes, Sample
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
eventGroupingSettings:
aggregationKind: AlertPerResult
version: 1.0.0
kind: Scheduled
03. Web server starts a shell or tool. The most reliable sign that an exploit worked: the PHP worker or web server starting sh, curl, python or whoami. Some applications call sendmail or image tools; review them once and add them to KnownGood.
id: 046199a4-b983-57ac-8e4d-3c78e139a8bf
name: PHP2Shell - Web server starts a shell or tool
description: |
'A PHP worker or web server process started a shell, a download tool or a reconnaissance command. After PHP2Shell exploitation the attacker runs commands as the web server account. Tune KnownGood for tools your applications call.'
severity: High
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceProcessEvents
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
- Persistence
relevantTechniques:
- T1059.004
- T1059.001
- T1505.003
query: |
// PHP2Shell post-exploitation: commands run by the PHP worker or web server (Linux and PHP on Windows).
let WebServer = @"(?i)^(php(-fpm|-cgi)?[0-9.]*(\.exe)?|apache2|httpd(\.exe)?|lsphp|frankenphp)$";
let Tools = dynamic(["sh", "bash", "dash", "zsh", "busybox", "curl", "wget", "python", "python3", "perl", "ruby", "nc", "ncat", "netcat", "socat", "base64", "whoami", "id", "uname", "cmd.exe", "powershell.exe", "pwsh.exe", "certutil.exe", "whoami.exe"]);
// Add command lines your applications legitimately run (a mail or image tool, for example) after review.
let KnownGood = dynamic(["REPLACE_WITH_EXPECTED_COMMAND_LINE"]);
DeviceProcessEvents
| where InitiatingProcessFileName matches regex WebServer
| where FileName in~ (Tools)
| where not(ProcessCommandLine has_any (KnownGood))
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Runs = count(), Commands = make_set(ProcessCommandLine, 20) by DeviceName, AccountName, InitiatingProcessFileName, FileName
| extend CommandLine = tostring(Commands[0])
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- entityType: Process
fieldMappings:
- identifier: CommandLine
columnName: CommandLine
eventGroupingSettings:
aggregationKind: AlertPerResult
version: 1.0.0
kind: Scheduled
04. Web server writes PHP or ini files. How web shells and auto_prepend_file persistence are planted. A CMS update run from the browser also writes PHP files, usually hundreds at once; a few new files in an uploads folder is the case to chase.
id: 451d8a12-b30b-578f-aa74-3255c881ef46
name: PHP2Shell - Web server writes PHP or ini files
description: |
'A PHP worker or web server, or a process it started, created or changed a PHP script, .user.ini, .htaccess or php.ini. This is how web shells and auto_prepend_file persistence are planted. CMS updates write many files at once; review those.'
severity: Medium
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceFileEvents
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1505.003
query: |
// PHP2Shell persistence: web shells and auto_prepend_file settings written by the web server or its children.
// A CMS update run from the browser also writes PHP files: a large Files count in one burst is usually an update.
let WebServer = @"(?i)^(php(-fpm|-cgi)?[0-9.]*(\.exe)?|apache2|httpd(\.exe)?|lsphp|frankenphp)$";
let ScriptExtensions = dynamic([".php", ".phtml", ".phar", ".php5", ".php7", ".pht", ".inc"]);
DeviceFileEvents
| where ActionType in ("FileCreated", "FileModified", "FileRenamed")
| where InitiatingProcessFileName matches regex WebServer or InitiatingProcessParentFileName matches regex WebServer
| extend Extension = tolower(extract(@"(\.[A-Za-z0-9]+)$", 1, FileName))
| where Extension in (ScriptExtensions) or FileName in~ (".user.ini", ".htaccess", "php.ini")
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Files = dcount(FileName), Names = make_set(FileName, 20), Folders = make_set(FolderPath, 10) by DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName
| extend FileName = tostring(Names[0]), FolderPath = tostring(Folders[0])
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- entityType: Account
fieldMappings:
- identifier: Name
columnName: InitiatingProcessAccountName
- entityType: File
fieldMappings:
- identifier: Name
columnName: FileName
- identifier: Directory
columnName: FolderPath
eventGroupingSettings:
aggregationKind: AlertPerResult
version: 1.0.0
kind: Scheduled
05. Web server reaches a new destination. Compares the last hour with the previous 14 days, so it learns each server's normal partners. Second-stage downloads, reverse shells and miners show up as new public addresses.
id: 15a8f236-3b55-55e9-afd0-be581a551778
name: PHP2Shell - Web server reaches a new destination
description: |
'The web server, a PHP worker or a process they started connected to a public address it had not contacted in the previous 14 days: second-stage downloads, reverse shells and miners. Applications calling new APIs will also match.'
severity: Medium
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceNetworkEvents
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1105
- T1071.001
query: |
// PHP2Shell follow-on traffic: outbound connections from the web server tree to public addresses not seen in the last 14 days.
let Recent = 1h;
let WebServer = @"(?i)^(php(-fpm|-cgi)?[0-9.]*(\.exe)?|apache2|httpd(\.exe)?|lsphp|frankenphp)$";
let Outbound = dynamic(["ConnectionSuccess", "ConnectionAttempt", "ConnectionRequest"]);
let Known = DeviceNetworkEvents
| where TimeGenerated < ago(Recent)
| where ActionType in (Outbound) and RemoteIPType == "Public"
| where InitiatingProcessFileName matches regex WebServer or InitiatingProcessParentFileName matches regex WebServer
| distinct DeviceId, RemoteIP;
DeviceNetworkEvents
| where TimeGenerated >= ago(Recent)
| where ActionType in (Outbound) and RemoteIPType == "Public"
| where InitiatingProcessFileName matches regex WebServer or InitiatingProcessParentFileName matches regex WebServer
| join kind=leftanti Known on DeviceId, RemoteIP
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Connections = count(), Ports = make_set(RemotePort, 10), Urls = make_set(RemoteUrl, 10) by DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, RemoteIP
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- entityType: Account
fieldMappings:
- identifier: Name
columnName: InitiatingProcessAccountName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: RemoteIP
eventGroupingSettings:
aggregationKind: AlertPerResult
version: 1.0.0
kind: Scheduled
06. Persistence by the web server account. The steps attackers take so that patching does not remove them: cron jobs, systemd units, SSH keys and new accounts, made by the web server account or a process it started.
id: 6bbdb4a8-2890-5061-ac24-5bca80d57c40
name: PHP2Shell - Persistence by the web server account
description: |
'The web server account, or a process started by the web server, edited cron, systemd units or SSH keys, or ran crontab, useradd or ssh-keygen. Attackers use these to survive patching. Add your site accounts to WebAccounts.'
severity: High
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceProcessEvents
- DeviceFileEvents
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1053.003
- T1098.004
- T1543.002
query: |
// PHP2Shell persistence that survives the patch: cron, systemd and SSH keys changed from the web server's side.
let WebServer = @"(?i)^(php(-fpm|-cgi)?[0-9.]*(\.exe)?|apache2|httpd(\.exe)?|lsphp|frankenphp)$";
let WebAccounts = dynamic(["www-data", "apache", "nginx", "httpd", "REPLACE_WITH_SITE_ACCOUNT"]);
let Processes = DeviceProcessEvents
| where AccountName in~ (WebAccounts) or InitiatingProcessFileName matches regex WebServer or InitiatingProcessParentFileName matches regex WebServer
| where FileName in~ ("crontab", "at", "ssh-keygen", "useradd", "usermod", "systemctl")
| project TimeGenerated, DeviceName, AccountName, Activity = strcat("ran ", FileName), Detail = ProcessCommandLine;
let Files = DeviceFileEvents
| where ActionType in ("FileCreated", "FileModified", "FileRenamed")
| where InitiatingProcessAccountName in~ (WebAccounts) or InitiatingProcessFileName matches regex WebServer or InitiatingProcessParentFileName matches regex WebServer
| where FolderPath has_any ("/var/spool/cron", "/etc/cron", "/etc/systemd/system", "/.ssh/") or FileName =~ "authorized_keys"
| project TimeGenerated, DeviceName, AccountName = InitiatingProcessAccountName, Activity = strcat("wrote ", FileName), Detail = FolderPath;
union Processes, Files
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Activities = make_set(Activity, 10), Details = make_set(Detail, 10) by DeviceName, AccountName
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
eventGroupingSettings:
aggregationKind: AlertPerResult
version: 1.0.0
kind: Scheduled
07. Startup memory fatal, then a shell. Joins rules 01 and 03 on the same host within 30 minutes: an attempt that worked. It fires rarely and should go straight to incident response.
id: 041b9e86-07b8-584e-9e39-731fe3660f9e
name: PHP2Shell - Startup memory fatal then shell
description: |
'On the same server, a PHP startup memory fatal (rule 01) was followed within 30 minutes by the web server starting a shell or tool (rule 03): an exploit attempt that worked. Matches hosts on their short name.'
severity: High
requiredDataConnectors:
- connectorId: SyslogAma
dataTypes:
- Syslog
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceProcessEvents
queryFrequency: 30m
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- Execution
relevantTechniques:
- T1190
- T1059.004
query: |
// PHP2Shell, attempt then execution: the error-log signal and a web server shell on the same host within 30 minutes.
// Syslog and Defender name hosts differently, so both sides are matched on the short host name.
let Window = 30m;
let WebServer = @"(?i)^(php(-fpm|-cgi)?[0-9.]*(\.exe)?|apache2|httpd(\.exe)?|lsphp|frankenphp)$";
let Tools = dynamic(["sh", "bash", "dash", "zsh", "busybox", "curl", "wget", "python", "python3", "perl", "ruby", "nc", "ncat", "netcat", "socat", "base64", "whoami", "id", "uname", "cmd.exe", "powershell.exe", "pwsh.exe", "certutil.exe", "whoami.exe"]);
let Signals = Syslog
| where SyslogMessage has "Allowed memory size" and SyslogMessage contains "in Unknown on line 0"
| extend Host = tolower(tostring(split(Computer, ".")[0]))
| summarize SignalTime = min(TimeGenerated), Signals = count() by Host;
DeviceProcessEvents
| where InitiatingProcessFileName matches regex WebServer
| where FileName in~ (Tools)
| extend Host = tolower(tostring(split(DeviceName, ".")[0]))
| join kind=inner Signals on Host
| where TimeGenerated between (SignalTime .. (SignalTime + Window))
| summarize StartTime = min(SignalTime), EndTime = max(TimeGenerated), Signals = max(Signals), Commands = make_set(ProcessCommandLine, 10) by DeviceName, AccountName
| extend CommandLine = tostring(Commands[0])
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- entityType: Process
fieldMappings:
- identifier: CommandLine
columnName: CommandLine
eventGroupingSettings:
aggregationKind: AlertPerResult
version: 1.0.0
kind: Scheduled
Defender XDR: which servers still need the fix. Run this in Defender XDR advanced hunting; Microsoft does not ingest the vulnerability tables into Sentinel. It compares each PHP version Defender has found with the fixed release for its branch. Once Microsoft maps the two CVEs, DeviceTvmSoftwareVulnerabilities filtered on the CVE IDs gives the same answer directly.
// Microsoft Defender XDR advanced hunting, not Sentinel: Defender Vulnerability Management tables are not ingested into Sentinel.
// PHP found by Defender Vulnerability Management, by branch, against the fixed releases of 21 October 2026.
// On Linux the inventory covers PHP installed with the package manager, not PHP built from source or inside containers.
// Distributions often fix PHP without changing the version number: check "below the fixed release" rows against your distribution's advisory.
let Fixed = datatable(Branch: string, FixedVersion: string) ["8.5", "8.5.12", "8.4", "8.4.27", "8.3", "8.3.36", "8.2", "8.2.35"];
DeviceTvmSoftwareInventory
| where SoftwareName has "php"
| extend Numeric = extract(@"^(\d+\.\d+\.\d+)", 1, SoftwareVersion)
| extend Branch = extract(@"^(\d+\.\d+)", 1, Numeric)
| join kind=leftouter Fixed on Branch
| extend Status = case(isempty(Numeric), "Version not recognised", isempty(FixedVersion), "No official fix: PHP 8.1 or older", parse_version(Numeric) < parse_version(FixedVersion), "Below the fixed release", "At or above the fixed release")
| summarize Devices = dcount(DeviceId), Examples = make_set(DeviceName, 10) by SoftwareVendor, SoftwareName, SoftwareVersion, Status
| order by Status asc, Devices desc
CrowdStrike Falcon Next-Gen SIEM rules
01. Web server starts a shell or tool. The same logic as Sentinel rule 03, on ProcessRollup2.
// Illustrative; not run against live data. CrowdStrike Falcon Next-Gen SIEM (CQL): save as a correlation rule or run in Event Search.
// PHP2Shell post-exploitation: a shell, download tool or reconnaissance command started by a PHP worker or web server.
// Schedule every 15 minutes over the last 20. Events carry the sensor ID (aid): map it to your host inventory.
#event_simpleName=ProcessRollup2
| ParentBaseFileName=/^(php(-fpm|-cgi)?[0-9.]*(\.exe)?|apache2|httpd(\.exe)?|lsphp|frankenphp)$/i
| FileName=/^(sh|bash|dash|zsh|busybox|curl|wget|python3?|perl|ruby|nc|ncat|netcat|socat|base64|whoami|id|uname|cmd\.exe|powershell\.exe|pwsh\.exe|certutil\.exe|whoami\.exe)$/i
| CommandLine!=/REPLACE_WITH_EXPECTED_COMMAND_LINE/
| groupBy([aid, UserName, ParentBaseFileName, FileName], function=[count(as=Runs), collect([CommandLine]), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)])
02. Web server writes a PHP script. NewScriptWritten records the file and the process that wrote it; CrowdStrike's own example of a web shell is a cache.php written by apache2 into an uploads folder.
// Illustrative; not run against live data. CrowdStrike Falcon Next-Gen SIEM (CQL): save as a correlation rule or run in Event Search.
// PHP2Shell persistence: a PHP script written to disk by the web server (NewScriptWritten, Linux sensor).
// A CMS update run from the browser writes many files at once; a handful of new files in an uploads folder is the case to chase.
#event_simpleName=NewScriptWritten
| ContextBaseFileName=/^(php(-fpm|-cgi)?[0-9.]*(\.exe)?|apache2|httpd(\.exe)?|lsphp|frankenphp)$/i
| FileName=/\.(php[0-9]?|phtml|phar|pht|inc)$/i
| groupBy([aid, ContextBaseFileName, FilePath], function=[count(as=Files), collect([FileName]), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)])
03. PHP runs a script from an uploads or cache folder. PhpExecuteScript records every PHP file executed. Legitimate applications rarely run code from upload, cache or temporary folders; web shells usually do.
// Illustrative; not run against live data. CrowdStrike Falcon Next-Gen SIEM (CQL): save as a correlation rule or run in Event Search.
// PHP2Shell web shell in use: PHP executing a script from a folder meant for uploads, caches or temporary files.
// Needs the Linux sensor's PHP visibility (PhpExecuteScript). Add your application's legitimate cache paths to the exclusion.
#event_simpleName=PhpExecuteScript
| TargetFileName=/\/(uploads?|tmp|temp|cache|images?|files|media)\//i
| TargetFileName!=/REPLACE_WITH_LEGITIMATE_CACHE_PATH/
| groupBy([aid, TargetFileName], function=[count(as=Runs), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)])
04. Outbound connections from the web server's processes. Joins network connections to the processes that made them. Long-running PHP workers started before the search window are missed; widen the window to 24 hours to include them.
// Illustrative; not run against live data. CrowdStrike Falcon Next-Gen SIEM (CQL): save as a correlation rule or run in Event Search.
// PHP2Shell follow-on traffic: public connections from processes started by the web server, and from workers started in the search window.
// Long-running PHP workers started before the window are missed; widen the window to 24 hours to include them.
#event_simpleName=NetworkConnectIP4
| cidr(field=RemoteAddressIP4, subnet=["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8", "169.254.0.0/16", "100.64.0.0/10"], negate=true)
| join({#event_simpleName=ProcessRollup2 | FileName=/^(php(-fpm|-cgi)?[0-9.]*(\.exe)?|apache2|httpd(\.exe)?|lsphp|frankenphp)$/i or ParentBaseFileName=/^(php(-fpm|-cgi)?[0-9.]*(\.exe)?|apache2|httpd(\.exe)?|lsphp|frankenphp)$/i}, field=[aid, ContextProcessId], key=[aid, TargetProcessId], include=[FileName, ParentBaseFileName, CommandLine])
| groupBy([aid, ParentBaseFileName, FileName, RemoteAddressIP4, RemotePort], function=[count(as=Connections), collect([CommandLine]), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)])
05. PHP memory fatal at request startup. The error-log signal, for web server logs sent to Next-Gen SIEM.
// Illustrative; not run against live data. CrowdStrike Falcon Next-Gen SIEM (CQL): save as a correlation rule or run in Event Search.
// PHP2Shell (CVE-2026-109618): the memory fatal raised before any script runs, in web server error logs ingested as third-party data.
// host.name follows the CrowdStrike Parsing Standard; change it if your parser names the host differently.
"Allowed memory size" "in Unknown on line 0"
| groupBy([host.name], function=[count(as=Events), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)])
06. Persistence by the web server account. The same logic as Sentinel rule 06, on process events.
// Illustrative; not run against live data. CrowdStrike Falcon Next-Gen SIEM (CQL): save as a correlation rule or run in Event Search.
// PHP2Shell persistence that survives the patch: cron, SSH keys, accounts and services changed by the web server account or its children.
#event_simpleName=ProcessRollup2
| UserName=/^(www-data|apache|nginx|httpd|REPLACE_WITH_SITE_ACCOUNT)$/i or ParentBaseFileName=/^(php(-fpm|-cgi)?[0-9.]*(\.exe)?|apache2|httpd(\.exe)?|lsphp|frankenphp)$/i
| FileName=/^(crontab|at|ssh-keygen|useradd|usermod|systemctl)$/i
| groupBy([aid, UserName, ParentBaseFileName, FileName], function=[count(as=Runs), collect([CommandLine]), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)])
Download the rules: Sentinel 01 · 02 · 03 · 04 · 05 · 06 · 07 · Defender inventory KQL · CrowdStrike 01 · 02 · 03 · 04 · 05 · 06.
When a rule fires. Treat rules 03, 06 and 07 as a likely compromise: isolate the server, keep its memory and logs before you rebuild it, check rule 04 and 06 results for anything planted, and rotate every secret in the server's configuration files. Rules 01 and 02 on a patched server mean someone is trying; on an unpatched one, patch or take it offline now. No alert does not mean no attacker: these rules see only what your error logs and EDR record.
Copy-paste templates
Replace everything in [square brackets]. Have Legal or Privacy review anything that leaves the company.
Questions for your hosting company, web agency or product vendor
Subject: PHP CVE-2026-109618 and CVE-2026-109619 ("PHP2Shell"): status of [site / product name]
PHP released fixes for two critical remote code execution flaws on 21 October 2026 (8.5.12, 8.4.27, 8.3.36, 8.2.35). The National Cybersecurity Office and the Prime Minister asked site owners to apply them immediately. Please answer by [date and time]:
1. Which PHP version and runtime (PHP-FPM, mod_php, CGI, LiteSpeed, other) does [site / product] use?
2. Has the fixed version, or your distribution's fixed package, been installed? Date and time. Were all PHP workers restarted?
3. If not yet: which interim measures are in place (session.upload_progress.enabled = 0, WAF rules, upload limits), and when will it be fixed?
4. Is any part on PHP 8.1 or older? If so, what is the plan?
5. Have you checked logs from 20 October onwards for signs of exploitation (memory errors "in Unknown on line 0", worker crashes, new PHP files)? Result?
6. Who is our contact if you find anything, and how quickly will you tell us?
Notice to customers when you pause a site as a precaution
[Service name]: temporary suspension as a security precaution
A serious vulnerability has been found in PHP, a programming language used by many websites, including ours. Following the advice of the National Cybersecurity Office, we have temporarily suspended [service / pages] from [time] on [date] while we apply the fix.
We have found no unauthorised access or leak of customer information. You do not need to do anything. We will restart the service once the fix and our checks are complete, expected by [time / date], and will update this notice.
Contact: [contact details]
Five-line briefing for management
1. What happened: on 21 October 2026 PHP fixed two critical flaws that let attackers run code on web servers without logging in (CVE-2026-109618, CVE-2026-109619). The Prime Minister and the National Cybersecurity Office asked site owners to patch that night, or suspend sites that could not be patched.
2. Our exposure: [number] internet-facing systems run PHP; [number] patched by [time]; [number] on PHP 8.1 or older with no official fix.
3. Interim measures: [settings / WAF rules / sites suspended, with times].
4. Checks: logs reviewed from 20 October on [number] systems; findings [none / under investigation]. Vendors asked: [number], answered: [number].
5. Decisions needed: [e.g. keep [site] offline until [date]; budget to upgrade end-of-life PHP]. Next update: [date and time].
What remains unknown
- How widely the flaws are exploited. Cloudflare saw attack traffic; NCSC-NL and Huntress had no confirmed exploitation on 21 October. No confirmed indicators had been published.
- Which old versions are affected. PHP's advisories cover the supported branches; reports that PHP 5 is affected rest on CERT-EU and researchers, not on PHP.
- How many Japanese servers are exploitable. Our count is of servers that announce PHP, before the release. Hidden headers, backported packages and the memory configuration all change the real number.
- Whether the shutdowns prevented anything. No organisation had reported damage when we checked.
Sources and updates
- Sanae Takaichi on X (Japanese), 21 October 2026, 21:55 JST
- National Cybersecurity Office (@cyber_forecast) on X (Japanese), 21 October 2026, 21:44 JST
- Yuichiro Tamaki on X (Japanese), 21 October 2026, 22:20 JST
- The PHP Foundation on X: advance notice, 20 October 2026 (UTC) · php.announce: Upcoming Critical PHP Security Release · oss-security, 20 October 2026
- PHP advisory GHSA-w8hw-w9fx-j79w: RCE via OOM corruption (CVE-2026-109618)
- PHP advisory GHSA-6wmf-4rv5-p3rp: RCE via stale global state (CVE-2026-109619)
- The PHP Foundation: Today's PHP security release, 21 October 2026
- IPA: request to prepare for the PHP security release (Japanese), 21 October 2026
- Cloudflare: protections against php2shell, 21 October 2026
- CrowdStrike: protecting customers from PHP2Shell
- NCSC-NL: php2shell operational information
- Sansec: PHP2Shell · Huntress: rapid response · watchTowr: FAQ
- The Next Web: CERT-EU says unpatched PHP sites may need to go offline
- Corelight: Zeek and Suricata detections
- Nikkei: the Prime Minister's post (Japanese) · Impress Watch: emergency maintenance across major services (Japanese) · ITmedia NEWS: fixed versions released (Japanese)
- Nikkei: Shizuoka Bank (Japanese) · Nikkei: 77 Bank (Japanese) · Niigata Nippo: Daishi Hokuetsu Bank (Japanese) · Nikkei: Sakura Internet (Japanese) · Game Watch: Cygames (Japanese)
- W3Techs: usage statistics of PHP
- Cisco Talos: attacks on Japanese organisations through CVE-2024-4577
- CISA: Known Exploited Vulnerabilities catalogue
- Microsoft Learn: DeviceProcessEvents · Syslog table
- Microsoft Learn: analytics rule format for Microsoft Sentinel · DeviceFileEvents · DeviceNetworkEvents · DeviceTvmSoftwareInventory
- CrowdStrike: advanced web shell detection and prevention on Linux
Our earlier coverage: Japan's “emergency in cyberspace” · 60-day review of Japan's breach notices · DFIR Part 2: collecting evidence.
22 October 2026, 09:50 JST: added detection rules: seven Microsoft Sentinel analytics rules, a Defender XDR query for servers that still need the fix, and six CrowdStrike Falcon CQL rules, with downloads. The KQL was analysed with Microsoft's Kusto.Language library against the documented table schemas; none of the rules was run against live data.
22 October 2026: first published. Sources were checked between 01:50 and 02:12 JST on 22 October. We could not open php.net, the PHP advisories on GitHub, or the IPA, National Cybersecurity Office and PHP Foundation pages directly from our research environment; their wording here comes from search-engine extracts and was checked against at least two reports. Times of posts on X are derived from the posts' IDs and match the times given by Impress Watch and Kyodo. We checked CISA's catalogue ourselves (version 2026.10.21). The Japan count is our own, made passively from an existing dataset; we did not scan anyone. We found no JPCERT/CC alert on these flaws at the time of writing. The screenshot in Figure 1 was supplied by a reader and is shown to report on and quote a public statement by the Prime Minister; we removed the profile photo and the X logo.
