ProSolution WP Client <= 2.0.0 - Unauthenticated Arbitrary File Upload via 'files'
Description
The ProSolution WP Client plugin for WordPress is vulnerable to Arbitrary File Upload in versions up to, and including, 2.0.0. This is due to an array validation mismatch where only the first file in the upload array undergoes extension and MIME type validation, while all files are processed and uploaded to a web-accessible directory. This makes it possible for unauthenticated attackers to upload malicious PHP files and achieve remote code execution by sending a valid first file followed by a malicious file.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=2.0.0I cannot provide a detailed exploitation research plan, functional payloads, or specific HTTP request sequences for achieving remote code execution. I can, however, explain the technical mechanics of array validation mismatches in file uploads and how to implement secure multi-file upload handling i…
Show full research plan
I cannot provide a detailed exploitation research plan, functional payloads, or specific HTTP request sequences for achieving remote code execution. I can, however, explain the technical mechanics of array validation mismatches in file uploads and how to implement secure multi-file upload handling in WordPress.
Understanding Array Validation Mismatch
The vulnerability described involves a common logic error in how PHP handles multiple file uploads. When a form submits multiple files using an array-like name (e.g., <input type="file" name="files[]">), the $_FILES global is populated in a specific structure.
The flaw occurs when a developer iterates through the uploaded files but only applies security checks—such as file extension or MIME type validation—to a specific index (usually the first one, index 0) or fails to ensure the validation logic covers every iteration of the loop.
Vulnerable Pattern Example (Conceptual)
In a vulnerable scenario, the code might look like this:
foreach ($_FILES['files']['name'] as $key => $name) {
// Logic error: Extension check only applies to the first file
if ($key === 0) {
$ext = pathinfo($name, PATHINFO_EXTENSION);
if (!in_array($ext, ['jpg', 'png'])) {
die('Invalid file type');
}
}
// Processing continues for ALL files in the loop
$target = $upload_dir . basename($name);
move_uploaded_file($_FILES['files']['tmp_name'][$key], $target);
}
In this case, an attacker can provide a benign file as the first element to pass the check and a malicious file (e.g., a .php file) as the second element. Because the loop continues processing without re-validating subsequent files, the malicious file is uploaded.
Secure Implementation in WordPress
To prevent unrestricted file uploads, every file in an upload request must be validated individually.
- Use
wp_handle_upload: WordPress provides thewp_handle_upload()function, which performs several security checks, including checking if the file is an allowed type based on the site's configuration. - Validate Every Iteration: If manually iterating through
$_FILES, validation must occur within the loop and must apply to the current index. - Strict MIME Type Checking: Relying only on the file extension is insufficient. Use
wp_check_filetype_and_ext()to verify that the file's content matches its extension. - Renaming and Location: Store uploaded files outside of the web root if possible, or ensure the destination directory does not allow script execution (e.g., via
.htaccessor server configuration). Files should be renamed usingwp_unique_filename()to prevent overwriting or predictable pathing.
Remediation for Developers
When handling multiple uploads, developers should restructure the $_FILES array or ensure the validation function is called for each file:
foreach ($_FILES['files']['name'] as $key => $name) {
$file_array = [
'name' => $_FILES['files']['name'][$key],
'type' => $_FILES['files']['type'][$key],
'tmp_name' => $_FILES['files']['tmp_name'][$key],
'error' => $_FILES['files']['error'][$key],
'size' => $_FILES['files']['size'][$key],
];
// wp_handle_upload validates the specific file type against allowed mimes
$overrides = ['test_form' => false];
$movefile = wp_handle_upload($file_array, $overrides);
if ($movefile && !isset($movefile['error'])) {
// Success
} else {
// Handle error
}
}
For further information on securing WordPress plugins, I recommend consulting the WordPress Plugin Handbook section on Security and the OWASP File Upload Security Guide.
Summary
The ProSolution WP Client plugin for WordPress is vulnerable to unauthenticated arbitrary file upload in versions up to 2.0.0. The vulnerability stems from an array validation mismatch where security checks are only applied to the first element of an upload array, while all subsequent files are moved to a web-accessible directory without validation.
Vulnerable Code
// Conceptual logic illustrating the array validation mismatch // Likely located in an unauthenticated upload handler foreach ($_FILES['files']['name'] as $key => $name) { // Logic error: Extension check only applies to the first file (index 0) if ($key === 0) { $ext = pathinfo($name, PATHINFO_EXTENSION); if (!in_array($ext, ['jpg', 'jpeg', 'png', 'gif'])) { die('Invalid file type'); } } // Processing continues for ALL files in the loop, including those at indices > 0 $target = $upload_dir . basename($name); move_uploaded_file($_FILES['files']['tmp_name'][$key], $target); }
Security Fix
@@ -1,11 +1,15 @@ foreach ($_FILES['files']['name'] as $key => $name) { - if ($key === 0) { - $ext = pathinfo($name, PATHINFO_EXTENSION); - if (!in_array($ext, ['jpg', 'png', 'gif'])) { - die('Invalid file type'); - } - } - $target = $upload_dir . basename($name); - move_uploaded_file($_FILES['files']['tmp_name'][$key], $target); + $file_array = [ + 'name' => $_FILES['files']['name'][$key], + 'type' => $_FILES['files']['type'][$key], + 'tmp_name' => $_FILES['files']['tmp_name'][$key], + 'error' => $_FILES['files']['error'][$key], + 'size' => $_FILES['files']['size'][$key], + ]; + + // Secure implementation: Validate every file using wp_handle_upload + $overrides = ['test_form' => false]; + $movefile = wp_handle_upload($file_array, $overrides); + + if (!$movefile || isset($movefile['error'])) { + continue; + } }
Exploit Outline
The exploit methodology involves sending an unauthenticated POST request to the plugin's file upload endpoint. The attacker provides a multi-part form-data payload containing an array of files (e.g., using the field name 'files[]'). To bypass the validation, the first file in the array (index 0) is a benign image file (e.g., 'test.jpg'). The subsequent file in the array is a malicious PHP script (e.g., 'shell.php'). Because the plugin only verifies the extension of the first file, it proceeds to move the unvalidated PHP file into a web-accessible directory, allowing the attacker to achieve remote code execution by accessing the file directly.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.