CVE-2022-0377: Use of a Broken or Risky Cryptographic Algorithm (CWE-327)
value: Users of the LearnPress WordPress plugin before 4.1.5 can upload an image as a profile avatar after the registration.  After this process the user crops and saves the image. Then a "POST" request that contains user supplied name of the image is sent to the server for renaming and cropping of the image. As a result of this request, the name of the user-supplied image is changed with a MD5 value. This process can be conducted only when type of the image is JPG or PNG. An attacker can use this vulnerability in order to rename an arbitrary image file. By doing this, they could destroy the design of the web site.
CWE description: The product uses a broken or risky cryptographic algorithm or protocol.
CVSS v3 base score: 4.3
Published: 2022-02-28T09:15Z
Repository: https://github.com/LearnPress/learnpress
Language: PHP
Files changed: assets/src/js/frontend/profile.js, inc/admin/class-lp-admin-ajax.php, inc/class-lp-ajax.php, inc/user/lp-user-functions.php
Commit date: 2022-01-14 09:56:52 +0700
Commit message: = 4.1.4.2 =
~ Modified: function upload, crop avatar
~ Added: function remove avatar

Diff: diff --git a/assets/src/js/frontend/profile.js b/assets/src/js/frontend/profile.js
index 0e4ea3e8..a4ad8fa7 100644
--- a/assets/src/js/frontend/profile.js
+++ b/assets/src/js/frontend/profile.js
@@ -22,11 +22,12 @@
 		},
 		_save( e ) {
 			e.preventDefault();
 			const self = this;
 			$.ajax( {
-				url: '?lp-ajax=save-uploaded-user-avatar',
+				//url: '?lp-ajax=save-uploaded-user-avatar',
+				url: ( typeof lpGlobalSettings !== 'undefined' ? lpGlobalSettings.ajax : '' ).addQueryVar( 'action', 'learnpress_save-uploaded-user-avatar' ),
 				data: this.$( '.lp-avatar-crop-image' ).serializeJSON(),
 				type: 'post',
 				success( response ) {
 					response = LP.parseJSON( response );
 					if ( ! response.success ) {
@@ -51,15 +52,24 @@
 			// eslint-disable-next-line no-alert
 			if ( ! confirm( 'Are you sure?' ) ) {
 				return;
 			}
 
-			this.$().removeAttr( 'data-custom' );
-			this.$( '.profile-picture' ).toggleClass( 'profile-avatar-current' );
-			this.$( '#submit' ).prop( 'disabled', false );
+			const el = this;
 
-			$( '.lp-user-profile-avatar' ).html( this.$( '.profile-avatar-current' ).find( 'img' ).clone() );
+			$.ajax( {
+				url: ( typeof lpGlobalSettings !== 'undefined' ? lpGlobalSettings.ajax : '' ).addQueryVar( 'action', 'learnpress_remove-avatar' ),
+				data: {},
+				type: 'post',
+				success( response ) {
+					el.$().removeAttr( 'data-custom' );
+					el.$( '.profile-picture' ).toggleClass( 'profile-avatar-current' );
+					el.$( '#submit' ).prop( 'disabled', false );
+
+					$( '.lp-user-profile-avatar' ).html( el.$( '.profile-avatar-current' ).find( 'img' ).clone() );
+				},
+			} );
 		},
 		_upload( e ) {
 			e.preventDefault();
 		},
 		_cancel( e ) {
diff --git a/inc/admin/class-lp-admin-ajax.php b/inc/admin/class-lp-admin-ajax.php
index 52c68523..da7159cf 100644
--- a/inc/admin/class-lp-admin-ajax.php
+++ b/inc/admin/class-lp-admin-ajax.php
@@ -1194,35 +1194,134 @@ if ( ! class_exists( 'LP_Admin_Ajax' ) ) {
 			learn_press_send_json( $response );
 
 			die();
 		}*/
 
+		/**
+		 * Upload avatar of user
+		 *
+		 * @editor tungnx
+		 * @modify 4.1.4.2
+		 */
 		public static function upload_user_avatar() {
+			$user_id = get_current_user_id();
+
+			if ( ! $user_id ) {
+				return;
+			}
+
 			$file       = $_FILES['lp-upload-avatar'];
 			$upload_dir = learn_press_user_profile_picture_upload_dir();
 
 			add_filter( 'upload_dir', array( __CLASS__, '_user_avatar_upload_dir' ), 10000 );
 
+			$file_info_arr        = explode( '.', $file['name'] );
+			$file_info_arr_length = count( $file_info_arr );
+			$file_ext_index       = $file_info_arr_length - 1;
+			$file_ext             = $file_info_arr[ $file_ext_index ];
+			$file['name']         = $user_id . '.' . $file_ext;
+
+			// Delete old image if exists
+			$path_img = get_user_meta( $user_id, '_lp_profile_picture', true );
+			if ( $path_img ) {
+				$path = $upload_dir['basedir'] . '/' . $path_img;
+				if ( file_exists( $path ) ) {
+					@unlink( $path );
+				}
+			}
+
 			$result = wp_handle_upload(
 				$file,
 				array(
 					'test_form' => false,
 				)
 			);
 
 			remove_filter( 'upload_dir', array( __CLASS__, '_user_avatar_upload_dir' ), 10000 );
 			if ( is_array( $result ) ) {
 				$result['name'] = $upload_dir['subdir'] . '/' . basename( $result['file'] );
+				update_user_meta( $user_id, '_lp_profile_picture', $result['name'] );
 				unset( $result['file'] );
 			} else {
 				$result = array(
 					'error' => __( 'Profile picture upload failed', 'learnpress' ),
 				);
 			}
 			learn_press_send_json( $result );
 		}
 
+		/**
+		 * Crop avatar of user
+		 *
+		 * @editor tungnx
+		 * @return void
+		 */
+		public static function save_uploaded_user_avatar() {
+			$avatar_data = wp_parse_args(
+				LP_Request::get( 'lp-user-avatar-crop' ),
+				array(
+					'name'   => '',
+					'width'  => '',
+					'height' => '',
+					'points' => '',
+					'nonce'  => '',
+				)
+			);
+
+			$current_user_id = get_current_user_id();
+
+			if ( ! wp_verify_nonce( $avatar_data['nonce'], 'save-uploaded-profile-' . $current_user_id ) ) {
+				die( 'ERROR VERIFY NONCE!' );
+			}
+
+			$url = learn_press_update_user_profile_avatar();
+			if ( $url ) {
+				learn_press_send_json(
+					array(
+						'success' => true,
+						'avatar'  => sprintf( '<img src="%s" />', $url ),
+					)
+				);
+			};
+
+			wp_die();
+		}
+
+		/**
+		 * Remove avatar of user
+		 *
+		 * @author tungnx
+		 * @since 4.1.4.2
+		 * @version 1.0.0
+		 * @return void
+		 */
+		public static function remove_avatar() {
+			$response = new LP_REST_Response();
+
+			try {
+				$user_id = get_current_user_id();
+				if ( ! $user_id ) {
+					throw new Exception( __( 'User is invalid', 'learnpress' ) );
+				}
+
+				// Delete old image if exists
+				$path_img = get_user_meta( $user_id, '_lp_profile_picture', true );
+				if ( $path_img ) {
+					$upload_dir = learn_press_user_profile_picture_upload_dir();
+					$path       = $upload_dir['basedir'] . '/' . $path_img;
+					if ( file_exists( $path ) ) {
+						unlink( $path );
+						$response->status = 'success';
+					}
+				}
+			} catch ( Throwable $e ) {
+				$response->message = $e->getMessage();
+			}
+
+			wp_send_json( $response );
+		}
+
 		public static function _user_avatar_upload_dir( $dir ) {
 			$dir = learn_press_user_profile_picture_upload_dir();
 
 			return $dir;
 		}
@@ -1258,9 +1357,11 @@ if ( ! class_exists( 'LP_Admin_Ajax' ) ) {
 		}
 	}
 
 	if ( defined( 'DOING_AJAX' ) ) {
 		add_action( 'wp_ajax_learnpress_upload-user-avatar', array( 'LP_Admin_Ajax', 'upload_user_avatar' ) );
+		add_action( 'wp_ajax_learnpress_save-uploaded-user-avatar', array( 'LP_Admin_Ajax', 'save_uploaded_user_avatar' ) );
+		add_action( 'wp_ajax_learnpress_remove-avatar', array( 'LP_Admin_Ajax', 'remove_avatar' ) );
 	}
 
 	add_action( 'init', array( 'LP_Admin_Ajax', 'init' ) );
 }
diff --git a/inc/class-lp-ajax.php b/inc/class-lp-ajax.php
index 7edb84f7..91390b5a 100644
--- a/inc/class-lp-ajax.php
+++ b/inc/class-lp-ajax.php
@@ -9,17 +9,17 @@ if ( ! class_exists( 'LP_AJAX' ) ) {
 		public static function init() {
 			$ajax_events = array(
 				'checkout-user-email-exists:nopriv',
 				'recover-order',
 				'request-become-a-teacher:nonce',
-				'upload-user-avatar',
+				// 'upload-user-avatar',
 				'checkout:nopriv',
 				'complete-lesson',
 				'finish-course', // finish_course.
 				// 'retake-course', // retake_course.
 				'external-link:nopriv',
-				'save-uploaded-user-avatar',
+				// 'save-uploaded-user-avatar',
 				'load-more-courses',
 			);
 
 			$ajax_events = apply_filters( 'learn-press/ajax/events', $ajax_events );
 
@@ -38,11 +38,11 @@ if ( ! class_exists( 'LP_AJAX' ) ) {
 				}
 
 				LP_Request::register_ajax( $action, $callback );
 			}
 
-			add_action( 'wp_ajax_learnpress_upload-user-avatar', array( __CLASS__, 'upload_user_avatar' ) );
+			//add_action( 'wp_ajax_learnpress_upload-user-avatar', array( __CLASS__, 'upload_user_avatar' ) );
 		}
 
 		public static function load_more_courses() {
 			$type     = LP_Request::get( 'type' );
 			$user_id  = LP_Request::get_int( 'user', 0 );
@@ -169,11 +169,11 @@ if ( ! class_exists( 'LP_AJAX' ) ) {
 			$response['output'] = apply_filters( 'learnpress/guest_checkout_email_exist_output', $output, $email );
 
 			learn_press_maybe_send_json( $response );
 		}
 
-		public static function upload_user_avatar() {
+		/*public static function upload_user_avatar() {
 			$file       = $_FILES['lp-upload-avatar'];
 			$upload_dir = learn_press_user_profile_picture_upload_dir();
 
 			add_filter( 'upload_dir', array( __CLASS__, '_user_avatar_upload_dir' ), 10000 );
 
@@ -194,44 +194,11 @@ if ( ! class_exists( 'LP_AJAX' ) ) {
 					'error' => __( 'Profile picture upload failed', 'learnpress' ),
 				);
 			}
 
 			learn_press_send_json( $result );
-		}
-
-		public static function save_uploaded_user_avatar() {
-			$avatar_data = wp_parse_args(
-				LP_Request::get( 'lp-user-avatar-crop' ),
-				array(
-					'name'   => '',
-					'width'  => '',
-					'height' => '',
-					'points' => '',
-					'nonce'  => '',
-				)
-			);
-
-			$current_user_id = get_current_user_id();
-
-			if ( ! wp_verify_nonce( $avatar_data['nonce'], 'save-uploaded-profile-' . $current_user_id ) ) {
-				die( 'ERROR VERIFY NONCE!' );
-			}
-
-			$url = learn_press_update_user_profile_avatar();
-			if ( $url ) {
-				$user = learn_press_get_current_user();
-
-				learn_press_send_json(
-					array(
-						'success' => true,
-						'avatar'  => sprintf( '<img src="%s" />', $url ),
-					)
-				);
-			};
-
-			wp_die();
-		}
+		}*/
 
 		public static function _user_avatar_upload_dir( $dir ) {
 			$dir = learn_press_user_profile_picture_upload_dir();
 
 			return $dir;
diff --git a/inc/user/lp-user-functions.php b/inc/user/lp-user-functions.php
index 152679ee..7752e78c 100644
--- a/inc/user/lp-user-functions.php
+++ b/inc/user/lp-user-functions.php
@@ -1077,10 +1077,11 @@ function learn_press_update_user_profile() {
 /**
  * Update user avatar
  */
 function learn_press_update_user_profile_avatar() {
 	$user_id = get_current_user_id();
+	$data    = learn_press_get_request( 'lp-user-avatar-crop' );
 
 	if ( ! $user_id ) {
 		return new WP_Error( 2, 'User is invalid!' );
 	}
 
@@ -1090,13 +1091,15 @@ function learn_press_update_user_profile_avatar() {
 		delete_user_meta( get_current_user_id(), '_lp_profile_picture' );
 
 		return false;
 	}
 
-	$data = learn_press_get_request( 'lp-user-avatar-crop' );
+	$path_img = get_user_meta( $user_id, '_lp_profile_picture', true );
 
-	if ( ! $data || ! ( $path = $upload_dir['basedir'] . $data['name'] ) && file_exists( $path ) ) {
+	$path = $upload_dir['basedir'] . $path_img;
+
+	if ( ! file_exists( $path ) ) {
 		return false;
 	}
 
 	$filetype = wp_check_filetype( $path );
 
@@ -1142,19 +1145,12 @@ function learn_press_update_user_profile_avatar() {
 	}
 
 	$new_avatar = false;
 
 	if ( file_exists( $output ) ) {
-
-		$old_avatar = get_user_meta( $user_id, '_lp_profile_picture', true );
-
-		if ( file_exists( $upload_dir['basedir'] . '/' . $old_avatar ) ) {
-			@unlink( $upload_dir['basedir'] . '/' . $old_avatar );
-		}
-
 		$new_avatar = preg_replace( '!^/!', '', $upload_dir['subdir'] ) . '/' . $newname;
-		update_user_meta( $user_id, '_lp_profile_picture', $new_avatar );
+		update_user_meta( $user_id, '_lp_profile_picture', '/' . $new_avatar );
 		update_user_meta( $user_id, '_lp_profile_picture_changed', 'yes' );
 
 		$new_avatar = $upload_dir['baseurl'] . '/' . $new_avatar;
 	}
 

Vulnerable code: 				url: '?lp-ajax=save-uploaded-user-avatar',
			this.$().removeAttr( 'data-custom' );
			this.$( '.profile-picture' ).toggleClass( 'profile-avatar-current' );
			this.$( '#submit' ).prop( 'disabled', false );
			$( '.lp-user-profile-avatar' ).html( this.$( '.profile-avatar-current' ).find( 'img' ).clone() );
				'upload-user-avatar',
				'save-uploaded-user-avatar',
			add_action( 'wp_ajax_learnpress_upload-user-avatar', array( __CLASS__, 'upload_user_avatar' ) );
		public static function upload_user_avatar() {
		}

		public static function save_uploaded_user_avatar() {
			$avatar_data = wp_parse_args(
				LP_Request::get( 'lp-user-avatar-crop' ),
				array(
					'name'   => '',
					'width'  => '',
					'height' => '',
					'points' => '',
					'nonce'  => '',
				)
			);

			$current_user_id = get_current_user_id();

			if ( ! wp_verify_nonce( $avatar_data['nonce'], 'save-uploaded-profile-' . $current_user_id ) ) {
				die( 'ERROR VERIFY NONCE!' );
			}

			$url = learn_press_update_user_profile_avatar();
			if ( $url ) {
				$user = learn_press_get_current_user();

				learn_press_send_json(
					array(
						'success' => true,
						'avatar'  => sprintf( '<img src="%s" />', $url ),
					)
				);
			};

			wp_die();
		}
	$data = learn_press_get_request( 'lp-user-avatar-crop' );
	if ( ! $data || ! ( $path = $upload_dir['basedir'] . $data['name'] ) && file_exists( $path ) ) {

		$old_avatar = get_user_meta( $user_id, '_lp_profile_picture', true );

		if ( file_exists( $upload_dir['basedir'] . '/' . $old_avatar ) ) {
			@unlink( $upload_dir['basedir'] . '/' . $old_avatar );
		}

		update_user_meta( $user_id, '_lp_profile_picture', $new_avatar );
Fixed code: 				//url: '?lp-ajax=save-uploaded-user-avatar',
				url: ( typeof lpGlobalSettings !== 'undefined' ? lpGlobalSettings.ajax : '' ).addQueryVar( 'action', 'learnpress_save-uploaded-user-avatar' ),
			const el = this;
			$.ajax( {
				url: ( typeof lpGlobalSettings !== 'undefined' ? lpGlobalSettings.ajax : '' ).addQueryVar( 'action', 'learnpress_remove-avatar' ),
				data: {},
				type: 'post',
				success( response ) {
					el.$().removeAttr( 'data-custom' );
					el.$( '.profile-picture' ).toggleClass( 'profile-avatar-current' );
					el.$( '#submit' ).prop( 'disabled', false );

					$( '.lp-user-profile-avatar' ).html( el.$( '.profile-avatar-current' ).find( 'img' ).clone() );
				},
			} );
		/**
		 * Upload avatar of user
		 *
		 * @editor tungnx
		 * @modify 4.1.4.2
		 */
			$user_id = get_current_user_id();

			if ( ! $user_id ) {
				return;
			}

			$file_info_arr        = explode( '.', $file['name'] );
			$file_info_arr_length = count( $file_info_arr );
			$file_ext_index       = $file_info_arr_length - 1;
			$file_ext             = $file_info_arr[ $file_ext_index ];
			$file['name']         = $user_id . '.' . $file_ext;

			// Delete old image if exists
			$path_img = get_user_meta( $user_id, '_lp_profile_picture', true );
			if ( $path_img ) {
				$path = $upload_dir['basedir'] . '/' . $path_img;
				if ( file_exists( $path ) ) {
					@unlink( $path );
				}
			}

				update_user_meta( $user_id, '_lp_profile_picture', $result['name'] );
		/**
		 * Crop avatar of user
		 *
		 * @editor tungnx
		 * @return void
		 */
		public static function save_uploaded_user_avatar() {
			$avatar_data = wp_parse_args(
				LP_Request::get( 'lp-user-avatar-crop' ),
				array(
					'name'   => '',
					'width'  => '',
					'height' => '',
					'points' => '',
					'nonce'  => '',
				)
			);

			$current_user_id = get_current_user_id();

			if ( ! wp_verify_nonce( $avatar_data['nonce'], 'save-uploaded-profile-' . $current_user_id ) ) {
				die( 'ERROR VERIFY NONCE!' );
			}

			$url = learn_press_update_user_profile_avatar();
			if ( $url ) {
				learn_press_send_json(
					array(
						'success' => true,
						'avatar'  => sprintf( '<img src="%s" />', $url ),
					)
				);
			};

			wp_die();
		}

		/**
		 * Remove avatar of user
		 *
		 * @author tungnx
		 * @since 4.1.4.2
		 * @version 1.0.0
		 * @return void
		 */
		public static function remove_avatar() {
			$response = new LP_REST_Response();

			try {
				$user_id = get_current_user_id();
				if ( ! $user_id ) {
					throw new Exception( __( 'User is invalid', 'learnpress' ) );
				}

				// Delete old image if exists
				$path_img = get_user_meta( $user_id, '_lp_profile_picture', true );
				if ( $path_img ) {
					$upload_dir = learn_press_user_profile_picture_upload_dir();
					$path       = $upload_dir['basedir'] . '/' . $path_img;
					if ( file_exists( $path ) ) {
						unlink( $path );
						$response->status = 'success';
					}
				}
			} catch ( Throwable $e ) {
				$response->message = $e->getMessage();
			}

			wp_send_json( $response );
		}

		add_action( 'wp_ajax_learnpress_save-uploaded-user-avatar', array( 'LP_Admin_Ajax', 'save_uploaded_user_avatar' ) );
		add_action( 'wp_ajax_learnpress_remove-avatar', array( 'LP_Admin_Ajax', 'remove_avatar' ) );
				// 'upload-user-avatar',
				// 'save-uploaded-user-avatar',
			//add_action( 'wp_ajax_learnpress_upload-user-avatar', array( __CLASS__, 'upload_user_avatar' ) );
		/*public static function upload_user_avatar() {
		}*/
	$data    = learn_press_get_request( 'lp-user-avatar-crop' );
	$path_img = get_user_meta( $user_id, '_lp_profile_picture', true );
	$path = $upload_dir['basedir'] . $path_img;

	if ( ! file_exists( $path ) ) {
		update_user_meta( $user_id, '_lp_profile_picture', '/' . $new_avatar );