CVE-2026-25328

Product File Upload for WooCommerce <= 2.2.4 - Unauthenticated Arbitrary File Deletion

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

Description

The Product File Upload for WooCommerce plugin for WordPress is vulnerable to arbitrary file deletion due to insufficient file path validation in all versions up to, and including, 2.2.4. This makes it possible for unauthenticated attackers 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).

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<=2.2.4
PublishedMarch 23, 2026
Last updatedApril 2, 2026

What Changed in the Fix

Changes introduced in v2.2.5

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2026-25328 - Unauthenticated Arbitrary File Deletion ## 1. Vulnerability Summary The **Product File Upload for WooCommerce** plugin (versions <= 2.2.4) contains a path traversal vulnerability in its file removal functionality. The plugin exposes an unauthenticated AJAX handler …

Show full research plan

Research Plan: CVE-2026-25328 - Unauthenticated Arbitrary File Deletion

1. Vulnerability Summary

The Product File Upload for WooCommerce plugin (versions <= 2.2.4) contains a path traversal vulnerability in its file removal functionality. The plugin exposes an unauthenticated AJAX handler that accepts a file path (or name) and deletes the corresponding file using PHP's unlink() function without proper sanitization or validation that the path resides within the intended upload directory. This allows an unauthenticated attacker to delete critical system files, such as wp-config.php.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Unauthenticated Action: superaddons_products_uploads_remove (as registered in frontend/index.php)
  • HTTP Method: POST
  • Vulnerable Parameter: file (inferred, based on typical removal handler patterns)
  • Required Nonce: Yes, the plugin requires a nonce created with the action string 'checkout_file_upload'.
  • Preconditions: The plugin must be active. The removal functionality is available to anyone who can access a product page to retrieve the nonce.

3. Code Flow

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php with the action superaddons_products_uploads_remove.
  2. Hook Registration: In frontend/index.php, the class Superaddons_Products_Uploads_Frontend registers the AJAX handler:
    add_action( 'wp_ajax_nopriv_superaddons_products_uploads_remove', array($this,'superaddons_products_uploads_remove') );
    
  3. Vulnerable Function: The superaddons_products_uploads_remove function is called.
  4. Insecure Sink (Inferred): The function likely retrieves a path from $_POST['file'], prepends the WordPress upload directory path, and passes the resulting string directly to unlink().
  5. Traversal: By providing a payload like ../../wp-config.php, the attacker escapes the intended directory and deletes the site configuration file.

4. Nonce Acquisition Strategy

The plugin enqueues a script and localizes a nonce for unauthenticated users on WooCommerce product pages.

  1. Identify Trigger: The nonce is enqueued via the woocommerce_before_add_to_cart_button hook in frontend/index.php. This means the script (and the nonce) will be present on any single product page.
  2. Create Content: To ensure a reliable target, create a standard WooCommerce product.
  3. Navigate: Use the browser to visit the product's URL.
  4. Extract: Execute JavaScript in the browser context to read the localized object:
    • JS Variable: superaddons_products_uploads
    • Nonce Key: nonce (as seen in add_lib() verbatim: 'nonce' => wp_create_nonce('checkout_file_upload'))
    • Command: browser_eval("window.superaddons_products_uploads?.nonce")

5. Exploitation Strategy

  1. Setup Canary: Create a file named canary.txt in the WordPress root directory to safely test deletion.
  2. Retrieve Nonce: Visit a product page and extract the nonce using the strategy above.
  3. Construct Payload:
    • The removal handler likely constructs the path as wp_upload_dir()['basedir'] . '/' . $file.
    • Since basedir is usually wp-content/uploads, the traversal to reach the root is ../../.
  4. Execute Deletion: Send a POST request using http_request.
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body:
      action=superaddons_products_uploads_remove&nonce=[NONCE]&file=../../canary.txt
      
  5. Verify: Check if canary.txt still exists on the filesystem.

6. Test Data Setup

  1. Active Plugin: Ensure products-file-upload-for-woocommerce is installed and activated.
  2. Woocommerce Dependency: Ensure WooCommerce is installed and basic setup is completed.
  3. Target Product:
    wp post create --post_type=product --post_title="Exploit Target" --post_status=publish
    
  4. Canary File:
    echo "vulnerable" > /var/www/html/canary.txt
    

