WordPress Core CVE-2026-87902: Page-Template Path Traversal to pearcmd Remote Code Execution

CVE-2026-87902 (unauthenticated local file inclusion via get_page_template() / pagename) · exploited in the wild since 2026-09-22, hours after the patch
Threat
CVE-2026-87902 (CISA: "WordPress Core Remote File Inclusion Vulnerability"). No vendor-assigned campaign name has been published.
Severity
EXPLOITED ITW / KEV
Type
Unauth. path traversal / LFI, conditional RCE
Access
Unauthenticated, internet-facing
Version
v0.1 · 2026-09-29
Author
HuntPack
Confidence
High (intel) / Medium (endpoint coverage)
01

Executive Summary

What the attacker wants: code execution on an internet-facing WordPress host, reached without a credential and without a single byte of user interaction. CVE-2026-87902 is a path-traversal flaw in how WordPress core resolves which template file to load for a page. The pagename request parameter feeds get_page_template(); WordPress's own sanitiser strips literal ../ sequences but deliberately preserves percent-encoded octets, so a double URL-encoded traversal payload (%252e%252e) survives sanitisation, gets decoded later, and lets the attacker point the template loader at any readable local .php file outside the active theme. On its own that is an information-disclosure-grade local file inclusion. It becomes remote code execution when two ordinary conditions both hold: the active theme ships a top-level directory whose name starts with page- (Twenty Twelve, Twenty Fourteen, and third-party themes Neve, Hestia and Sydney all qualify), and PEAR's pearcmd.php is present and readable with register_argc_argv enabled in PHP, which is the default in the official PHP Docker image and in cPanel builds on PHP below 8.5. When both hold, the query string is exposed to pearcmd.php as $argv, and its config-create directive will write attacker-controlled PHP content to an arbitrary writable path.

Why it matters operationally: the gap between patch and exploitation was measured in hours, not days. WordPress 7.1.2 (plus 7.0.6, 6.9.9, 6.8.10 and backports to every branch down to 4.7.37) shipped 2026-09-22 alongside advisory GHSA-7hp8-65ch-5whp; Patchstack logged the first probing attempt at 11:49 UTC that same day and the first successful file-write attempt at 15:34 UTC. By 2026-09-23 a named Nuclei template and dedicated scanner tooling were in public circulation, CrowdSec's virtual-patch rule went live, and traffic had moved from reconnaissance to active file-write attempts. CISA added the flaw to the Known Exploited Vulnerabilities catalog on 2026-09-25 with a federal remediation deadline of 2026-09-28. CrowdSec's network recorded 322,680 matching signals between 2026-09-23 and 2026-09-27 alone, distinct source addresses growing from 237 to 23,321 across those five days and reaching 30,813 cumulative unique IPs by 2026-09-28, while signals per source fell from roughly 63 to roughly 5, the textbook signature of an exploit moving from a handful of operators into commodity mass-scanning tooling.

The highest-value defensive angle for Falcon: not the HTTP request, the consequence of a successful chain. A Falcon sensor on the web server never sees a pagename parameter, a percent-encoded traversal sequence, or the cve-2026-87902-poc/1.0 user agent; those live in web-server, WAF and CDN logs. What the sensor sees cleanly is what happens after the include succeeds and the dropped payload runs: a web-server or PHP process (httpd, nginx, php-fpm, php-cgi, or w3wp.exe on IIS) spawning a shell or scripting interpreter. That parent-child relationship is close to zero in a healthy estate, it fires regardless of which of the reported dropped-file names appears, and it is the pack's spine. The atomic indicators (two literal dropped filenames, two scanner user agents, a handful of request markers) are useful corroboration, not the plan.

Defender priority: confirm every WordPress host you own is running 7.1.2, 7.0.6, 6.9.9, 6.8.10 or a later backport, then assume the window from 2026-09-22 to your patch time was hostile and hunt it. Disabling register_argc_argv and removing pearcmd.php breaks the RCE escalation specifically, but the underlying LFI persists until you patch. Run Q1 and Q2 (Section 8) across the last 30 days before you close this out, and do not assume auto-updates saved you. The exposure concentrates precisely on the sites where someone turned them off.

02

Source Review & Web Hunter Notes

TierSourceKey findingCarry forward
1GitHub Security Advisory GHSA-7hp8-65ch-5whp (WordPress/wordpress-develop, published by johnbillion)Primary vendor advisory. Full affected/patched version matrix per branch (7.1.0-7.1.1 through 4.7.0-4.7.36), CVSS 4.0 vector CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N (9.2 Critical), CWE-98, the two preconditions verbatim, and credit to discoverer Robert Ressl.Yes, authoritative
1NVD (nvd.nist.gov, via the NVD 2.0 REST API) and the MITRE CVE Program record (cveawg.mitre.org)Independent CVSS 3.1 scoring at 8.1 High (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H), CWE-98 confirmed, CISA SSVC data (exploitation: active, automatable: no), cisaExploitAdd: 2026-09-25, cisaActionDue: 2026-09-28, CISA-assigned name "WordPress Core Remote File Inclusion Vulnerability".Yes
1CISA Known Exploited Vulnerabilities catalog (cisa.gov, JSON feed)Confirms KEV addition date 2026-09-25, due date 2026-09-28, knownRansomwareCampaignUse: Unknown, forensicTriage: Yes.Yes
1Patchstack, "Attackers Started Probing WordPress Sites Hours After the Patch" (two-part post, updated 2026-09-23)Deepest operational detail available anywhere: the three-stage attack progression (recon → pearcmd reachability check → file write), exact pagename query-string shapes for each stage, the three pearcmd.php install paths attackers try, the dropped filenames, the two scanner user agents, payload variation (traversal depth, encoding case, GET vs POST, direct /index.php targeting), and the detection guidance this pack's native hunts are built from.Yes, primary hunt-angle source
1CrowdSec, VulnTracking Report "30,000 Addresses in Five Days" (crowdsec.net, published 2026-09-28)Quantified exploitation telemetry: 322,680 signals 2026-09-23 to 2026-09-27, daily volume 14,938 → 124,154, distinct sources 237 → 23,321, 30,813 cumulative unique IPs by 2026-09-28, signals-per-source collapsing from ~63 to ~5 (mass commoditisation signature), CrowdSec virtual-patch rule id crowdsecurity/vpatch-CVE-2026-87902, explicit statement that a matching request is an attempt, not a confirmed compromise.Yes
2The Hacker News, "Attackers Exploit WordPress CVE-2026-87902 Within Hours of Disclosure" (2026-09-24, updated with the 2026-09-25 KEV addition)Independent confirmation of the preconditions and timeline. Additionally reports a named third-party vendor (Previdian) honeypot observation: a single attacking IP and a GitHub-hosted PHP uploader used in one observed chain. See the provenance note below: this detail is carried as context only, not as a shipped indicator.Partial, see note
2SecurityWeek, "Critical WordPress Vulnerability Exploited Immediately After Disclosure"Independent corroboration of the timeline, the three-stage progression, and the theme/pearcmd preconditions, citing Patchstack directly.Yes

Conflict noted, not resolved: WordPress's own advisory scores this CVSS 4.0 9.2 Critical, where the theme and server preconditions are modelled as "Attack Requirements" and complexity stays Low. NVD and CISA score the same flaw CVSS 3.1 8.1 High, where the identical preconditions are modelled as High Attack Complexity. Same vulnerability, two scoring frameworks, two numbers. This pack treats it as critical on the basis of unauthenticated code execution and CISA KEV inclusion, and records the disagreement rather than picking a winner. Verify against your own risk register's scoring convention before quoting a single number.

Single-sourced detail, deliberately not shipped as an indicator: The Hacker News quotes Previdian's founder describing honeypot traffic from one IP address (in the New Jersey area) that chained pearcmd.php inclusion, a file write to /tmp/, and then a second inclusion of a PHP uploader hosted on GitHub. This is a real and specific report, but it is corroborated by none of the other four sources in this table, and per this pack's provenance standard nothing ships as a confirmed indicator on a single citation. The IP and the GitHub path are discussed here for analyst context only; neither appears in Section 4's IOC table, Section 8's CQL, or Section 10's machine-readable appendix.

Provenance discipline: the raw fetched text of every source above is saved alongside this file in WordPress-CVE-2026-87902-RFI-Hunt-sources/ (eight snapshots: Patchstack, CrowdSec, The Hacker News, SecurityWeek, NVD, CISA KEV, the GHSA advisory, and the MITRE CVE record). Every atomic indicator shipped in Section 10 traces to one of those snapshots. Nothing in this pack was written from memory, and no fetched page contained instructions directed at an automated agent.

03

Hunt Brief & Attack Chain

Attack chain

#StepTelemetryHunt angle
1Stage-one recon: pagename traversal pointed at a harmless core file (wp-links-opml.php, wp-includes/feed-rss2.php, wp-cron.php, wp-login.php, install.php) to confirm the inclusion is liveWeb server / WAF / CDN access logNative hunt only (Section 7 N1). NO ENDPOINT TELEMETRY
2Stage-two probe: pagename repointed at one of three known pearcmd.php install paths with +config-show appended, confirming PEAR is present and the $argv trick worksWeb server access logNative hunt only. NO ENDPOINT TELEMETRY
3Stage-three write: same path with +config-create and attacker-controlled PHP content, writing a file to /tmp or /var/tmpWeb server access log; on the host, a new file appearing outside the web rootNative hunt N1/N3; Q3 if FIM covers those paths (rare, see caveat)
4Dropped file is later reached, typically via a second LFI inclusion through the same pagename parameter, since files under /tmp are not normally web-reachable directly, and its PHP content executes inside the PHP-FPM/Apache workerNo new OS process yet; execution happens inside the existing web workerNative hunt (web log for the second inclusion request)
5If the payload calls shell_exec()/system()/proc_open(), the web-server worker spawns a shell or interpreter: the first point Falcon can see anything at allProcessRollup2 parent/childQ1, the pack's highest-fidelity signal
6Discovery: whoami, id, uname, hostnameProcessRollup2Q2
7Second-stage retrieval via curl/wget/certutil.exe/powershell.exeProcessRollup2, NetworkConnectIP4Q5
8Persistence: cron entry, systemd unit, scheduled task, or an SSH authorized_keys dropProcessRollup2Q6, Q7
9Outbound C2 or exfiltration from the compromised web-tier hostNetworkConnectIP4Q8

