Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/prosolution-wp-client/prosolwpclient.php
+++ b/prosolution-wp-client/prosolwpclient.php
@@ -16,7 +16,7 @@
* Plugin Name: ProSolution WP Client
* Plugin URI: https://prosolution.com/produkte-und-services/workexpert.html
* Description: WordPress client for ProSolution
- * Version: 1.9.9
+ * Version: 2.0.0
* Author: ProSolution
* Author URI: https://www.prosolution.com
* License: GPL-2.0+
@@ -41,7 +41,7 @@
defined('PROSOLWPCLIENT_PLUGIN_NAME') or define('PROSOLWPCLIENT_PLUGIN_NAME', 'prosolwpclient');
- defined('PROSOLWPCLIENT_PLUGIN_VERSION') or define('PROSOLWPCLIENT_PLUGIN_VERSION', '1.9.9');
+ defined('PROSOLWPCLIENT_PLUGIN_VERSION') or define('PROSOLWPCLIENT_PLUGIN_VERSION', '2.0.0');
defined('PROSOLWPCLIENT_BASE_NAME') or define('PROSOLWPCLIENT_BASE_NAME', plugin_basename(__FILE__));
defined('PROSOLWPCLIENT_ROOT_PATH') or define('PROSOLWPCLIENT_ROOT_PATH', plugin_dir_path(__FILE__));
defined('PROSOLWPCLIENT_ROOT_URL') or define('PROSOLWPCLIENT_ROOT_URL', plugin_dir_url(__FILE__));
--- a/prosolution-wp-client/public/class-prosolwpclient-public.php
+++ b/prosolution-wp-client/public/class-prosolwpclient-public.php
@@ -995,43 +995,117 @@
//if the upload dir for prosolwpclient is not created then then create it
$dir_info = $this->proSol_checkUploadDir();
- $submit_data = $_FILES["files"];
- $mime_type = isset( $submit_data['type'] ) ? $submit_data['type'][0] : '';
- $ext = proSol_mimeExt($mime_type);
+ $submit_data = $_FILES["files"] ?? null;
+
+ //this is for if someone somehow able to run this function without file
+ if ( ! $submit_data ) {
+ die(__("No file uploaded", "prosolwpclient"));
+ }
+
+ // get file name and temp file location and sanitize them
+ $org_filename = isset( $submit_data['name'][0] ) ? sanitize_file_name( $submit_data['name'][0] ) : '';
+ $tmp_fileloc = isset( $submit_data['tmp_name'][0] ) ? $submit_data['tmp_name'][0] : '';
+
+ // if file name or location empty, process must be aborted
+ if ( empty( $org_filename ) || empty( $tmp_fileloc ) || ! is_uploaded_file( $tmp_fileloc ) ) {
+ die(__("Invalid file", "prosolwpclient"));
+ }
+ //check file extension for uploaded "up" file
+ $up_fileext = strtolower( pathinfo( $org_filename, PATHINFO_EXTENSION ) );
+
+ //since most of cv or profile picture are typically using this format, we should whitelist these extension only.
+ //do not use proSol_mimeExt function, it allow all kind of extension including big nono one like php or other programming language.
+ $whitelist_ext = array( 'jpg', 'jpeg', 'png', 'gif', 'webp', 'pdf', 'doc', 'docx' );
+
+ //check extension first
+ if ( ! in_array( $up_fileext, $whitelist_ext, true ) ) {
+ die(__("File type not allowed", "prosolwpclient"));
+ }
+
+ //check for REAL mime type, $submit_data['type'] only check for surface-level.
+ $finfoObj = new finfo( FILEINFO_MIME_TYPE );
+ $true_mmime = $finfoObj->file( $tmp_fileloc );
+
+ //syntax below is big nono, don't use it to check mime!!!
+ //$mime_type = isset( $submit_data['type'] ) ? $submit_data['type'][0] : '';
+ //again do not use prosol_mimeext, they will allow script or programming language
+ //$ext = proSol_mimeExt($mime_type);
+
+ $wp_mime_chk = wp_check_filetype( $org_filename );
+ if ( $wp_mime_chk['type'] == false ) {
+ die(__("File type is not allowed.", "prosolwpclient"));
+ }
+
+ //only listed mimes type are allow
+ $whitelist_mimes = array(
+ 'jpg' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'png' => 'image/png',
+ 'gif' => 'image/gif',
+ 'webp' => 'image/webp',
+ 'pdf' => 'application/pdf',
+ 'doc' => 'application/msword',
+ 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ );
+
+ //check for real hidden mimes type
+ if ( ! isset( $whitelist_mimes[ $up_fileext ] ) || $true_mmime !== $whitelist_mimes[ $up_fileext ] ) {
+ die(__("File content does not match its extension", "prosolwpclient"));
+ }
+
+ if ( in_array( $up_fileext, array( 'jpg', 'jpeg', 'png', 'gif', 'webp' ), true ) ) {
+ //for image upload we can also verified image via dimension size like height and width, fake image file will be false result
+ $img_dimension = @getimagesize( $tmp_fileloc );
+ if ( $img_dimension === false ) {
+ die(__("Invalid image dimension", "prosolwpclient"));
+ }
+ }
- if ( in_array( $ext, proSol_imageExtArr() ) || in_array( $ext, proSol_documentExtArr() ) ) {
- if ( is_array( $dir_info ) && sizeof( $dir_info ) > 0 && array_key_exists( 'folder_exists', $dir_info ) && $dir_info['folder_exists'] == 1 ) {
- $options = array(
- 'script_url' => admin_url( 'admin-ajax.php' ),
- 'upload_dir' => $dir_info['prosol_base_dir'],
- 'upload_url' => $dir_info['prosol_base_url'],
- 'print_response' => false,
- );
-
- $upload_handler = new CBXProSolWpClient_UploadHandler( $options );
-
- $response_obj = $upload_handler->response['files'][0];
- if ( $response_obj->name != '' ) {
- if ( ! session_id() ) {
- session_start();
- }
-
- $attached_file_name = $response_obj->name;
-
- $extension = pathinfo( $attached_file_name, PATHINFO_EXTENSION );
-
- $newfilename = wp_create_nonce( session_id() . time() ) . '.' . $extension;
- $rename_status = rename( $dir_info['prosol_base_dir'] . $attached_file_name, $dir_info['prosol_base_dir'] . $newfilename );
- $response_obj->newfilename = $newfilename;
- $response_obj->rename_status = $rename_status;
- $response_obj->extension = $extension;
-
- $return_response = array( 'files' => array( 0 => $response_obj ) );
- echo json_encode( $return_response );
- wp_die();
+ if ( is_array( $dir_info ) && sizeof( $dir_info ) > 0 && array_key_exists( 'folder_exists', $dir_info ) && $dir_info['folder_exists'] == 1 ) {
+ $options = array(
+ 'script_url' => admin_url( 'admin-ajax.php' ),
+ 'upload_dir' => $dir_info['prosol_base_dir'],
+ 'upload_url' => $dir_info['prosol_base_url'],
+ 'print_response' => false,
+ );
+
+ $upload_handler = new CBXProSolWpClient_UploadHandler( $options );
+
+ $response_obj = $upload_handler->response['files'][0];
+
+ //change $response_obj->name != '' to !empty( $response_obj->name )
+ if ( ! empty( $response_obj->name ) ) {
+ if ( ! session_id() ) {
+ session_start();
}
+
+ $attached_file_name = $response_obj->name;
+
+ //check final result extension, and make it universal lowercase
+ $fin_ext = strtolower( pathinfo( $attached_file_name, PATHINFO_EXTENSION ) );
+
+ //check it one last time on the result
+ if ( ! in_array( $fin_ext, $whitelist_ext, true ) ) {
+ die(__("File type mismatch after upload", "prosolwpclient"));
+ }
+
+ $newfilename = wp_create_nonce( session_id() . time() ) . '.' . $fin_ext;
+ $rename_status = rename( $dir_info['prosol_base_dir'] . $attached_file_name, $dir_info['prosol_base_dir'] . $newfilename );
+ $response_obj->newfilename = $newfilename;
+ $response_obj->rename_status = $rename_status;
+ $response_obj->extension = $fin_ext;
+
+ $return_response = array( 'files' => array( 0 => $response_obj ) );
+ //success return
+ echo json_encode( $return_response );
+ wp_die();
}
}
+
+ //default return
+ wp_send_json_error( array( 'error' => 'Upload failed' ) );
+ wp_die();
+
}
/**