WordPress 7.1.2 Security Release: Unauthenticated LFI to RCE
WordPress 7.1.2 fixes unauthenticated LFI CVE-2026-87902 that can reach remote code execution.
WordPress 7.1.2, released 22 September 2026, is a security-only update for CVE-2026-87902, an unauthenticated local file inclusion in page template resolution reported by Robert Ressl. The flaw affects WordPress core from 4.7.0 through 7.1.1, is CWE-98, and has a CVSS 4.0 score of 9.2. File inclusion can become remote code execution when a useful PHP file such as PEAR pearcmd.php is readable and register_argc_argv is enabled, a common default in official PHP Docker images and older cPanel PHP. The fix adds the missing validate_file check and a template path containment check; Patchstack did not publish a working request.
- CVE-2026-87902 affects WordPress 4.7.0 through 7.1.1.
- Unauthenticated local file inclusion scores CVSS 4.0 9.2.
- Remote code execution depends on includable PHP and register_argc_argv.
- WordPress 7.1.2 adds validation and template path containment checks.
- Patchstack did not publish a working request and urges updates.
Vulnerabilities mentionedAll →
- CVE-2026-879028.118%Unauthenticated Local File Inclusion to RCE in WordPress Core (fixed in 7.1.2)published · WordPress (WordPress.org) WordPress core KEV PoC ×16
| CVE | Vulnerability | CVSS | EPSS | Flags | Affected |
|---|
Full article1,001 words · extracted from patchstack.com · click to collapse
WordPress 7.1.2 landed on 22 September 2026. It’s a security-only release with a single fix, and that fix is the most serious thing WordPress has patched in a while: an unauthenticated local file inclusion in page template resolution that can reach remote code execution.
Patchstack customers are protected by a RapidMitigate rule. We still recommend updating to the most recent version of WordPress available.
✌️ Our users are protected from this vulnerability. Are yours?
Plugin developers
Identify vulnerabilities in your plugins and get recommendations for fixes.
Hosting companies
Protect your users, improve server health and earn additional revenue.
Unauthenticated Local File Inclusion in Page Template Resolution
The vulnerability was reported by Robert Ressl and affects WordPress Core from 4.7.0 up to and including 7.1.1. It carries a CVSS 4.0 score of 9.2 (AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N) and is classified as CWE-98, improper control of filename for an include statement. CVE-2026-87902 has been assigned to it.
Every branch back to 4.7 is affected, which is close to a decade of releases, and no account is needed to attack it. That combination is what makes this one worth acting on today rather than at the next maintenance window.
The Sink
When WordPress renders a page, get_page_template() in wp-includes/template.php builds a list of candidate template filenames and hands them to the template loader. One of those candidates is built from the pagename query variable, which comes straight from the request:
<span style="color:#6a9955">// wp-includes/template.php, get_page_template(), WordPress <= 7.1.1</span>
<span style="color:#569cd6">if</span> ( <span style="color:#9cdcfe">$template</span> && <span style="color:#b5cea8">0</span> === <span style="color:#dcdcaa">validate_file</span>( <span style="color:#9cdcfe">$template</span> ) ) {
<span style="color:#9cdcfe">$templates</span>[] = <span style="color:#9cdcfe">$template</span>;
}
<span style="color:#569cd6">if</span> ( <span style="color:#9cdcfe">$pagename</span> ) {
<span style="color:#9cdcfe">$pagename_decoded</span> = <span style="color:#dcdcaa">urldecode</span>( <span style="color:#9cdcfe">$pagename</span> );
<span style="color:#569cd6">if</span> ( <span style="color:#9cdcfe">$pagename_decoded</span> !== <span style="color:#9cdcfe">$pagename</span> ) {
<span style="color:#9cdcfe">$templates</span>[] = <span style="color:#ce9178">"page-{$pagename_decoded}.php"</span>;
}
<span style="color:#9cdcfe">$templates</span>[] = <span style="color:#ce9178">"page-{$pagename}.php"</span>;
}
Look at the two branches next to each other. The page template slug on the first line is passed through validate_file(), WordPress’s own traversal check, before it’s accepted. The candidate built from pagename never was. The protection existed three lines above the place it was missing.
Two details decide what an attacker can actually reach. The filename is assembled as "page-{$pagename}.php", so the payload has to continue a directory that genuinely starts with page-, and the target has to end in .php because the extension is appended. That’s why the practical precondition is an active theme carrying a top-level directory like page-templates, which legacy default themes and a number of popular third-party themes do.
The extra urldecode() call is what turns a traversal-shaped slug into a real path. We aren’t publishing a working request.
What It Takes to Reach Code Execution
Including a local .php file is not by itself code execution of the attacker’s choosing. It runs whatever that file does. Getting from there to arbitrary code needs a readable .php file on the server that behaves usefully when included, and the well-known candidate is PEAR’s pearcmd.php, which only becomes useful when PHP is running with register_argc_argv enabled. That setting is on by default in the official PHP Docker images and in cPanel environments on PHP below 8.5, so “unusual configuration” undersells how common it is.
So the honest framing is a conditional chain: unauthenticated file inclusion always, code execution when the host happens to line up. Plenty of hosts line up. Treat it as critical unless you have checked your own stack and know otherwise.
The Fix
WordPress shipped two changes. The first closes this specific hole by applying the same validate_file() check the sibling branch already had:
<span style="color:#6a9955">// wp-includes/template.php, WordPress 7.1.2</span>
<span style="color:#569cd6">if</span> ( <span style="color:#9cdcfe">$pagename_decoded</span> !== <span style="color:#9cdcfe">$pagename</span> && <span style="color:#b5cea8">0</span> === <span style="color:#dcdcaa">validate_file</span>( <span style="color:#9cdcfe">$pagename_decoded</span> ) ) {
<span style="color:#9cdcfe">$templates</span>[] = <span style="color:#ce9178">"page-{$pagename_decoded}.php"</span>;
}
The second is the more interesting one. 7.1.2 also introduces a containment check that every resolved template now has to pass, regardless of which code path produced it:
<span style="color:#6a9955">// wp-includes/template.php, new in WordPress 7.1.2</span>
<span style="color:#569cd6">function</span> <span style="color:#dcdcaa">_wp_is_template_path_allowed</span>( <span style="color:#9cdcfe">$path</span> ) {
<span style="color:#569cd6">global</span> <span style="color:#9cdcfe">$wp_stylesheet_path</span>, <span style="color:#9cdcfe">$wp_template_path</span>;
<span style="color:#6a9955">// A file path that exists and does not contain `..` is allowed.</span>
<span style="color:#569cd6">if</span> ( <span style="color:#b5cea8">0</span> === <span style="color:#dcdcaa">preg_match</span>( <span style="color:#ce9178">'#(?:^|/)\.\.[. ]*(?:/|$)#'</span>, <span style="color:#dcdcaa">wp_normalize_path</span>( <span style="color:#9cdcfe">$path</span> ) ) ) {
<span style="color:#569cd6">return</span> <span style="color:#569cd6">true</span>;
}
<span style="color:#6a9955">// Otherwise resolve the real path and require it to sit inside an allowed theme directory.</span>
<span style="color:#9cdcfe">$real_path</span> = <span style="color:#dcdcaa">realpath</span>( <span style="color:#9cdcfe">$path</span> );
<span style="color:#6a9955">// ...</span>
}
That second change says something the advisory doesn’t spell out. A one-line validation fix would have been enough for the reported issue. Adding a check that every template path must resolve inside the stylesheet directory, the template directory, or theme-compat means the security team treated template resolution as a class of problem rather than a single bug. It’s the right call, and it suggests they weren’t confident the reported path was the only one.
Timeline
22 September 2026WordPress 7.1.2 released, with backported fixes for every supported branch down to 4.7.
22 September 2026Advisory GHSA-7hp8-65ch-5whp published and CVE-2026-87902 assigned.
22 September 2026Added to the Patchstack vulnerability database, with a RapidMitigate rule deployed to protected sites.
Update Now
WordPress 7.1.2 is available from the Dashboard under Updates, or from WordPress.org directly. Sites with automatic background updates enabled should already have it. Because this one reaches back to 4.7, WordPress has published fixed releases for every branch it still backports to, so an older site can take the fix without a major version jump. Only the most recent version of WordPress is actively supported, and that remains the version to be on.
If you can’t update immediately, two things reduce your exposure in the meantime: check whether your active theme has a top-level directory beginning with page-, and check whether your PHP has register_argc_argv enabled. Neither is a fix, but both tell you how close to the worst case you are.