PhastPress <= 3.7 - Unauthenticated Arbitrary File Read via Null Byte Injection
Description
The PhastPress plugin for WordPress is vulnerable to Unauthenticated Arbitrary File Read via null byte injection in all versions up to, and including, 3.7. This is due to a discrepancy between the extension validation in `getExtensionForURL()` which operates on URL-decoded paths, and `appendNormalized()` which strips everything after a null byte before constructing the filesystem path. This makes it possible for unauthenticated attackers to read arbitrary files from the webroot, including wp-config.php, by appending a double URL-encoded null byte (%2500) followed by an allowed extension (.txt) to the file path.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=3.7Source Code
WordPress.org SVNThis exploitation research plan targets **CVE-2025-14388** in the **PhastPress** plugin. The vulnerability allows unauthenticated attackers to read arbitrary files (like `wp-config.php`) due to inconsistent handling of null bytes during file extension validation versus path normalization. --- ### …
Show full research plan
This exploitation research plan targets CVE-2025-14388 in the PhastPress plugin. The vulnerability allows unauthenticated attackers to read arbitrary files (like wp-config.php) due to inconsistent handling of null bytes during file extension validation versus path normalization.
1. Vulnerability Summary
- Vulnerability: Unauthenticated Arbitrary File Read via Null Byte Injection.
- Plugin: PhastPress <= 3.7.
- Vulnerable Functions:
getExtensionForURL()andappendNormalized(). - Root Cause: The plugin validates file extensions against a URL-decoded string but truncates the path at the first null byte when constructing the final filesystem path. By using a double-encoded null byte (
%2500), an attacker can bypass extension filters (e.g., making the plugin think it's serving a.txtor.jsfile) while the filesystem operation reads the target file before the null byte.
2. Attack Vector Analysis
- Endpoint: The plugin serves optimized assets via a specific query parameter or rewrite rule. Based on PhastPress architecture, this is typically handled via the
?phast_get_...parameter or similar interceptors ininit. - Target Parameter: A parameter representing the path to a resource (likely
serviceorurl). - Authentication: None (Unauthenticated).
- Preconditions: The plugin must be active. The attacker needs to know the relative path from the webroot to
wp-config.php.
3. Code Flow (Inferred)
- Entry Point: A request is made to a PhastPress-controlled URL (e.g.,
/?phast_get_asset=...). - Validation: The plugin calls
getExtensionForURL($url). This function URL-decodes the input. If the input iswp-config.php%2500.js, it decodes towp-config.php\0.js. The extension check sees.jsand approves it. - Normalization: The plugin calls
appendNormalized(). This function identifies the null byte (\0) and strips everything following it, resulting in the stringwp-config.php. - File Read: The resulting string is used in a filesystem call (e.g.,
file_get_contents()orreadfile()) within the webroot context, returning the contents ofwp-config.php.
4. Nonce Acquisition Strategy
Based on the "Arbitrary File Read" nature of this vulnerability (serving assets), it is highly likely that no nonce is required for the retrieval endpoint. PhastPress serves optimized versions of public scripts/styles to all visitors.
Verification of Nonce Requirement:
If the endpoint does require a nonce (check for check_ajax_referer or wp_verify_nonce in the handler for the asset-serving logic), follow this strategy:
- Search for Nonce Exposure: Check
Phast\Plugin->enqueue_scriptsor similar. - Shortcode: PhastPress usually runs automatically on all pages to optimize scripts. No specific shortcode is likely needed.
- Extraction:
- Navigate to the homepage.
- Use
browser_evalto look for localized data:browser_eval("window.phast_config"). - Look for keys like
nonceorajax_nonce.
5. Exploitation Strategy
The goal is to read wp-config.php.
- Step 1: Identify the Resource Path. PhastPress usually maps assets relative to the WordPress content or root.
- Step 2: Construct the Payload.
- Target:
wp-config.php - Null Byte:
%2500(Double encoded so it survives the first internal URL decode). - Allowed Extension:
.js,.css, or.txt. - Full Payload Component:
wp-config.php%2500.js
- Target:
- Step 3: Trigger the Request.
We will target the common asset-serving entry point for PhastPress.
HTTP Request (Example):
GET /?phast_get_asset=wp-config.php%2500.js HTTP/1.1
Host: localhost
(Note: The exact parameter name phast_get_asset should be verified by checking the init hook in the plugin source.)
6. Test Data Setup
- Environment: Standard WordPress installation.
- Plugin: Install and activate PhastPress <= 3.7.
- Content: No special content is required, as
wp-config.phpexists by default in the webroot. - Confirm Path: Ensure
wp-config.phpis in the directory where the plugin expects to find assets (usually the site root).
7. Expected Results
- Successful Exploit: The HTTP response body contains the PHP source code of
wp-config.php, includingDB_NAME,DB_USER, andDB_PASSWORD. - Response Headers:
Content-Typemight betext/javascriptorapplication/x-javascript(due to the.jssuffix) despite containing PHP code. - HTTP Status:
200 OK.
8. Verification Steps
- Check Response Content: Use
http_requestand verify the response body contains the stringdefine( 'DB_NAME'. - WP-CLI Confirmation: After the exploit, run:
Compare the output to the value extracted via the exploit to confirm the file read was accurate.wp config get DB_NAME
9. Alternative Approaches
If ?phast_get_asset is not the correct parameter:
- Grep for Entry Points:
grep -rn "isset(\$_GET\['phast_" wp-content/plugins/phastpress/ - Analyze Rewrite Rules: Check if the plugin uses
add_rewrite_rule. If so, the URL might look like/phast/wp-config.php%2500.js. - Extension variation: If
.jsis blocked, try.css,.png,.txt, or.map. - Path Traversal: If the plugin prepends a specific directory (like
wp-content/uploads/), use:../../wp-config.php%2500.js.
Summary
The PhastPress plugin for WordPress (<= 3.7) is vulnerable to unauthenticated arbitrary file read due to a logic flaw in how file extensions are validated versus how file paths are constructed. Attackers can use a double URL-encoded null byte to bypass extension filters and read sensitive files like wp-config.php from the server.
Exploit Outline
An unauthenticated attacker identifies the plugin's asset-serving endpoint (typically involving query parameters like 'phast_get_asset' or specific rewrite rules). The attacker constructs a payload targeting a sensitive file (e.g., 'wp-config.php') and appends a double URL-encoded null byte (%2500) followed by an allowed extension (e.g., '.js', '.css', or '.txt'). When the plugin processes the request, it validates the extension against the decoded string (which includes the allowed extension), but the filesystem operation truncates the path at the null byte, causing the server to return the contents of the target file instead of the asset.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.