Hunt hypotheses (ordered by fidelity)

#HypothesisMITREFalcon eventsExpected FP sourcesConf
H1A web-server or PHP process on a WordPress host spawned an interactive shell or interpreter shortly after the flaw's disclosure window: the execution moment of the pearcmd-written payloadT1190, T1505.003, T1059.004, T1059.003ProcessRollup2 / SyntheticProcessRollup2CMS cron/health-check scripts, backup plugins, WP-CLI wrappers, deploy hooks legitimately shelling out as the web userHIGH
H2Host and user discovery commands ran under a web-server process tree in the same windowT1033, T1082ProcessRollup2Monitoring agents and container health checks running as the web userHIGH
H3A web-server process launched a download utility with a URL in the command line, pulling a second stageT1105ProcessRollup2Plugin/theme update routines, composer/npm installs run as the web userHIGH
H4Persistence was established from a web-server process tree (cron, systemd, scheduled task) or an SSH key was written/modified from that treeT1053.003, T1053.005, T1098.004ProcessRollup2Shared-hosting control panels that register cron as the web userMEDIUM
H5A new PHP file was written outside the theme directory or under /tmp, /var/tmp by a web-server processT1505.003CriticalFileModified (FIM-dependent)Legitimate temp-file usage by session handlers, caching layers, upload processingMEDIUM
H6The exact reported dropped filenames (wp-pear-rce-flag.php, poc87902.php, or the luci_/zeta_ random-prefix pattern) appear in file-write telemetryT1505.003CriticalFileModified, NewExecutableWritten, ELFFileWrittenNone expected: these are scanner-generated proof-of-execution markers, not benign filenamesHIGH
H7A WordPress web-server host made an outbound connection to a destination outside its normal CDN/package-mirror allowlist shortly after a Q1 hitT1071.001, T1105NetworkConnectIP4Legitimate plugin/theme update traffic, license-check callbacks, analytics beaconsLOW-MEDIUM
H8A rare, never-before-seen child process appeared under a web-server parent across the estateT1190, T1505.003ProcessRollup2High by design: this is a stack-ranking hunt, not an alertMEDIUM
04

Consolidated IOC Table

TypeValueConfActionContextExpiry
filenamewp-pear-rce-flag.phpHIGHhuntDropped stage-three artifact in /tmp or /var/tmp (Patchstack, The Hacker News). Not a Falcon Custom IOC importable type, see Section 10.2026-12-29
filenamepoc87902.phpHIGHhuntDropped stage-three artifact; harmless proof-of-execution marker (Patchstack, The Hacker News)2026-12-29
filename-patternluci_<random>.phpMEDIUMhuntRandomised dropped-filename prefix observed by Patchstack and The Hacker News. Hunted as a regex, not an exact string.2026-12-29
filename-patternzeta_<random>.phpMEDIUMhuntRandomised dropped-filename prefix (Patchstack, The Hacker News)2026-12-29
user-agentcve-2026-87902-poc/1.0HIGHdetectNamed PoC/scanner user agent (Patchstack, CrowdSec). Web/proxy logs only, invisible to the endpoint sensor.2026-12-29
user-agentnuclei-cve-2026-87902/1.0HIGHdetectNamed public Nuclei template user agent (Patchstack, CrowdSec). Web/proxy logs only.2026-12-29
uri-parampagename containing .. or %2e%2e (incl. double-encoded %252e%252e)HIGHhuntCore traversal marker (Patchstack). Query string or POST body. Web logs only.2026-12-29
uri-parampagename beginning with templates/ or another page- directory nameHIGHhuntPayload must continue a real page-* directory before it can traverse out (Patchstack). Web logs only.2026-12-29
uri-param-pairpagename and page_id present together on the site root or /index.phpHIGHhuntWithout a resolvable page_id the vulnerable code path is never reached, so this pairing rarely occurs in benign traffic (Patchstack). Web logs only.2026-12-29
uri-markerrequest containing pearcmd, +config-show or +config-createHIGHhuntStage-two/stage-three escalation markers (Patchstack, CrowdSec, The Hacker News). Web logs only.2026-12-29
uri-pathwp-links-opml.php, wp-cron.php, wp-includes/feed-rss2.php requested via pagename, with a 200 response returning OPML or RSS contentMEDIUMhuntStage-one confirmation signal: the response body proves the inclusion ran (Patchstack). Web logs only.2026-12-29
rule-idcrowdsecurity/vpatch-CVE-2026-87902INFOcontrolCrowdSec AppSec virtual-patch rule name (CrowdSec). This is a mitigating control, not an indicator of compromise.n/a

Read the Action column before you build anything from this table. Every row here is request-level or filesystem-level, not a hash or a network indicator, and none of it appears in Falcon endpoint telemetry directly. No malware hashes and no payload-hosting infrastructure have been confirmed across two or more of this pack's sources as of 2026-09-29 (see the single-sourced note in Section 2), so nothing here ships as a Falcon Custom IOC Management row. The commodity attacking-IP set CrowdSec measured (30,813 addresses and rising) is deliberately not listed as a blocklist: at that scale and turnover rate a static IP list is stale before it is imported, and blocking by source address teaches nothing about whether a given host was actually compromised.

05

Affected Surface & Telemetry Matrix

SurfaceRequired telemetryPriorityGap risk
Linux WordPress hosts (Apache/nginx + php-fpm)Falcon sensor, ProcessRollup2 with full parent lineageCRITICALLow if the sensor is installed. This is where Q1/Q2/Q5/Q6/Q7 land.
Windows WordPress hosts (IIS + w3wp.exe / php-cgi.exe)Falcon sensor, ProcessRollup2HIGHLow. Rarer deployment for WordPress specifically, identical detection logic; every query in Section 8 includes the Windows web-server and shell binaries alongside the Linux ones.
Containerised WordPress (Docker / Kubernetes)Falcon Container sensor or node-level sensor with container visibilityHIGHGAP The official PHP Docker image ships with register_argc_argv enabled by default, per the vendor advisory, which makes containerised deployments disproportionately likely to hit the full RCE chain. A node-only sensor may lose pod attribution. Verify before trusting Q1 results here.
Managed / third-party WordPress hostingNone, no sensor deployableHIGHGAP Entirely dependent on the provider. Request patch attestation and access logs for the window from 2026-09-22 onward.
WAF / CDN / reverse proxy edgeHTTP access logs shipped to NG-SIEM or a log platformCRITICALPARTIAL This is the only place the pagename traversal, the pearcmd markers and the two scanner user agents are visible. If these logs are not centralised, chain steps 1 through 3 are undetectable.
Filesystem outside the web root (/tmp, /var/tmp)FileVantage/FIM policy explicitly extended to cover these paths, or scheduled host-side scanningHIGHGAP Falcon emits no generic write event for a PHP text file, and FIM policies covering /tmp specifically are unusual (most estates scope FIM to the web root, if at all). Without it, Q3 and Q4 return nothing regardless of compromise, and the host-side find in Section 7 N3 is the only coverage.
PHP runtime configuration (register_argc_argv, pearcmd.php presence)Configuration management / asset inventory, not Falcon telemetryCRITICALGAP Whether a given host is escalatable to RCE at all depends entirely on this setting and this file's presence, and Falcon has no native visibility into php.ini. Section 10's audit block covers this.
06

ATT&CK Mapping

