psf-wordpress-pt-check(8)
WordPress core page-template path traversal: read-only checker (v1)
CVE-2026-87902
If this software is helpful, consider a donation to our 501c3 nonprofit:
https://privacysafe.foundation
Disclosed and fixed on 22 September 2026. CVSS 9.2 (Critical). Unauthenticated
path traversal in WordPress core's page-template resolver, present in every
release from 4.7.0 through 7.1.1, roughly a decade. Exploitation was
observed on the same day as disclosure. CISA added CVE-2026-87902 to its
Known Exploited Vulnerabilities catalog on 25 September 2026.
Root cause: get_page_template() in wp-includes/template.php calls urldecode()
on the pagename query variable after sanitization has run. A double-encoded
traversal sequence (%252E%252E%252F) passes the first decode as a percent
literal, survives sanitize_title_with_dashes(), and is decoded a second time
by WordPress into ../, which escapes the theme directory. WordPress then builds
a candidate template name and, on servers where a PHP file exists at the
traversed path, includes it.
The fix (7.1.2 and the matching branch releases) changes one file, wp-includes/template.php,
in two places: the decoded pagename is used only if validate_file() accepts it, and a
template found by locate_template() is loaded only if the new function
_wp_is_template_path_allowed() accepts it. This script reads both from the source, so
a distribution backport under an older version number is recognized.
Affected: WordPress 4.7.0 through 7.1.1, across every supported branch.
WordPress before 4.7.0 is not affected.
Fixed: 7.1.2, 7.0.6, 6.9.9, 6.8.10, 6.7.9, 6.6.9 and backports down
to 4.7.37 (released September 22, 2026). WordPress 4.6 and
earlier move to a supported branch.
WordPress releases a fixed version for every branch on the same day, so
updating within your branch is always safe. On 7.1: update to 7.1.2. On
7.0: update to 7.0.6. And so on. Use `wp core update` or the admin panel.
There is no official workaround.
Discovered by Robert Ressl.
Advisory: GHSA-7hp8-65ch-5whp
This script is read-only. It finds WordPress installs by wp-includes/version.php.
Usage:
./wordpress-pt-check.sh [check] [DIR ...]
check (default) find WordPress under DIR, read each version, and read template.php
for the two fixes. Exit 0 = nothing exposed, 1 = an install
without the fix, or one whose state is unclear.
hunt look in web server logs for an encoded traversal in the pagename
parameter and for the scanner user agents seen in the attacks.
Exit 0 = nothing found, 1 = a strong lead. A clean hunt means none of the published indicators were found.
version print the script version.
Env: WPT_LOGS (space separated paths or globs) to name web server logs.
Containers: with no directory argument the check also copies the files it reads from running
docker and podman containers (SCAN_CONTAINERS=1, the default; 0 skips them) and judges them like
host installs. The copies are temporary: a path shown as .../docker_NAME_PATH is the PATH inside
the container NAME.The Security Patches software and this documentation are dedicated to the public domain under CC0 1.0 Universal. Other pages on privacysafe.app/docs are CC BY-SA 4.0 unless the page says otherwise.