7. Expected Results

  • The AJAX request should return a successful response (likely JSON or a '1').
  • The file /var/www/html/canary.txt should be deleted from the server.
  • If targeting wp-config.php, the site should immediately enter the WordPress installation screen upon the next refresh.

8. Verification Steps

  1. CLI Check: Use ls via the agent's shell or a similar check to confirm the canary is gone.
    ls /var/www/html/canary.txt
    
  2. Response Check: Confirm the AJAX endpoint didn't return a 403 Forbidden (which would indicate a nonce failure).

9. Alternative Approaches

  • Parameter Guessing: If file is not the correct parameter, try file_name, path, or remove_file.
  • Path Depth: If ../../ fails, try deeper traversals (e.g., ../../../canary.txt) in case the plugin stores files in a nested subdirectory like wp-content/uploads/product-files/.
  • Targeting WP-Config: As a final PoC step, attempt to delete wp-config.php and verify the site goes to wp-admin/setup-config.php.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Product File Upload for WooCommerce plugin is vulnerable to unauthenticated arbitrary file deletion due to a lack of sanitization and path validation in its file removal functionality. An attacker can exploit this by providing a path traversal string (e.g., ../../wp-config.php) via an AJAX request, leading to the deletion of critical system files and potentially full site takeover.

Vulnerable Code

// frontend/index.php lines 11-12 (v2.2.3)
add_action( 'wp_ajax_woo_superaddons_products_uploads_remove', array($this,'superaddons_products_uploads_remove') );
add_action( 'wp_ajax_nopriv_woo_superaddons_products_uploads_remove', array($this,'superaddons_products_uploads_remove') );

---

// assets/js/drap_drop_file_upload.js lines 201-207 (v2.2.3)
$("body").on("click",".products-uploads_file_upload_remove",function(e){
   e.preventDefault();
   var cr_name = $(this).data("name") ;
   var data = {
    'action': 'superaddons_products_uploads_remove',
    'name' : cr_name,
    'nonce': superaddons_products_uploads.nonce
  };

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/products-file-upload-for-woocommerce/2.2.3/assets/js/drap_drop_file_upload.js /home/deploy/wp-safety.org/data/plugin-versions/products-file-upload-for-woocommerce/2.2.5/assets/js/drap_drop_file_upload.js
--- /home/deploy/wp-safety.org/data/plugin-versions/products-file-upload-for-woocommerce/2.2.3/assets/js/drap_drop_file_upload.js	2025-06-07 08:28:42.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/products-file-upload-for-woocommerce/2.2.5/assets/js/drap_drop_file_upload.js	2026-02-02 08:06:38.000000000 +0000
@@ -200,22 +201,24 @@
       $("body").on("click",".products-uploads_file_upload_remove",function(e){
          e.preventDefault();
          var cr_name = $(this).data("name") ;
+          console.log(cr_name);
          var data = {
           'action': 'superaddons_products_uploads_remove',
           'name' : cr_name,
           'nonce': superaddons_products_uploads.nonce
         };
         var name = $('#woo_products_upload_files').val().split("|");
+        console.log(name);
         for (var i=name.length-1; i>=0; i--) {
             if (name[i] === cr_name) {
                 name.splice(i, 1);
             }
         }
-         $('#woo_sing_upload_files').val(name.join("|"));
+         $('#woo_products_upload_files').val(name.join("|"));
          $(this).closest('.products-uploads-drop-statusbar').remove();
          jQuery.post(superaddons_products_uploads.ajax_url, data, function(response) {
         });
          return false;
       })
     })
-  })(jQuery);
+  })(jQuery);

Exploit Outline

The exploit targets the unauthenticated AJAX action 'superaddons_products_uploads_remove' (or 'woo_superaddons_products_uploads_remove' depending on version). An attacker first retrieves a valid nonce by visiting any WooCommerce product page where the plugin is active, extracting it from the localized 'superaddons_products_uploads' JavaScript object. The attacker then sends a POST request to 'wp-admin/admin-ajax.php' with the 'action' parameter set to the removal hook, the 'nonce' parameter, and a 'name' parameter containing a path traversal payload such as '../../wp-config.php'. Because the server-side implementation fails to validate that the provided file name is restricted to the intended upload directory, it passes the manipulated path directly to a file system removal function like unlink(), deleting the target file.

Check if your site is affected.

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