TacticTechniqueObserved behaviourQuery / control
ReconnaissanceT1595.002 : Active Scanning: Vulnerability ScanningStage-one probes against harmless core files to confirm the inclusion is live, at scale via a named Nuclei templateNative hunt N1/N2. NO ENDPOINT TELEMETRY
Initial AccessT1190 : Exploit Public-Facing ApplicationUnauthenticated pagename path traversal reaching get_page_template(), escalated through pearcmd.phpNative hunt N1/N2; controls H1, H2
ExecutionT1059.004 : Unix Shellphp-fpm/httpd/apache2/nginx spawning sh or bash once the dropped payload runsQ1
ExecutionT1059.003 : Windows Command Shellw3wp.exe or php-cgi.exe spawning cmd.exe or powershell.exe on IIS-hosted WordPressQ1
PersistenceT1505.003 : Server Software Component: Web ShellAttacker-controlled PHP written via pearcmd config-create to /tmp or /var/tmpQ3, Q4; native hunt N3
PersistenceT1053.003 / T1053.005 : Cron / Scheduled TaskScheduled execution created from the web-server process treeQ6
PersistenceT1098.004 : Account Manipulation: SSH Authorized KeysSSH key material written or authorized_keys modified from a web-server process treeQ7
Defense EvasionT1027 : Obfuscated Files or InformationPatchstack reports one payload family assembling its marker string via chained chr() calls rather than a literal stringNative hunt N3 (grep for the resulting marker or for chr( chains in dropped PHP)
DiscoveryT1082 / T1033 : System & Owner/User Discoveryid, whoami, uname, hostname under a web-server parentQ2
Command and ControlT1105 : Ingress Tool Transfercurl/wget/certutil.exe/powershell.exe pulling a second stage from a web-server parentQ5
Command and ControlT1071.001 : Web ProtocolsOutbound connections from the compromised web-tier host to non-CDN, non-mirror destinationsQ8
Discovery (of the estate)T1190 (stack-rank)Rare, never-before-seen children of web-server parents across the fleet, catching payload families with no published nameQ9
07

Native Audit-Log Hunts (non-CQL)

Why this section exists. Steps 1 through 3 of the attack chain (recon, pearcmd reachability check, file write) leave no endpoint trace at all. They live entirely in web-server, WAF and CDN logs. An EDR sensor cannot see a pagename parameter. Treat the checks below as first-class hunt work, not as an appendix to the CQL, and note explicitly: a request that never got past the theme-directory precondition leaves no artifact anywhere, on the host or in a response body that differs from a normal 404. Absence of a hit here is not proof the host was never probed.

N1 · Web-server access logs, exploitation attempts and successes

Run on each WordPress host, or against centralised HTTP logs. Cover 2026-09-21 onward, or from the last known-good backup if earlier.

# The two published exploitation/scanner user agents
grep -aEi 'cve-2026-87902-poc|nuclei-cve-2026-87902' /var/log/nginx/access.log* /var/log/apache2/access.log*

# Core traversal marker in the pagename parameter, query string or POST body,
# single or double URL-encoded
grep -aEi 'pagename=[^ ]*(%2e%2e|%252e%252e|\.\.)' /var/log/nginx/access.log* /var/log/apache2/access.log*

# pearcmd escalation markers -- stage two (reachability) and stage three (write)
grep -aEi 'pearcmd|\+config-show|\+config-create' /var/log/nginx/access.log* /var/log/apache2/access.log*

# pagename and page_id co-occurring at the site root or /index.php -- the pairing
# needed to actually reach the vulnerable code path
grep -aEi '(\?|&)page_id=[0-9]+.*pagename=|pagename=.*(\?|&)page_id=[0-9]+' /var/log/nginx/access.log* /var/log/apache2/access.log*

# Stage-one confirmation: a 200 response to a pagename-driven request for a core
# file that should never render standalone
awk '$0 ~ /pagename=/ && ($9 == 200)' /var/log/nginx/access.log | grep -aEi 'wp-links-opml|feed-rss2|wp-cron'

Read the response body, not just the status code, on that last check. Per Patchstack, a 200 response to a pagename-driven request for wp-links-opml.php or wp-includes/feed-rss2.php that actually returns OPML or RSS content is the confirmation the inclusion ran on that host. That is a materially stronger finding than a blocked or 404'd probe, and it means the exposure window should be treated as a confirmed vulnerable period, not merely a scanned one.

N2 · WAF / CDN edge

  • Confirm a block rule exists for the pagename traversal pattern in both the query string and the POST body. WordPress reads pagename from the POST body in preference to the query string, and Patchstack reports POST has overtaken GET as the more common delivery method.
  • If you run CrowdSec, confirm the crowdsecurity/vpatch-CVE-2026-87902 AppSec rule is enabled and set to block, not log.
  • Confirm the rule matches traversal depth from one to twelve levels and both single and double percent-encoding (including uppercase hex), per the payload variation Patchstack has observed. A rule anchored to one exact depth or encoding will miss most of the traffic.

N3 · Filesystem outside the web root, dropped-payload hunt

# Any PHP file in /tmp or /var/tmp created or modified since the day before disclosure
find /tmp /var/tmp -maxdepth 2 -name '*.php' -newermt '2026-09-21' -printf '%T+ %s %p\n' 2>/dev/null | sort

# The two exact reported dropped filenames, anywhere under /tmp or /var/tmp
find /tmp /var/tmp -maxdepth 2 \( -name 'wp-pear-rce-flag.php' -o -name 'poc87902.php' \) 2>/dev/null

# The randomised-prefix pattern
find /tmp /var/tmp -maxdepth 2 -regextype posix-extended -regex '.*/(luci_|zeta_)[A-Za-z0-9]+\.php' 2>/dev/null

# Confirm whether pearcmd.php exists and is readable at any of the three known paths
for p in /usr/local/lib/php/pearcmd.php /usr/share/php/pearcmd.php /usr/share/pear/pearcmd.php; do
  [ -r "$p" ] && echo "READABLE: $p"
done

# Suspicious shell-out primitives, or the chr()-assembled marker pattern Patchstack
# reported, in any PHP file dropped since disclosure
grep -rlE 'shell_exec|passthru|proc_open|popen|(chr\([0-9]+\)\s*\.\s*){6,}' /tmp /var/tmp --include='*.php' 2>/dev/null

A file in /tmp is not normally reachable over the web on its own; per Patchstack this is evidence of proof of execution, not necessarily a persistent web-reachable backdoor. Treat any hit above as a confirmed compromise regardless of whether the file is externally reachable, since the same file-write primitive can be pointed at a more useful location on a second attempt.

N4 · PHP runtime and theme precondition audit

  • Check register_argc_argv in every php.ini / pool configuration in the estate. On plus a readable pearcmd.php is what turns this from an information leak into code execution.
  • Enumerate active themes for a top-level directory whose name starts with page-. Twenty Twelve, Twenty Fourteen, Neve, Hestia and Sydney are named in the advisory as meeting this condition; a custom or unlisted theme can too.
  • Confirm WordPress core version on every install. Anything below 7.1.2 on the 7.1 branch, 7.0.6 on the 7.0 branch, 6.9.9 on the 6.9 branch, 6.8.10 on the 6.8 branch, or the equivalent backported fix on any older branch down to 4.7.37, is exposed to the LFI regardless of the RCE preconditions.
08

CrowdStrike LogScale CQL Hunt Queries

Pick your tenant's cloud first. Every "Open in Falcon" button below uses this selection.

Scope note, endpoint telemetry only. None of the queries below can see an HTTP request path, a response code, or a user-agent string. Falcon's sensor observes processes, files and sockets, not the web server's request handling. Every query here therefore targets what the exploit chain does after the dropped payload executes. The request-level indicators are hunted in Section 7 against web and proxy logs. If you have HTTP logs in NG-SIEM the same markers can be queried there, but field names are parser-specific to your ingest and must be validated against a real sample row before use; this pack does not guess at them.

No query carries an in-query time filter. Set the window with the Falcon console time picker; each card records the intended lookback as a // LOOKBACK: comment. Start at 30 days and widen to cover the interval from 2026-09-22 to the moment your estate patched. Where a query targets both platforms, Linux and Windows events differ (ELFFileWritten is Linux-only, NewExecutableWritten is Windows-only, ProcessRollup2 covers both); each card's // REQUIRES line says which.

Q1 · Web-server process spawning a shell or interpreter
CONF HIGHFP MEDCOST LOW

Looks for: the execution moment of the pearcmd-written payload, a PHP interpreter or web-server worker becoming the parent of a shell. This is the single highest-value query in the pack because it fires regardless of which dropped filename appears, regardless of whether the request ever gets logged centrally, and it is the first point in the whole chain where Falcon has any visibility at all. FP: CMS cron jobs, backup and migration plugins, WP-CLI wrappers and deploy hooks all legitimately shell out as the web user; baseline by parent command line and by host before alerting.

// HUNT: Web-server or PHP process spawning a shell or interpreter (post-exploitation execution)
// MITRE: T1190, T1505.003, T1059.004, T1059.003 | CONF: high  FP: medium  COST: low
// REQUIRES: ProcessRollup2 with parent lineage. Covers both Linux (php-fpm/httpd/nginx -> sh/bash)
// REQUIRES: and Windows (w3wp.exe/php-cgi.exe -> cmd.exe/powershell.exe) web hosts in one query.
// FALSE POSITIVES: CMS cron jobs, backup/migration plugins, WP-CLI wrappers, deploy hooks
// TUNING: exclude your deploy and backup service accounts by UserName, and your CI/CD build hosts
// TUNING: by ComputerName; then re-run and treat whatever remains as a genuine lead
// LOOKBACK: 30d (set in the console time picker)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| ParentBaseFileName=/^(php|php-fpm[0-9.]*|php[0-9.]*-fpm|php-cgi|httpd|apache2|nginx|lsphp|litespeed|w3wp\.exe)$/i
| FileName=/^(sh|bash|dash|zsh|ksh|busybox|python[0-9.]*|perl|ruby|node|cmd\.exe|powershell\.exe|pwsh\.exe)$/i
| table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine, aid])
| sort(@timestamp, order=desc, limit=200)
Q2 · Discovery commands under a web-server process tree
CONF HIGHFP MEDCOST LOW

Looks for: the first thing an operator runs after the dropped payload executes: basic host and identity orientation. Grouping by host and parent turns this into a short triage list rather than an event firehose. FP: monitoring agents and container health-check scripts run these constantly as the web user; exclude the specific parent command line of your monitoring stack rather than the binary names, which are the signal.

// HUNT: Discovery commands executed under a web-server parent (post-exploitation recon)
// MITRE: T1033, T1082 | CONF: high  FP: medium  COST: low
// REQUIRES: ProcessRollup2 with parent lineage on web hosts, Linux and Windows
// FALSE POSITIVES: monitoring agents, container health checks and readiness probes running as the web user
// TUNING: exclude the ComputerName of dedicated build and monitoring boxes; exclude the UserName your
// TUNING: observability agent runs as. Do NOT exclude the binaries themselves - they are the signal.
// LOOKBACK: 30d (set in the console time picker)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| ParentBaseFileName=/^(sh|bash|dash|php|php-fpm[0-9.]*|php-cgi|httpd|apache2|nginx|w3wp\.exe)$/i
| FileName=/^(whoami|id|uname|hostname|hostnamectl|ifconfig|ip|netstat|ss|ps|env|lsb_release|systeminfo\.exe|net\.exe|net1\.exe|nslookup)$/i
| groupBy([ComputerName, UserName, ParentBaseFileName], function=[count(as=Hits), collect([FileName, CommandLine], limit=40)], limit=200)
| sort(Hits, order=desc, limit=100)
Q3 · PHP file written under /tmp or /var/tmp by a web-server process (file-integrity telemetry)
CONF MEDFP MEDCOST LOW

