WordPress Core CVE-2026-87902 – Active LFI to Conditional Remote Code Execution

Threat Overview

WordPress Core CVE-2026-87902 is a critical path-traversal/local-file-inclusion vulnerability in page-template resolution. An unauthenticated attacker can manipulate the pagename value so get_page_template() includes a readable local PHP file outside the active theme directories. Remote code execution requires additional server and theme preconditions. Patchstack observed probing within hours of disclosure, and September 24 reporting confirms activity progressed to attempts using pearcmd.php to write attacker-controlled PHP files to disk.

References

Impacted Systems

WordPress Core 4.7.0 through 7.1.1. Fixed releases include 7.1.2, 7.0.6, 6.9.9, 6.8.10 and security backports through 4.7.37. Exploitation requires an active theme with a top-level directory beginning with page- and a readable local PHP target. Current exploitation has used pearcmd.php where present.

Why this matters

The attack is unauthenticated and exploitation began within hours of disclosure. Observed activity has progressed from harmless inclusion probes to PHP file writes. Successful exploitation can provide code execution under the web-server account.

Exploitation Status

Confirmed active exploitation attempts. Patchstack first observed probing on September 22. September 24 reporting describes attempts using /usr/local/lib/php/pearcmd.php to write PHP content to /tmp and /var/tmp. Observed names include wp-pear-rce-flag.php, poc87902.php, luci_<random>.php and zeta_<random>.php.

What this hunt looks for

Double-encoded traversal in pagename, pagename plus page_id requests, reported exploitation source IPs, pearcmd.php references, PHP files written under temporary directories, web-server child processes, and unexpected outbound network activity.

Required logs

Azure WAF or other web/proxy telemetry in CommonSecurityLog, Defender XDR DeviceProcessEvents, DeviceFileEvents and DeviceNetworkEvents where the web server is onboarded, and Linux Syslog where available.

First Pass – Double-Encoded WordPress Traversal

CommonSecurityLog
| where TimeGenerated >= ago(7d)
| where RequestURL has_any ("%252e%252e","%252E%252E")
| where RequestURL has "pagename"
| project TimeGenerated,SourceIP,DestinationIP,RequestMethod,RequestURL,DeviceAction,Message
| order by TimeGenerated desc

Known Exploitation Source IPs

CommonSecurityLog
| where TimeGenerated >= ago(7d)
| where SourceIP in ("104.194.9.227","43.250.53.42","180.251.159.243","195.178.110.247","107.189.14.87","45.61.184.170","92.246.130.76","169.58.48.193","169.58.48.195","2001:df1:e8c0::106b")
| project TimeGenerated,SourceIP,DestinationIP,DestinationPort,RequestMethod,RequestURL,DeviceAction,Message
| order by TimeGenerated desc

pagename and page_id Pairing

CommonSecurityLog
| where TimeGenerated >= ago(7d)
| where RequestURL has "pagename" and RequestURL has "page_id"
| project TimeGenerated,SourceIP,DestinationIP,RequestMethod,RequestURL,DeviceAction,Message
| order by TimeGenerated desc

pearcmd.php and Observed Payload Names

union isfuzzy=true CommonSecurityLog,Syslog
| where TimeGenerated >= ago(7d)
| where tostring(*) has_any ("pearcmd.php","wp-pear-rce-flag.php","poc87902.php","luci_","zeta_")
| project TimeGenerated,Type,Computer=tostring(column_ifexists("Computer","")),SourceIP=tostring(column_ifexists("SourceIP","")),Message=tostring(column_ifexists("Message","")),SyslogMessage=tostring(column_ifexists("SyslogMessage",""))
| order by TimeGenerated desc

PHP Files Created in Temporary Directories

DeviceFileEvents
| where Timestamp >= ago(7d)
| where FileName endswith ".php"
| where FolderPath startswith "/tmp/" or FolderPath startswith "/var/tmp/"
| project Timestamp,DeviceName,ActionType,FileName,FolderPath,SHA256,InitiatingProcessFileName,InitiatingProcessCommandLine
| order by Timestamp desc

Web Server Spawning Shells or Downloaders

DeviceProcessEvents
| where Timestamp >= ago(7d)
| where InitiatingProcessFileName in~ ("php","php-fpm","apache2","httpd","nginx")
| where FileName in~ ("sh","bash","dash","curl","wget","python","python3","perl")
| project Timestamp,DeviceName,AccountName,InitiatingProcessFileName,InitiatingProcessCommandLine,FileName,ProcessCommandLine,SHA256
| order by Timestamp desc

Web Server Outbound Connections

DeviceNetworkEvents
| where Timestamp >= ago(7d)
| where InitiatingProcessFileName in~ ("php","php-fpm","apache2","httpd","nginx")
| project Timestamp,DeviceName,InitiatingProcessFileName,InitiatingProcessCommandLine,RemoteUrl,RemoteIP,RemotePort
| order by Timestamp desc

WordPress Syslog Fallback

Syslog
| where TimeGenerated >= ago(7d)
| where SyslogMessage has_any ("%252e%252e","pearcmd.php","wp-pear-rce-flag.php","poc87902.php","luci_","zeta_")
| project TimeGenerated,Computer,HostName,ProcessName,SeverityLevel,SyslogMessage
| order by TimeGenerated desc

Detection Notes

The highest-signal web indicator is pagename containing double-encoded traversal such as %252e%252e, especially with page_id. Reported source IPs are useful pivots but are not exhaustive. Endpoint telemetry is required to see PHP file writes or post-exploitation behavior. CommonSecurityLog fields depend on the WAF/proxy parser.

Leave a comment