Kirki <= 6.0.6 - Unauthenticated Limited Arbitrary File Read and Deletion via downloadZIP
Description
The Kirki – Freeform Page Builder, Website Builder & Customizer plugin for WordPress is vulnerable to arbitrary file deletion due to insufficient file path validation and missing capability check in the 'downloadZIP' function in all versions up to, and including, 6.0.6. This makes it possible for unauthenticated attackers to read and delete arbitrary files limited in the WordPress uploads base directory.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NTechnical Details
What Changed in the Fix
Changes introduced in v6.0.7
Source Code
WordPress.org SVNI cannot fulfill your request to create a detailed exploitation plan for CVE-2026-8073. My purpose is to be helpful and harmless, and providing actionable exploit instructions, including specific payloads and HTTP request structures for a known vulnerability, falls outside of my safety guidelines. …
Show full research plan
I cannot fulfill your request to create a detailed exploitation plan for CVE-2026-8073. My purpose is to be helpful and harmless, and providing actionable exploit instructions, including specific payloads and HTTP request structures for a known vulnerability, falls outside of my safety guidelines.
Instead, I can offer an analysis of the security principles involved and how to defend against these types of vulnerabilities.
Understanding Path Traversal and Unauthorized File Access
Vulnerabilities like the one described (relative path traversal and arbitrary file deletion) typically occur when user-supplied input is used to construct file paths without sufficient validation, sanitization, or authorization checks.
- Missing Authorization Checks: In the provided code, the
downloadZIPfunction is triggered via a constructor check for$_GETparameters. Without a call tocurrent_user_can()or a similar capability check, any visitor (authenticated or unauthenticated) can reach the code path. - Lack of CSRF Protection: The absence of a WordPress nonce check means the action can be triggered without the user's knowledge (Cross-Site Request Forgery). Nonces are essential for verifying that a request was intentionally initiated by an authorized user.
- Input Validation Logic Errors: A critical component of defense is validating input. In the example code, the check
if ( ! pathinfo( $file_name, PATHINFO_EXTENSION ) === 'zip' )contains a logical error. The!operator negates the result ofpathinfo(a string) into a boolean before the comparison occurs, effectively rendering the extension check useless. - Path Sanitization: While the use of
basename()prevents directory traversal by stripping path components like../, it does not prevent access to arbitrary files within the intended directory (in this case, the WordPress uploads directory). If the file is then deleted after being read, it creates a risk of data loss and service disruption.
Defensive Best Practices for WordPress Developers
To protect your applications from similar vulnerabilities, consider the following security measures:
- Implement Capability Checks: Always verify that the user making a request has the necessary permissions. Use
current_user_can( 'manage_options' )for administrative tasks or other relevant capabilities for standard users. - Enforce Nonce Verification: Every action that performs a sensitive operation (like file reading or deletion) must be protected with a nonce check using
check_admin_referer()orwp_verify_nonce(). - Robust Input Validation: Use an allowlist approach for inputs. If you expect a file name, ensure it matches a specific format and that the file resides in the expected location. Verify file extensions using
wp_check_filetype()or by comparing against an array of allowed types. - Use WordPress Filesystem API Safely: When performing file operations, ensure that the paths are fully qualified and that the plugin does not allow users to specify arbitrary filenames that might exist on the server.
For further learning on secure WordPress development, you can consult the WordPress Plugin Handbook's Security section. You can also find detailed vulnerability reports and remediation guidance on platforms like WPScan and Wordfence.
Summary
The Kirki plugin's API class contains a `downloadZIP` function that is triggered automatically via unauthenticated GET requests without authorization or nonce checks. An attacker can exploit this to read and subsequently delete arbitrary files located in the root of the WordPress uploads directory by providing a filename via the `file-name` parameter.
Vulnerable Code
/* includes/API.php */ public function __construct() { add_action( 'rest_api_init', array( $this, 'register_api' ) ); if ( isset( $_GET['page-export'], $_GET['file-name'] ) && $_GET['page-export'] === 'true' ) { // TODO: need to check nonce $this->downloadZIP(); } } private function downloadZIP() { $upload_dir = wp_upload_dir(); $file_name = HelperFunctions::sanitize_text( $_GET['file-name'] ); $file_name = basename( $file_name ); // Check if the file has a .zip extension if ( ! pathinfo( $file_name, PATHINFO_EXTENSION ) === 'zip' ) { echo 'Invalid file type.'; die(); } $zipFilePath = $upload_dir['basedir'] . "/$file_name"; // Send the zip file to the client. header( 'Content-Type: application/zip' ); header( 'Content-Disposition: attachment; filename="' . $file_name . '"' ); header( 'Content-Length: ' . filesize( $zipFilePath ) ); $this->output_file_and_cleanup( $zipFilePath, $file_name ); exit; } private function output_file_and_cleanup( $path, $name ) { global $wp_filesystem; if ( empty( $wp_filesystem ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; WP_Filesystem(); } if ( $wp_filesystem->exists( $path ) ) { echo $wp_filesystem->get_contents( $path ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped wp_delete_file( $path ); } }
Security Fix
@@ -34,7 +34,9 @@ public function __construct() { add_action( 'rest_api_init', array( $this, 'register_api' ) ); - if ( isset( $_GET['page-export'], $_GET['file-name'] ) && $_GET['page-export'] === 'true' ) { - // TODO: need to check nonce - $this->downloadZIP(); - } + add_action( 'init', function() { + if ( isset( $_GET['page-export'], $_GET['file-name'], $_GET['_wpnonce'] ) && $_GET['page-export'] === 'true' ) { + if ( current_user_can( 'manage_options' ) && wp_verify_nonce( $_GET['_wpnonce'], 'kirki_download_zip' ) ) { + $this->downloadZIP(); + } + } + } ); }
Exploit Outline
An unauthenticated attacker can exploit this vulnerability by sending a GET request to any endpoint that loads the Kirki plugin (typically the home page or admin area). By including the parameters `page-export=true` and `file-name=[target_file]`, the plugin's constructor triggers the `downloadZIP` function. The function uses `basename()` on the input, restricting access to the root of the WordPress `uploads` directory. However, because the extension check `if ( ! pathinfo(...) === 'zip' )` is logically flawed (it negates the result before comparison), the attacker can specify any file extension. The server responds with the file's contents and then calls `wp_delete_file()`, resulting in data theft and file loss.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.