Looks for: the pearcmd config-create write itself. Read this caveat before trusting an empty result: Falcon emits no generic write event for a PHP text file, so this query only returns data if a FileVantage or equivalent FIM policy has been explicitly extended to cover /tmp and /var/tmp, which is unusual (most FIM policies scope to the web root, if anywhere). On an estate without that coverage this query is silent no matter how many payloads were dropped, and the host-side find in Section 7 N3 is the only real coverage of this step. FP: legitimate temp-file usage by session handlers, upload processing and caching layers is common; correlate every hit against Q1 on the same host before escalating.

// HUNT: PHP file written under /tmp or /var/tmp (pearcmd config-create artifact) via FIM telemetry
// MITRE: T1505.003 | CONF: medium  FP: medium  COST: low
// REQUIRES: CriticalFileModified - needs a FileVantage/FIM policy covering /tmp and /var/tmp, which is
// REQUIRES: atypical. Without that policy this query returns nothing even on a compromised host - see
// REQUIRES: Section 7 N3 for the host-side scan that is the real coverage of this step.
// FALSE POSITIVES: legitimate temp-file writes by session handlers, upload processing, caching layers
// TUNING: correlate every survivor against Q1 on the same ComputerName within a few minutes; a lone
// TUNING: temp-file write with no shell nearby is usually unrelated application behaviour
// LOOKBACK: 30d (set in the console time picker)
#event_simpleName=/^CriticalFileModified$/
| ParentBaseFileName=/^(php|php-fpm[0-9.]*|php-cgi|httpd|apache2|nginx|w3wp\.exe)$/i
| TargetFileName=/\/(tmp|var\/tmp)\/[^\/]+\.php$/i
| table([@timestamp, ComputerName, UserName, ParentBaseFileName, TargetFileName, aid])
| sort(@timestamp, order=desc, limit=200)
Q4 · Published dropped filename observed in file-write telemetry
CONF HIGHFP LOWCOST LOW

Looks for: the two exact reported filenames and the two randomised-prefix patterns, matched on basename only so no path separator needs escaping inside the regex. Same FIM-coverage caveat as Q3 applies; this query is a bonus check that costs nothing to run, not a substitute for the Section 7 N3 filesystem scan. FP: none expected. wp-pear-rce-flag.php and poc87902.php are scanner-generated proof-of-execution markers with no legitimate purpose; the luci_/zeta_ prefixes are similarly not a naming convention any real WordPress component uses.

// HUNT: Published CVE-2026-87902 dropped-payload filename in file-write telemetry
// MITRE: T1505.003 | CONF: high  FP: low  COST: low
// REQUIRES: CriticalFileModified - same FIM-on-/tmp caveat as Q3. Matched on basename via FileName so
// REQUIRES: no path separator appears inside the regex.
// FALSE POSITIVES: none expected - any hit is a confirmed incident
// LOOKBACK: 90d (set in the console time picker)
#event_simpleName=/^CriticalFileModified$/
| FileName=/^(wp-pear-rce-flag\.php|poc87902\.php|luci_[a-z0-9]{4,}\.php|zeta_[a-z0-9]{4,}\.php)$/i
| table([@timestamp, ComputerName, UserName, FilePath, FileName, TargetFileName, aid])
| sort(@timestamp, order=desc, limit=200)
Q5 · Download utility launched by a web-server process
CONF HIGHFP MEDCOST LOW

Looks for: second-stage retrieval, the step between "I have a one-shot marker-writing PoC" and "I have a functional implant." Requiring a URL-ish token in the command line keeps this quieter than matching the binaries alone. FP: plugin and theme update routines, composer and npm all fetch over HTTPS as the web user; exclude fetches to your package mirror and to wordpress.org rather than excluding curl outright.

// HUNT: Download utility launched by a web-server process (second-stage retrieval)
// MITRE: T1105 | CONF: high  FP: medium  COST: low
// REQUIRES: ProcessRollup2 with CommandLine, Linux and Windows
// FALSE POSITIVES: plugin/theme update routines, composer and npm installs run as the web user
// TUNING: exclude CommandLine values pointing at your internal package mirror and at wordpress.org;
// TUNING: exclude CI/CD hosts by ComputerName. Keep curl and wget in scope - narrow the destination.
// LOOKBACK: 30d (set in the console time picker)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| ParentBaseFileName=/^(php|php-fpm[0-9.]*|php-cgi|httpd|apache2|nginx|sh|bash|dash|w3wp\.exe)$/i
| FileName=/^(curl|wget|fetch|bitsadmin\.exe|certutil\.exe|powershell\.exe|pwsh\.exe|python[0-9.]*)$/i
| CommandLine=/(http:|https:|ftp:|--output|-OutFile|Invoke-WebRequest|DownloadString|DownloadFile|urlcache)/i
| table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine, aid])
| sort(@timestamp, order=desc, limit=200)
Q6 · Persistence created from a web-server process tree
CONF HIGHFP LOWCOST LOW

Looks for: an attacker converting a one-shot LFI hit into something that survives a patch and a cleanup: a cron entry, a systemd unit, a scheduled task, or a new local account. The web-server user should essentially never invoke these. A hit here is one of the strongest single signals in the pack. FP: low; some shared-hosting control panels register cron as the web user, worth confirming once per estate and documenting.

// HUNT: Persistence mechanism created from a web-server process tree
// MITRE: T1053.003, T1053.005 | CONF: high  FP: low  COST: low
// REQUIRES: ProcessRollup2 with parent lineage, Linux and Windows
// FALSE POSITIVES: rare - some shared-hosting control panels register cron jobs as the web user
// LOOKBACK: 30d (set in the console time picker)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| ParentBaseFileName=/^(php|php-fpm[0-9.]*|php-cgi|httpd|apache2|nginx|sh|bash|dash|w3wp\.exe|cmd\.exe|powershell\.exe)$/i
| FileName=/^(crontab|at|systemctl|systemd-run|schtasks\.exe|sc\.exe|reg\.exe|useradd|adduser|usermod)$/i
| table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine, aid])
| sort(@timestamp, order=desc, limit=200)
Q7 · SSH key material written or modified from a web-server process tree
CONF MEDFP LOWCOST LOW

Looks for: the specific persistence path called out for this hunt angle: an attacker appending their own public key to authorized_keys, or generating a new key pair, from a process tree rooted at the web server. This is a durable foothold that survives a WordPress-level cleanup entirely. FP: low; legitimate automation that manages SSH keys from a web-server account is rare and worth an explicit exception if found.

// HUNT: SSH key material touched from a web-server process tree (authorized_keys persistence)
// MITRE: T1098.004, T1552.004 | CONF: medium  FP: low  COST: low
// REQUIRES: ProcessRollup2 with CommandLine on Linux/Unix web hosts
// FALSE POSITIVES: rare - configuration management tooling that legitimately provisions SSH keys
// TUNING: exclude your CM tool's binary and its service account if it manages keys by design
// LOOKBACK: 30d (set in the console time picker)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| ParentBaseFileName=/^(php|php-fpm[0-9.]*|php-cgi|httpd|apache2|nginx|sh|bash|dash)$/i
| CommandLine=/(authorized_keys|ssh-keygen|\.ssh\/id_rsa|\.ssh\/id_ed25519)/i
| table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine, aid])
| sort(@timestamp, order=desc, limit=200)
Q8 · Outbound connection from a web-server process to a non-standard destination
CONF MEDFP HIGHCOST MED

Looks for: C2 or exfiltration traffic leaving the web tier directly from the server process, rather than from a spawned child. No confirmed C2 infrastructure has been published for this campaign (see Section 2), so this query cannot key on a known-bad destination; it ranks every outbound peer instead and lets rarity surface the interesting ones. FP: high by construction. A public web server legitimately reaches package mirrors, license-check endpoints, CDNs and its own database; this must never be promoted to a Scheduled Search without a maintained CDN/mirror exclusion list built from your own baseline.

// HUNT: Web-server process making outbound connections, ranked by destination rarity
// MITRE: T1071.001 | CONF: medium  FP: high  COST: medium
// REQUIRES: NetworkConnectIP4, Linux and Windows web hosts
// FALSE POSITIVES: high by design - package mirrors, license-check callbacks, CDN edges, your own DB
// TUNING: run once to baseline, then exclude your known CDN/mirror/DB destinations by RemoteAddressIP4
// TUNING: or by ASN if your pipeline enriches it; re-run and read only what is left. Never promote this
// TUNING: query to a Scheduled Search without that exclusion list built from your own baseline first.
// LOOKBACK: 30d (set in the console time picker)
#event_simpleName=/^NetworkConnectIP4$/
| ContextBaseFileName=/^(php|php-fpm[0-9.]*|php-cgi|httpd|apache2|nginx|w3wp\.exe)$/i
| !cidr(RemoteAddressIP4, subnet=["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","127.0.0.0/8"])
| groupBy([ComputerName, RemoteAddressIP4], function=[count(as=Hits), collect([RemotePort], limit=10)], limit=500)
| sort(Hits, order=asc, limit=150)
Q9 · Rare child processes of web-server parents (baseline sweep)
CONF MEDFP HIGHCOST MED

Looks for: everything Q1 and Q2 did not think to name. Instead of an allowlist of suspicious binaries this ranks every child of a web-server parent by rarity, ascending, so payloads nobody has published a signature for yet float to the top. Run it once to build the estate baseline, then re-run weekly and read only the new arrivals. FP: high by construction; this is a stack-ranking hunt, not an alert, and must never be promoted to a Scheduled Search without a hard exclusion list.

