File Uploader for WooCommerce is a small plugin that lets shop customers attach files to products and orders via the Uploadcare CDN. Versions up to and including 1.0.3 ship an unauthenticated REST endpoint that will happily write an attacker-controlled file, with an attacker-controlled extension, straight into the uploads directory. That is a remote code execution vulnerability (CVE-2025-13329, CVSS 9.8), and it is about as bad as it gets on a live shop.
What the vulnerability does
The plugin registers POST /wp-json/v1/add-image-data during rest_api_init. The permission callback on that route, in src/JsonApi/class-imagejsonapi.php looks like this:
public function check_user_permissions(): bool {
return true; // Allow logged-out users.
}No nonce, no capability check and no rate limit. Anyone on the web can call it.
The handler takes three parameters from the request, runs them through sanitize_text_field, and passes them into an upload helper:
$uuid = sanitize_text_field( wp_unslash( $request->get_param( 'uuid' ) ) ); $original_file_name = sanitize_text_field( wp_unslash( $request->get_param( 'fileName' ) ) ); $modifications = sanitize_text_field( wp_unslash( $request->get_param( 'cdnUrlModifiers' ) ) ); $url = UploaderHelper::upload_image( $uuid, $original_file_name, $modifications );
The sanitize_text_field filter strips tags and whitespace but it does nothing to stop “shell.php”, “evil.phtml”, “.phar” or any other executable extension.
Inside UploaderHelper::upload_image the plugin uses pathinfo() to pull the extension straight off the supplied filename, builds a path under wp-content/uploads/file-uploader/, then uses Guzzle to stream whatever lives at https://ucarecdn.com/{uuid}/ into that path:
$file_name = sanitize_text_field( $uuid . '.' . $filename_from_url['extension'] ); $file_path = $upload_dir['basedir'] . '/file-uploader/' . $file_name; $client->request( 'GET', // ... esc_url_raw( 'https://ucarecdn.com/' . $uuid . '/' . $modifications ), array( 'sink' => $file_path ) );
There’s no MIME sniff, no call to wp_check_filetype_and_ext(), no wp_handle_sideload(), no allow-list. Uploadcare is a public CDN with a free tier. The attacker can upload their PHP web shell to Uploadcare, grab the UUID, then ask the plugin to pull that payload into the WordPress uploads directory with a “.php” extension. The response even hands back a thumbnail_url pointing at the file, so the attacker knows exactly where to hit it.
That is full unauthenticated RCE on any site running the plugin.
How to check
Run the usual version check:
# Check the version you're running, using WP CLI wp plugin list --name=file-uploader-for-woocommerce --field=version # If it's <= 1.0.3, check your site's access logs. # NOTE: Change the location to match your site's actual access log file(s). grep 'add-image-data' /var/log/access.log
Any 200 responses from an unauthenticated IP should be treated as a probable compromise. If you see hits to add-image-data in your logs from before you patched, assume the site is compromised and treat it accordingly. Rotate secrets, audit users, restore from a backup – whatever your process is.
Finally, look for nasty PHP files in the uploads/ directory:
# Report unusual file extensions in the upload folder (likely shells). find wp-content/uploads/file-uploader -type f \( -name '*.php*' -o -name '*.phtml' -o -name '*.phar' \)
Anything with an executable extension in that folder is a live web shell. It’s not something the plugin places there legitimately.
What to do
Update to >= 1.0.4 now. The patch makes the endpoint require a logged-in user and a valid nonce, which closes the unauthenticated path.
important: Version 1.0.4 carries its own separate vulnerability (CVE-2026-25397 – path traversal). At the time of writing (April 2026), no patched version is available.
Also Important: The underlying problem has only been side-stepped because any logged-in user can still exploit it. Even a user who is only a “subscriber” or a “customer”. If your WooCommerce site has open registration enabled, updating to version “1.0.4” won’t fully protect you.
A more robust solution (apart from replacing the plugin) is to deny access to all PHP files in wp-content/uploads/ . This is a standard security configuration deployed by lots of managed WordPress Hosting providers, and it works well. Just add this to your site’s main “.htaccess”:
# Block PHP execution in the WordPress uploads directory. # Add this near the top of your site's .htaccess file, # before the standard WordPress section. <IfModule mod_rewrite.c> RewriteEngine On RewriteRule ^wp-content/uploads/.*\.(?:php|phtml|phar)$ - [R=403,L,NC] </IfModule>
The take-away advice here is to be very careful with file-upload plugins. Make sure you’re using a robust managed WordPress hosting provider who understands defence-in-depth, proper server configuration and routine updates.