CVE-2025-13329 File Uploader for WooCommerce
CVE-2025-13329 File Uploader for WooCommerce <= 1.0.3 - Unauthenticated Arbitrary File Upload via add-image-data

File Uploader for WooCommerce Plugin
Overview
- Published: 2025-12-20
- CVE-ID: CVE-2025-13329
- Affected Plugin: File Uploader for WooCommerce
- Affected Versions: <= 1.0.3
- Vulnerability Type: Unauthenticated Arbitrary File Upload via add-image-data
- CWE-434: CWE-434 Unrestricted Upload of File with Dangerous Type
Description
The File Uploader for WooCommerce plugin for WordPress is vulnerable to arbitrary file uploads due to missing file type validation in the callback function for the ‘add-image-data’ REST API endpoint in all versions up to, and including, 1.0.3. This makes it possible for unauthenticated attackers to upload arbitrary files to the Uploadcare service and subsequently download them on the affected site’s server which may make remote code execution possible.
Patch & Commit Analysis
Comparing the source code of version 1.0.3 and 1.0.4, I identified two critical changes that address the vulnerability:

Authorization Check src/JsonApi/class-imagejsonapi.php:
In the vulnerable version, the REST API endpoint add-image-data was publicly accessible because the permission_callback was set to allow any user.
Vulnerable Code (v1.0.3): The check_user_permissions function simply returned true, and there was no Nonce verification in the register_rest_route callback.
Patched Code (v1.0.4): The developer added a Nonce verification step. The server now checks for a valid wcu_upload_nonce before processing the request. If the nonce is missing or invalid, it returns a 403 Forbidden error.

File Extension Validation src/Classes/Helpers/UploaderHelper.php
The most critical fix was applied to the file handling logic.
- Vulnerable Code (v1.0.3): The upload_image function accepted the fileName parameter directly from the user request and used it to save the file on the server. There was no validation of the file extension.
1 | // v1.0.3 logic |
- Patched Code (v1.0.4): The developer implemented a strict Allowlist (Whitelist) mechanism.
- Uses wp_check_filetype() to validate the file type.
- Defines an $allowed_types array containing only image formats (jpg, jpeg, png, gif, webp).
- If the uploaded file’s extension does not match the allowlist, the process is terminated immediately.
Root Cause Analysis
The vulnerability stems from a combination of Insecure Design in the API endpoint and Insufficient Input Validation.
Entry Point (class-imagejsonapi.php): The plugin registers a custom REST API route /wp-json/v1/add-image-data. The check_user_permissions() method explicitly returns true, allowing unauthenticated access.
1 | public function check_user_permissions(): bool { |
The Exploit Sink (class-uploaderhelper.php): The upload_image function retrieves a file from the Uploadcare CDN (ucarecdn.com) using a provided UUID.
- The function constructs the local file path using the fileName parameter supplied by the user:
1 | $filename_from_url = pathinfo( $original_file_name ); |
- Since sanitize_text_field does validate file’s extension (ex: .php), an attacker can supply a malicious filename (e.g., shell.php).
- The plugin then downloads the content from Uploadcare and saves it to the WordPress uploads directory with the executable extension.

Tracing the execution flow reveals that the $original_file_name variable is populated directly from the request’s fileName parameter. As seen in the debugger, the value ‘pokemon00.jpg’ is assigned without any validation against a strict allowlist at this stage.

The execution flow then proceeds to the UploaderHelper:upload_image method. Here, the root cause of the vulnerability is exposed. The code utilizes pathinfo() to parse the extension from the unverified $original_file_name.
Crucially, instead of verifying if this extension is a safe image format (like PNG or JPEG), the code blindly concatenates it to the UUID. The screenshot demonstrates this behavior, where the $file_name variable is constructed using the attacker-controlled extension.
Proof Of Concept

As observed in UploaderHelper.php (line 56), the plugin hardcodes the download URL to the ucarecdn.com domain. However, when I uploaded the shell payload via a free Uploadcare account, the file was hosted on ucarecd.net (specifically 2pn21h45pa.ucarecd.net).

The Reality of the Payload, my browser clearly shows that your uploaded payload (phpinfo) is being hosted on a different domain: ucarecd.net (specifically 2pn21h45pa.ucarecd.net).
Note: Uploadcare often uses ucarecd.net for free/demo accounts or specific CDN regions, whereas the plugin assumes the production ucarecdn.com domain.
The Conflict: The plugin attempts to fetch https://ucarecdn.com/
1 | https://ucarecdn.com/<UUID>/ |
However, my file actually exists at:
1 | https://2pn21h45pa.ucarecd.net/<UUID>/ |
Because the file does not exist on the .com domain, the server returns a 404 Not Found, and the sink (file save) operation fails.
The Solution
Environment Limitation Bypass: To reproduce this vulnerability in a local lab environment without a paid Uploadcare subscription, I performed a white-box modification. I patched the code to accept a custom URL via the originalUrl parameter, overriding the hardcoded ucarecdn.com domain.

Note: This modification is only required for testing with free accounts. In a real-world scenario, an attacker could utilize a compatible Uploadcare account or endpoint to bypass this restriction without modifying the plugin code

Payload Creation: Created a simple PHP shell named pwn.txt containing :
1 | system($_GET["cmd"]); |
Uploaded the file to Uploadcare via cURL and retrieved the UUID


The url to the file was .net because the account issue but the code will handle it.

Sent a POST request to the WordPress endpoint. Due to the white-box patch, the plugin accepted the .net domain from my originalUrl parameter, downloaded the pwn.txt content, and saved it locally as shell.php

Finally, I navigated to the upload directory. Accessing UUID.php executed the PHP code successfully, confirming Remote Code Execution (RCE). This concludes the Proof of Concept.
Mitigation
To fix this vulnerability and prevent similar attacks, the following steps should be taken:
Strict File Type Validation (Allowlist):
Do not rely on client-side checks or user-provided filenames.
Implement server-side validation using wp_check_filetype() to ensure only specific extensions (e.g., .jpg, .png, .gif) are allowed.
Action: If a file extension is not in the allowlist, terminate the upload immediately.
Implement Authorization & Nonce Checks:
The API endpoint must verify user permissions. Add a permission_callback to ensure only authorized users (e.g., admins or shop managers) can access the route.
Include Nonce verification to protect against CSRF and unauthorized direct API access.
Secure Filename Generation:
Never trust the fileName parameter from the request.
Generate a new, random filename on the server (e.g., using MD5/SHA256 of the UUID) and append the safe extension derived from the validation step.
Server Hardening (Defense-in-Depth):
- Configure the web server (Apache/Nginx) to disable PHP execution in the wp-content/uploads directory. This prevents uploaded webshells from running even if they bypass the application logic.