// HUNT: Rare child processes of web-server parents (catch-all for unnamed payloads)
// MITRE: T1190, T1505.003 | CONF: medium  FP: high  COST: medium
// REQUIRES: ProcessRollup2 across every web host in the estate, Linux and Windows
// FALSE POSITIVES: high by design. The common rows are your normal; the sparse tail is the signal.
// TUNING: read only rows where Hosts is 1 or 2 - a child seen fleet-wide is infrastructure, not an
// TUNING: intrusion. Once baselined, exclude your CMS cron and backup binaries by FileName. Never
// TUNING: promote this query to a Scheduled Search without that exclusion list in place.
// LOOKBACK: 30d (set in the console time picker)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| ParentBaseFileName=/^(php|php-fpm[0-9.]*|php[0-9.]*-fpm|php-cgi|httpd|apache2|nginx|lsphp|litespeed|w3wp\.exe)$/i
| groupBy([FileName], function=[count(as=Execs), count(ComputerName, distinct=true, as=Hosts), collect([CommandLine], limit=5)], limit=500)
| sort(Execs, order=asc, limit=120)
09

Custom IOA Recommendations & Alert Packaging

Custom IOA candidates

IOA namePatternExclusionsActionFrom
WEBSHELL – Web Server Spawns ShellProcess creation where the parent image is php-fpm, php, httpd, apache2, nginx, lsphp or w3wp.exe and the child image is a shell or scripting interpreterDeploy and backup service accounts; CI/CD hosts; documented control-panel helpersDetect (Critical) on WordPress host groups; start in Monitor for 7 daysQ1
WEBSHELL – Web Server Persistence AttemptParent image is a web-server or PHP process and the child image is crontab, systemctl, systemd-run, schtasks.exe, sc.exe, useradd or usermodControl panels that register cron as the web user (document per host group)Detect (High), very low volume, safe to promote quicklyQ6
WEBSHELL – SSH Key Written by Web ServerParent image is a web-server or PHP process and the command line references authorized_keys or ssh-keygenConfiguration management tooling with a documented service accountDetect (High) after one baseline cycleQ7
WEBSHELL – Web Server Retrieves Remote PayloadParent image is a web-server or PHP process, child is curl/wget/certutil.exe, command line contains a URL schemeInternal package mirror; wordpress.org update endpointsMonitor first, noisier than the other threeQ5

Not IOA candidates: Q2, Q3, Q4, Q8 and Q9 stay Investigate-only. Q2 is a volume hunt best triaged in bulk, Q3 and Q4 depend on FIM policy coverage of /tmp that varies wildly by host group, Q8 and Q9 are explicitly stack-ranking hunts with FP high by design, and none of the four should fire an alert until they have been baselined per-estate. Promoting any of them without the tuning in their cards will train analysts to ignore the whole rule family.

Scheduled Search packaging (for the promoted IOAs)

FieldValue
Scheduled Search nameDETECT - Web Server Spawns Shell (CVE-2026-87902 WordPress RFI)
DescriptionFires when a PHP interpreter or web-server worker becomes the parent of a shell or scripting interpreter. This is the execution signature of the pearcmd-written payload for this campaign and of web-to-shell chains generally, so it retains value beyond this specific CVE.
ScheduleEvery 15 minutes, 30-minute lookback with overlap
Alert subject[CRITICAL] Detection: CVE-2026-87902 webshell execution, web server spawned a shell

Alert email body (template)

WHAT FIRED
A web-server or PHP process spawned an interactive shell or scripting interpreter
on a host in the WordPress server group.

  Host          : {{ComputerName}}
  User          : {{UserName}}
  Parent        : {{ParentBaseFileName}}
  Child         : {{FileName}}
  Command line  : {{CommandLine}}
  First seen    : {{@timestamp}}

WHAT IT MEANS
A healthy web server almost never launches a shell. This parent-child pair is the
execution signature of a web shell. Given active in-the-wild exploitation of
CVE-2026-87902 (WordPress page-template path traversal to pearcmd RCE), treat this
as a probable post-exploitation event until proven otherwise.

IMMEDIATE ACTIONS
  1. Do not reboot or reimage yet, capture volatile state first.
  2. Network-contain the host in Falcon if the command line shows recon,
     credential access, or an outbound download.
  3. Confirm the WordPress version. Anything below 7.1.2 / 7.0.6 / 6.9.9 / 6.8.10
     (or the branch-matched backport) is vulnerable; treat the host as compromised.
  4. Run the Section 7 N3 filesystem hunt (/tmp, /var/tmp) on this host.
  5. Preserve the web-server access log before rotation removes the request that
     triggered the shell.

ESCALATION
  Confirmed webshell    -> incident response, severity 1
  Unexplained but clean -> hunt lead, 24h follow-up
  Matched exclusion     -> tune the rule, record the exclusion, close

L1 triage checklist

  1. Record host, user, parent, child and full command line from the detection before touching anything.
  2. Check the WordPress version on the host. A vulnerable version plus this alert equals a presumed compromise.
  3. Pivot the host into Q2, Q5, Q6 and Q7 scoped to the same ComputerName and a window of a few hours either side. A lone shell is ambiguous; a shell followed by whoami and curl is not.
  4. Run the Section 7 N3 filesystem hunt on the host: recently modified PHP in /tmp//var/tmp, the two exact filenames, and the luci_/zeta_ pattern.
  5. Grep the web-server access log for the pagename traversal marker, the pearcmd markers, and the two user agents, restricted to the hours before the alert.
  6. Check whether register_argc_argv is On and whether pearcmd.php is present and readable on the host. If both, the RCE precondition was met and this alert should be treated at higher confidence.
  7. If two or more of steps 4 to 6 return findings, escalate to incident response and move to the Section 14 containment runbook. If all are clean and the parent command line matches a documented exclusion, tune and close with the exclusion recorded.
10

Machine-Readable IOC Appendix

Falcon IOC Management CSVbulk import
type,value,action,severity,expiration,description,tags
sha256,REPLACE_WITH_SHA256_OF_YOUR_OWN_RECOVERED_DROPPED_PHP_FILE,prevent,critical,2026-12-29,CVE-2026-87902 locally recovered pearcmd-dropped PHP payload,campaign:cve-2026-87902
# No file hashes and no payload-hosting infrastructure have been published across two
# or more of this pack's sources as of 2026-09-29 (see Section 2 for the single-sourced
# exception, which is deliberately not shipped here). The two reported dropped-file
# names are literal filenames, not hashes or domains, and Falcon Custom IOC Management
# accepts only sha256, md5, domain, ipv4 and ipv6 - a filename type cannot be imported.
# They are hunted via FileName regex in Q4 and via the host-side find in Section 7 N3
# instead. If you recover a sample locally, compute its SHA256 and replace the row above.
# wp-pear-rce-flag.php
# poc87902.php
# luci_<random>.php
# zeta_<random>.php
Behavioural signaturesdurable, survives payload rotation
PROCESS LINEAGE
  php | php-fpm | php-cgi | httpd | apache2 | nginx | lsphp | w3wp.exe
    -> sh | bash | dash | zsh | busybox | python | perl | cmd.exe | powershell.exe
  same parents -> whoami | id | uname | hostname | ss | netstat
  same parents -> curl | wget | certutil.exe   (command line contains a URL)
  same parents -> crontab | systemctl | schtasks.exe | useradd
  same parents -> ssh-keygen | commands referencing authorized_keys

FILESYSTEM
  new or modified *.php under /tmp or /var/tmp (NOT the WordPress web root - this
    campaign drops outside it, unlike a typical webshell-in-plugins pattern)
  exact filenames: wp-pear-rce-flag.php | poc87902.php
  pattern filenames: luci_<random>.php | zeta_<random>.php
  marker string CVE-2026-87902-POC-OK, or an obfuscated marker assembled via
    chained chr() calls (reported form: LUCIFER-RCE-OK)
  pearcmd.php present and readable at one of three install paths:
    /usr/local/lib/php/pearcmd.php | /usr/share/php/pearcmd.php | /usr/share/pear/pearcmd.php

HTTP  (web / WAF / proxy logs only - NOT visible to the endpoint sensor)
  pagename parameter containing .. or %2e%2e, single or double encoded, in the
    query string or the POST body (POST has overtaken GET as the common method)
  pagename beginning with templates/ or another page-<name> directory
  pagename and page_id present together on the site root or /index.php
  request containing pearcmd, +config-show, or +config-create
  User-Agent contains: cve-2026-87902-poc/1.0 | nuclei-cve-2026-87902/1.0
  200 response to a pagename-driven request for wp-links-opml.php, wp-cron.php or
    wp-includes/feed-rss2.php that returns actual OPML/RSS content (confirms the
    inclusion ran, not just that it was attempted)
Named tooling & attributioncontext
CAMPAIGN   None. No vendor-assigned campaign or actor name has been published for this
           exploitation activity; reporting refers to it by CVE only. CISA's assigned
           vulnerability name is "WordPress Core Remote File Inclusion Vulnerability".
CVE        CVE-2026-87902   WordPress core page-template path traversal / LFI, conditional RCE
           CVSS 4.0: 9.2 Critical (WordPress/GHSA)   CVSS 3.1: 8.1 High (NVD/CISA) - disputed, see Section 2
           CWE-98 Remote File Inclusion (authoritative, MITRE CVE record). Patchstack additionally
           describes it as "Path Traversal leading to Local File Inclusion" (CWE-22 family behaviour).
DISCOVERY  Robert Ressl, via the WordPress HackerOne programme
DISCLOSED  2026-09-22   EXPLOITED ITW from 2026-09-22 11:49 UTC (Patchstack), first file-write attempt
           2026-09-22 15:34 UTC. KEV added 2026-09-25, federal deadline 2026-09-28.

VULNERABLE   4.7.0 - 7.1.1 across every branch (full per-branch matrix in GHSA-7hp8-65ch-5whp)
PATCHED      7.1.2 | 7.0.6 | 6.9.9 | 6.8.10 and backports on every branch down to 4.7.37

ROOT CAUSE  get_page_template() builds a template filename from the pagename request parameter.
            WordPress's sanitiser strips literal ../ but preserves percent-encoded octets; a later
            urldecode() turns a double-encoded traversal payload into a real path, and the resulting
            file is loaded with include(). The code path skips the validate_file() check used
            elsewhere in core for the same class of filename.

RCE PRECONDITIONS (both required)
  1. Active theme has a top-level directory starting with page- (Twenty Twelve, Twenty
     Fourteen, Neve, Hestia, Sydney are named; a custom theme can also qualify)
  2. A readable local .php file exists for pearcmd.php RCE: register_argc_argv On
     (default in the official PHP Docker image and cPanel PHP < 8.5) plus pearcmd.php
     present at a standard PEAR install path

ATTACK PROGRESSION (Patchstack, three stages)
  1. Recon: pagename points at a harmless core file to confirm the inclusion is live
  2. pearcmd reachability: pagename + config-show confirms PEAR present and argv works
  3. File write: pagename + config-create writes attacker PHP to /tmp or /var/tmp

SCALE (CrowdSec, 2026-09-23 to 2026-09-28)
  322,680 signals over 5 days, daily volume 14,938 -> 124,154
  distinct sources 237 -> 23,321; 30,813 cumulative unique IPs by 2026-09-28
  signals-per-source fell from ~63 to ~5 (mass scanner commoditisation)

CAVEAT  No malware hashes and no corroborated (two-plus-source) payload-hosting
        infrastructure exist for this campaign as of 2026-09-29. Treat any IOC feed
        claiming otherwise with the same scrutiny this pack applied in Section 2.
Patch & exposure auditrun first
# 1. Enumerate WordPress versions across the estate
find / -name version.php -path '*wp-includes*' 2>/dev/null \
  | while read -r f; do printf '%s ' "$f"; grep -m1 wp_version "$f"; done

# 2. With WP-CLI, per site root
wp core version --allow-root
wp core check-update --allow-root
wp theme list --fields=name,status --allow-root

# 3. Confirm the RCE preconditions on each host
php -i 2>/dev/null | grep -i register_argc_argv
for p in /usr/local/lib/php/pearcmd.php /usr/share/php/pearcmd.php /usr/share/pear/pearcmd.php; do
  [ -r "$p" ] && echo "READABLE: $p"
done
find /var/www -maxdepth 3 -type d -iname 'page-*' -path '*wp-content/themes*'

# 4. Confirm the traversal is actually blocked at the edge post-mitigation.
#    A 403/404 with NO OPML/RSS body is the pass condition.
curl -sk -o /dev/null -w '%{http_code} traversal probe\n' \
  'https://SITE/?page_id=1&pagename=templates%252f..%252f..%252fwp-links-opml'

# 5. Falcon sensor coverage - every WordPress host must have one, containers included.

Provenance: both dropped-payload filenames, both randomised-prefix patterns, both user agents, and every request marker in the behavioural block above appear verbatim in the saved source snapshots 01-patchstack.txt and 03-thehackernews.txt. The SHA256 row is a labelled placeholder because no hash has been published; it is deliberately not filled with a guess. Everything else in this appendix is a behavioural pattern, a configuration check, or context, not an atomic claim requiring its own source trace.

11

Detection Validation Gates

GateCheckPass condition
1 · Telemetry readyRun #event_simpleName=/^ProcessRollup2$/ | groupBy([ComputerName], limit=500) restricted to your WordPress host groupEvery WordPress host you believe you own appears. Any missing host is a blind spot, not a clean result; record it before proceeding.
2 · Parent lineage populatedConfirm ParentBaseFileName is populated on those hosts, not emptyQ1, Q2, Q5, Q6, Q7 and Q9 all key on the parent. If it is blank on containerised hosts, every one of them is silently useless there.
3 · FIM coverage on /tmpConfirm whether a FileVantage or equivalent policy has been extended to cover /tmp and /var/tmp, not just the web rootEither it does and Q3/Q4 are live, or it does not and both are formally recorded as a gap covered by the Section 7 N3 host scan. An untested silent query is the worst of the three outcomes.
4 · Benign baselineRun Q1 and Q9 over 30 days before alertingYou can name every recurring parent-child pair. Undocumented pairs are hunt leads, and the exclusion list for the IOAs is derived from this run, not invented up front.
5 · Positive testOn an isolated non-production WordPress host, have an authorised operator invoke a benign command through the normal PHP execution path (for example a maintenance script that calls id) and confirm Q1 and Q2 return itBoth queries return the event within the expected latency. This validates the detection path without creating or using any exploit code.
6 · Web log pathConfirm HTTP access logs from every WordPress host and from the WAF reach a searchable platformThe Section 7 N1 and N2 hunts return data. Without this, chain steps 1 through 3 are permanently invisible and the pack's coverage tops out at post-exploitation.
7 · Precondition audit completeRun the Section 10 audit block's register_argc_argv and pearcmd.php checks across the estateEvery host is classified as RCE-capable or LFI-only. This does not change detection coverage, but it re-prioritises which Q1 hits get triaged first.
8 · PromotionMove Q1, Q6 and Q7 to Scheduled Searches in Monitor mode for 7 days, then to DetectFewer than roughly 5 unexplained hits per week per 100 web hosts. Above that, tune before promoting rather than raising the threshold.
12

Hardening, Tiered

Immediate, this week, no compatibility risk

H1. Patch WordPress Core to 7.1.2, 7.0.6, 6.9.9, 6.8.10, or the matching backport on any older branch (down to 4.7.37). MITRE M1051 Update Software. This is the only complete fix for the underlying LFI; every other control on this page reduces blast radius or buys time. Verify with wp core version or the version enumeration in the Section 10 audit block, across production, staging and container images, not just the sites you remember.

H2. Reject pagename values containing a traversal sequence at the WAF or reverse proxy. MITRE M1037 Filter Network Traffic. A real page slug never contains .. or a percent-encoded dot, in either the query string or the POST body; per CIS Apache HTTP Server Benchmark and CIS Nginx Benchmark guidance on input filtering at the edge, this can be blocked with no effect on legitimate traffic. Cover both single and double encoding, uppercase and lowercase hex. Playbook P1.

H3. Disable register_argc_argv in php.ini. PHP hardening, CWE-98 mitigation. This does not fix the LFI, but it breaks the specific mechanism that turns it into code execution through pearcmd.php: without $argv populated from the query string, config-create cannot be triggered this way. Verify with php -i | grep register_argc_argv. Playbook P2.

H4. Remove or deny access to pearcmd.php. PHP hardening, attack-surface reduction. A production web server has no legitimate reason to serve PEAR's command-line tool over HTTP. Delete it, or deny it at the web-server layer, at all three commonly reported install paths. Verify with the readability check in the Section 10 audit block. Playbook P2/P3.

H5. Set open_basedir to confine PHP to the site's own webroot. PHP hardening, MITRE M1022 Restrict File and Directory Permissions. This is the single control that most directly addresses the root cause: even if get_page_template() is tricked into requesting a path outside the theme directory, open_basedir stops PHP from opening anything outside the configured tree, including pearcmd.php and anything in /tmp. TRADE-OFF some plugins that read shared assets or system temp paths outside the webroot will break; pilot before fleet rollout. Playbook P2.

Near term, 1 to 4 weeks, pilot on a ring first

H6. Audit every active theme for a top-level page-* directory and remove or rename it where the theme does not actually need one. Attack-surface reduction, follows directly from the vendor-published precondition. Twenty Twelve, Twenty Fourteen, Neve, Hestia and Sydney are named; a custom theme can also qualify. Eliminating the precondition on a given host removes the RCE escalation path entirely, independent of the pearcmd controls above.

H7. Extend FileVantage/FIM coverage to /tmp and /var/tmp on WordPress hosts, not just the web root. Detection prerequisite, not a MITRE mitigation. Validation gate 3 in Section 11 is the acceptance test. Without this, Q3 and Q4 are theoretical and the host-side N3 scan is the estate's only coverage of the file-write step. TRADE-OFF /tmp is a high-churn path on most Linux hosts; scope the policy narrowly (extension and depth limits) to keep the event volume manageable.

H8. Restrict disable_functions to remove shell_exec, system, passthru and proc_open from the web-facing PHP-FPM pool where the application does not need them. PHP hardening, MITRE M1042 Disable or Remove Feature. This is the specific mechanism that turns an included payload into a spawned shell (the event Q1 hunts for); removing it at the PHP layer is a second, independent control on top of the process-lineage detection. TRADE-OFF some legitimate plugins (media processing, backups) call these functions; audit before disabling estate-wide, and prefer a per-pool policy over a single global php.ini.

H9. Complete Falcon sensor coverage on every WordPress host, including containers, and confirm containerised images do not inherit register_argc_argv=On from the base image. Detection prerequisite; CIS Docker Benchmark for the runtime. The official PHP Docker image ships with this setting on by default per the vendor advisory, which makes an unaudited container fleet disproportionately exposed.

H10. Subscribe the WordPress estate to an inventory with version and theme attestation. MITRE M1051 supporting control. The recurring finding across CVE campaigns against WordPress is not difficulty of patching, it is not knowing which sites exist. Shadow installs on marketing subdomains and old staging copies are the standard blind spot. best-practice, no formal benchmark

Strategic, 1 to 3 months, architectural

H11. Make the web root, and any path PHP can reach including /tmp, read-only to the PHP worker except for the specific paths that need writes. MITRE M1022. If php-fpm cannot write outside a narrowly scoped upload path, the config-create primitive has nowhere to land regardless of whether the LFI or the pearcmd chain is ever fully patched on a given host.

H12. Segment the web tier from the rest of the internal network. MITRE M1030 Network Segmentation. Assume the next unauthenticated WordPress RCE lands too. The question that decides blast radius is what a compromised web server can reach: the database it needs, and as little else as possible.

H13. Establish an emergency CMS patching SLA with virtual patching as the bridge. MITRE M1051, M1037. The defining fact of this campaign is timing: exploitation began the same day as disclosure, and CrowdSec measured a hundred-fold growth in distinct attacking sources within five days. A patch cycle measured in weeks is no longer a sufficient control for internet-facing CMS software. Target managed WAF/virtual-patch deployment (such as the CrowdSec crowdsecurity/vpatch-CVE-2026-87902 rule, Section 7 N2) within hours of disclosure and core patching within 72 hours.

13

Deployable Playbooks

P1 · Web-server configuration, block pagename traversal at the edge

Apply on every reverse proxy and web server fronting WordPress, then reload. Test on one node before fleet rollout.

nginx

# /etc/nginx/snippets/cve-2026-87902-mitigation.conf
# 1. Block a pagename argument containing a traversal sequence, encoded or not,
#    in the query string. Covers single and double percent-encoding.
if ($arg_pagename ~* "(\.\.|%2e%2e|%252e%252e)") { return 403; }

# 2. The same check for pagename delivered in the POST body needs request-body
#    inspection (e.g. via ngx_http_lua_module or an AppSec/WAF component) -
#    nginx's own directives cannot filter POST body content natively. If you run
#    CrowdSec, its AppSec Component covers this out of the box via the
#    crowdsecurity/vpatch-CVE-2026-87902 rule (see N2 in Section 7).

# 3. Deny direct access to pearcmd.php at all three commonly reported paths,
#    belt-and-braces alongside removing the file per playbook P2.
location ~* /(usr/(local/lib|share)/php|usr/share/pear)?/?pearcmd\.php$ { deny all; return 403; }

# 4. Block the two published scanner/PoC user agents outright.
if ($http_user_agent ~* "(cve-2026-87902-poc|nuclei-cve-2026-87902)") { return 403; }

Apache 2.4

# /etc/apache2/conf-available/cve-2026-87902-mitigation.conf
RewriteEngine On

# Query-string pagename traversal, single or double encoded
RewriteCond %{QUERY_STRING} pagename=[^&]*(\.\.|%2e%2e|%252e%252e) [NC]
RewriteRule .* - [F,L]

# Deny direct access to pearcmd.php wherever it is reachable under the docroot
RewriteCond %{REQUEST_URI} pearcmd\.php [NC]
RewriteRule .* - [F,L]

# Block the two published scanner/PoC user agents
RewriteCond %{HTTP_USER_AGENT} (cve-2026-87902-poc|nuclei-cve-2026-87902) [NC]
RewriteRule .* - [F,L]

# POST-body pagename filtering needs mod_security or an equivalent request-body
# inspection module; Apache's rewrite directives alone cannot see the POST body.

Verify: nginx -t or apachectl configtest, reload, then run probe 4 from the Section 10 audit block. It must return 403 with no OPML/RSS content in the body.

P2 · PHP runtime hardening (php.ini / pool configuration)

; Add to php.ini or the relevant php-fpm pool .conf, then restart php-fpm.

; Breaks the pearcmd RCE escalation specifically: without argv populated from
; the query string, pearcmd.php cannot be driven via +config-show/+config-create
; even if it is included.
register_argc_argv = Off

; Confines PHP's own file access to the site root, which is the control that
; most directly addresses this vulnerability's root cause: even a successful
; get_page_template() traversal cannot open a file outside this path.
; Set per-vhost/per-pool, not just globally, so each site is confined to its
; own tree rather than a shared one.
open_basedir = /var/www/SITE_ROOT:/tmp

; Removes the specific functions this campaign's payloads use to turn a PHP
; include into a spawned shell. Audit for legitimate plugin usage first.
disable_functions = shell_exec,system,passthru,proc_open,popen

; Belt-and-braces: rename or remove pearcmd.php entirely rather than relying
; on runtime settings alone.
; mv /usr/local/lib/php/pearcmd.php /usr/local/lib/php/pearcmd.php.disabled
; mv /usr/share/php/pearcmd.php     /usr/share/php/pearcmd.php.disabled
; mv /usr/share/pear/pearcmd.php    /usr/share/pear/pearcmd.php.disabled

Verify: php -i | grep -E 'register_argc_argv|open_basedir|disable_functions' reflects the new values, then restart php-fpm and confirm the site still renders normally end to end (theme, uploads, any plugin that touches the filesystem).

P3 · Theme audit and pearcmd removal script

#!/usr/bin/env bash
# Run per site root. Read-only theme audit plus pearcmd removal (with backup).
set -euo pipefail
SITE_ROOT="/var/www/example-site"     # <-- set this

echo "[*] Themes with a top-level page-* directory (RCE precondition 1)"
find "${SITE_ROOT}/wp-content/themes" -maxdepth 2 -type d -iname 'page-*'

echo "[*] pearcmd.php presence at the three commonly reported install paths"
for p in /usr/local/lib/php/pearcmd.php /usr/share/php/pearcmd.php /usr/share/pear/pearcmd.php; do
  if [ -e "$p" ]; then
    echo "  found: $p -- backing up and removing"
    mkdir -p /var/backups/pearcmd-quarantine
    cp -p "$p" "/var/backups/pearcmd-quarantine/$(basename "$p").$(date +%Y%m%d%H%M%S)"
    chmod 000 "$p"
  fi
done

echo "[*] Done. Confirm your PEAR-dependent tooling, if any, still works before removing"
echo "    the quarantined copies permanently."

Pilot this one. A small number of hosting control panels and legacy PEAR-based tooling do call pearcmd.php for legitimate package management. Confirm nothing in your stack depends on it before removing it estate-wide; chmod 000 here is reversible, deletion is not.

P4 · Triage collection script (read-only)

#!/usr/bin/env bash
# Read-only evidence collection for a suspected CVE-2026-87902 compromise.
# Collects only, changes nothing. Run before any cleanup.
set -uo pipefail
SITE_ROOT="${1:-/var/www}"
OUT="/var/tmp/cve-2026-87902-triage-$(hostname)-$(date +%Y%m%d%H%M%S)"
mkdir -p "$OUT"

echo "[*] WordPress versions"
find "$SITE_ROOT" -name version.php -path '*wp-includes*' \
  -exec grep -H -m1 wp_version {} + | tee "$OUT/versions.txt"

echo "[*] PHP files in /tmp and /var/tmp changed since the day before disclosure"
find /tmp /var/tmp -maxdepth 2 -name '*.php' -newermt '2026-09-21' \
  -printf '%T+ %s %p\n' 2>/dev/null | sort | tee "$OUT/dropped-php.txt"

echo "[*] Exact and pattern-matched dropped filenames"
find /tmp /var/tmp -maxdepth 2 -regextype posix-extended \
  -regex '.*/(wp-pear-rce-flag|poc87902|luci_[A-Za-z0-9]+|zeta_[A-Za-z0-9]+)\.php' \
  2>/dev/null | tee "$OUT/named-dropped-php.txt"

echo "[*] pearcmd.php reachability"
for p in /usr/local/lib/php/pearcmd.php /usr/share/php/pearcmd.php /usr/share/pear/pearcmd.php; do
  [ -r "$p" ] && echo "READABLE: $p"
done | tee "$OUT/pearcmd-status.txt"

echo "[*] register_argc_argv setting"
php -i 2>/dev/null | grep -i register_argc_argv | tee "$OUT/php-config.txt"

echo "[*] Themes with a page-* top-level directory"
find "$SITE_ROOT" -maxdepth 4 -type d -iname 'page-*' -path '*wp-content/themes*' \
  | tee "$OUT/theme-precondition.txt"

echo "[*] Access-log hits"
grep -aEi 'pagename=[^ ]*(%2e%2e|%252e%252e|\.\.)|pearcmd|config-show|config-create|cve-2026-87902-poc|nuclei-cve-2026-87902' \
  /var/log/nginx/access.log* /var/log/apache2/access.log* 2>/dev/null \
  | tee "$OUT/access-hits.txt"

echo "[*] Cron and SSH keys for the web user"
crontab -l -u www-data 2>/dev/null | tee "$OUT/webuser-cron.txt"
cat /var/www/.ssh/authorized_keys 2>/dev/null | tee "$OUT/webuser-authorized-keys.txt"

echo "[+] Collected to $OUT - preserve this before remediating."
14

Containment Runbook

PhaseActionsOwnerEvidence to capture
0 · Triage
0 to 30 min
Confirm the WordPress version on the alerting host and whether the RCE preconditions (page-* theme directory, register_argc_argv On, readable pearcmd.php) are met. Confirm whether the site was internet-reachable during 2026-09-22 to patch time. A vulnerable version plus a Q1 hit is a presumed compromise until disproven.SOC L1Detection record, version output, precondition audit, exposure window
1 · Isolate
30 to 60 min
Network-contain the host in Falcon. Do not reboot, do not reimage, do not delete anything under /tmp yet. If the site must stay up, fail over to a known-good build from before 2026-09-22 rather than cleaning in place.SOC L2Containment timestamp; running process list; established connections
2 · Preserve
1 to 3 h
Run playbook P4. Snapshot /tmp and /var/tmp. Copy access logs before rotation. Preserve the disk/volume snapshot if the host is a cloud instance.IRP4 output bundle, access logs, disk/volume snapshot
3 · Scope
3 to 8 h
Run Q1, Q2, Q5, Q6 and Q7 across the whole web host group, not just the alerting host; the same scanner traffic hit everything you expose. Run Section 7 N1, N3 and N4 on every WordPress host. Reconcile the precondition audit (Section 10) fleet-wide.IR + HuntPer-host findings matrix; list of every host with any hit or any met precondition
4 · Eradicate
8 to 24 h
Rebuild from a known-good artefact where a Q1/Q4 hit or a filesystem finding confirms compromise; do not clean in place. Patch to the fixed version for the branch, apply P1, P2 and P3, rotate any credential a dropped shell may have touched, and remove any persistence found by Q6/Q7.Platform + IRRebuild record, patch attestation, credential rotation log, removed persistence artefacts
5 · Recover
24 to 72 h
Restore service from the rebuilt artefact behind the P1 WAF rules. Re-run the Section 10 audit block's traversal probe to confirm a 403 with no OPML/RSS content in the body.PlatformAudit block output showing the probe blocked
6 · Monitor
72 h to 30 d
Promote Q1, Q6 and Q7 to Scheduled Searches per Section 9. Re-run Section 7 N3 and N4 at day 7 and day 30; a second drop through a persistence mechanism missed in scoping will surface there.SOCScheduled Search hit counts; day-7/day-30 rescan results

Do not clean in place. The exploit writes attacker-controlled PHP outside the web root where routine web-root integrity tooling will never look, and the same primitive can be re-run to write somewhere more durable on a second attempt. Removing the one dropped file you found is not eradication if the precondition that let the attacker in is still standing. Rebuild from a known-good artefact and confirm the preconditions are closed (H3 through H6) before returning the host to service.

15

Detection Coverage Map

TechniqueBehaviourCQLIOACoverage
T1595.002Reconnaissance probing against harmless core files via pagenamen/an/aGAP Web/WAF logs only (Section 7 N1). No endpoint telemetry exists for this step, and an unsuccessful probe against a patched or precondition-clean host leaves no artifact anywhere.
T1190pearcmd reachability check and file-write exploitation requestn/an/aGAP Web-log only (Section 7 N1, N2). Falcon has no visibility into HTTP request parameters.
T1505.003Attacker PHP written to /tmp or /var/tmp via pearcmd config-createQ3, Q4n/aPARTIAL Conditional on FIM policy explicitly extended to /tmp, which is atypical. Without it, Section 7 N3 is the only coverage.
T1059.004 / T1059.003Web-server process spawns a shell or interpreter when the dropped payload executesQ1, Q9YesGOOD Primary detection. Fires regardless of which dropped filename or payload variant is used.
T1033 / T1082Discovery commands under a web-server parentQ2n/aGOOD
T1105Second-stage download by a web-server processQ5YesGOOD
T1053.003 / T1053.005Persistence from a web-server process treeQ6YesGOOD
T1098.004 / T1552.004SSH key written or modified from a web-server process treeQ7YesGOOD
T1071.001Outbound C2/exfiltration from the compromised web tierQ8n/aGAP No confirmed C2 infrastructure exists in reporting for this campaign, so this is a rarity-ranked stack hunt rather than an indicator match. High effort per result.
T1027Obfuscated marker string assembled via chained chr() callsn/an/aGAP Filesystem-content hunt only (Section 7 N3 grep). No process-telemetry signature exists for PHP source obfuscation, since PHP never becomes a process image.

Honest read on this map. Three of the first four steps in the attack chain (reconnaissance, the pearcmd check, and the file write itself) have no endpoint coverage at all, and that is a property of the vulnerability rather than a shortfall in the queries: everything up to and including the write happens inside HTTP request handling, a domain an EDR sensor cannot see into. The pack is therefore strong from the moment the dropped payload actually executes onward, and blind before it. Two consequences follow. First, the web-log hunts in Section 7 are not optional garnish, they are the only coverage for the first half of the intrusion, and validation gate 6 (Section 11) is the highest-leverage item in the whole pack. Second, because Falcon cannot distinguish a probe that never got past the theme precondition from a request that never happened at all, a clean Q1 result on a host that was never patched proves only that the payload was never triggered successfully, not that the host was never targeted. Validation: gates 1 through 3 must pass before any result from this pack is trusted; gate 4 must complete before Q1, Q6 or Q7 are promoted to Detect.

16

Hunt Summary Ticket

TITLE       WordPress core page-template path traversal to pearcmd RCE (CVE-2026-87902)
SEVERITY    Critical - unauthenticated code execution, exploited in the wild, on CISA KEV

SCOPE       Every WordPress install in the estate, internet-facing first, including
            shadow installs on marketing subdomains, staging copies, and container
            images. Vulnerable: 4.7.0-7.1.1 across every branch. Patched: 7.1.2 |
            7.0.6 | 6.9.9 | 6.8.10 and backports to 4.7.37. Hunt window: 2026-09-22
            to the moment each site actually patched.

HYPOTHESIS  An unauthenticated attacker sent a pagename parameter carrying a double
            URL-encoded traversal sequence to get_page_template(), confirmed the
            inclusion was live against a harmless core file, checked pearcmd.php
            reachability, then used pearcmd's config-create directive to write PHP
            content to /tmp or /var/tmp. If register_argc_argv was On and a page-*
            theme directory existed, that payload later executed inside the PHP
            worker and, if it called a shell function, spawned a shell.

QUERIES     Q1  Web server spawns shell or interpreter          PRIMARY
            Q2  Discovery commands under web-server parent      PRIMARY
            Q3  PHP written to /tmp or /var/tmp (needs FIM)      CONDITIONAL
            Q4  Published dropped filename in file telemetry     CONDITIONAL
            Q5  Download utility launched by web server
            Q6  Persistence created from web-server tree
            Q7  SSH key material touched from web-server tree
            Q8  Outbound to non-standard destination             BASELINE SWEEP
            Q9  Rare children of web-server parents              BASELINE SWEEP

DO FIRST    1. Section 10 audit block - WordPress versions, register_argc_argv,
               pearcmd.php reachability, and page-* theme directories, estate-wide.
            2. Q1 and Q2 over 30 days across the web host group.
            3. Section 7 N1 - grep web logs for the pagename traversal marker,
               the pearcmd markers, and the two user agents.
            Steps 1 and 3 do not require Falcon and can start immediately.

FINDINGS    [ ] Unpatched WordPress instances found:      ____
            [ ] Hosts meeting both RCE preconditions:      ____
            [ ] Q1 / Q2 hits requiring triage:             ____
            [ ] Dropped payloads recovered from /tmp:      ____
            [ ] pagename traversal requests in web logs:   ____

GAPS        - No endpoint telemetry for chain steps 1-3 (HTTP request handling).
              Web/WAF logs are the only coverage, and unsuccessful probes against
              a patched or precondition-clean host leave no artifact at all.
            - Q3/Q4 return nothing without FIM explicitly covering /tmp and
              /var/tmp, which is atypical.
            - Managed/third-party hosting has no sensor - provider attestation only.
            - No malware hashes or corroborated payload infrastructure published
              for this campaign as of 2026-09-29; nothing importable ships in the CSV.

ACTIONS     [ ] Patch to 7.1.2 / 7.0.6 / 6.9.9 / 6.8.10 (or branch backport)  H1  Immediate
            [ ] Block pagename traversal at the WAF/edge                     H2  Immediate
            [ ] Disable register_argc_argv                                  H3  Immediate
            [ ] Remove or deny access to pearcmd.php                        H4  Immediate
            [ ] Set open_basedir per site                                   H5  Immediate
            [ ] Promote Q1, Q6, Q7 to Scheduled Search (Monitor 7d)

OWNER       HuntPack
VERSION     v0.1 - 2026-09-29
17

Changelog

v0.12026-09-29Initial pack. Nine CQL hunt queries built from eight saved source snapshots (GHSA advisory, NVD, CISA KEV, MITRE CVE record, Patchstack, CrowdSec, The Hacker News, SecurityWeek). Hunt thesis is endpoint-side post-exploitation behaviour (web-server process lineage after the pearcmd-written payload executes) rather than HTTP request indicators, which the Falcon sensor cannot see; the request-level indicators are carried as native audit-log hunts in Section 7 and recorded as coverage gaps in Section 15. No file hashes or corroborated payload infrastructure shipped as indicators; the two exact dropped filenames and two randomised-prefix patterns are hunted via FileName regex (Q4) and host-side scanning rather than Falcon Custom IOC import, since neither filenames nor the commodity 30,000-plus attacking-IP set are appropriate for that mechanism. CVSS disagreement between the WordPress/GHSA CVSS 4.0 score (9.2) and the NVD/CISA CVSS 3.1 score (8.1) recorded rather than resolved. A single-sourced detail from The Hacker News (Previdian's honeypot observation of one attacking IP and a GitHub-hosted uploader) deliberately not shipped as an indicator pending a second source.
18

References

TierSourceUsed forSnapshotAccessed
1GitHub Security Advisory GHSA-7hp8-65ch-5whpPrimary vendor advisory: full version matrix, CVSS 4.0 vector, CWE-98, both RCE preconditions, discovery credit07-ghsa.txt2026-09-29
1NVD - CVE-2026-87902 (via the NVD 2.0 REST API)CVSS 3.1 8.1 High, CISA SSVC data, KEV add/due dates, CWE-9805-nvd.txt2026-09-29
1MITRE CVE Program record - CVE-2026-87902 (via the CVE Services API)Independent confirmation of CWE-98, CVSS 3.1 vector, CISA ADP Vulnrichment timeline08-cve-mitre-record.txt2026-09-29
1CISA Known Exploited Vulnerabilities CatalogKEV addition date 2026-09-25, remediation due date 2026-09-2806-cisa-kev.txt2026-09-29
1Patchstack - Attackers Started Probing WordPress Sites Hours After the PatchThree-stage attack progression, exact request shapes, dropped filenames, scanner user agents, detection guidance01-patchstack.txt2026-09-29
1CrowdSec - CVE-2026-87902: 30,000 Addresses in Five DaysExploitation telemetry (signals, distinct sources, cumulative IPs), CrowdSec virtual-patch rule id02-crowdsec.txt2026-09-29
2The Hacker News - Attackers Exploit WordPress CVE-2026-87902 Within Hours of DisclosureIndependent confirmation of preconditions and timeline; single-sourced Previdian honeypot detail (not shipped as an indicator)03-thehackernews.txt2026-09-29
2SecurityWeek - Critical WordPress Vulnerability Exploited Immediately After DisclosureIndependent corroboration of the timeline and the three-stage progression, citing Patchstack directly04-securityweek.txt2026-09-29

The raw fetched text of every source above is saved in WordPress-CVE-2026-87902-RFI-Hunt-sources/ next to this file. No source contained instructions aimed at an automated agent, and no source was disqualified. Nothing in this pack was written from memory.