漏洞概述 该漏洞涉及WordPress插件“Ultimate Member”中的密码重置功能。攻击者可以通过构造特定的请求,绕过密码重置流程中的验证机制,从而重置任意用户的密码。 影响范围 受影响插件:Ultimate Member 受影响版本:具体版本未明确,但根据文件路径推测为5.5.1版本 影响用户:所有使用该插件的WordPress网站用户 修复方案 1. 更新插件:建议用户立即更新Ultimate Member插件到最新版本,以获取最新的安全补丁。 2. 代码审查:对插件代码进行安全审查,确保密码重置流程中的验证机制正确无误。 3. 加强验证:在密码重置流程中增加额外的验证步骤,如验证码、二次确认等,以防止未经授权的密码重置。 POC代码 ```php is_ajax_form = true; $this->form_session_var = 'FormsessionVars[UM_PASS_RESET]'; $this->type_form_tag = 'um_on_phone_enable'; $this->type_email_tag = 'um_on_email_small'; $this->phone_form_id = '1'; $this->field_key = 'UM_PASS_RESET'; $this->form_name = 'Ultimate Member Password Reset Form'; $this->form_enabled = get_option( 'um_on_phone_enable' ) ? true : false; $this->phone_key = $this->phone_key ? $this->phone_key : 'mobile_number'; $this->form_documents = VMObjects::UM_PASS_RESET_FORM_LINK; $this->generate_otp_action = 'um_user_send_otp'; $this->validate_otp_action = 'um_verify_otp_action'; $this->button_text = get_option( 'um_on_phone_button_text' ); $this->button_text = $this->button_text ? $this->button_text : ''; parent::__construct(); } / Function checks if form has been enabled by the admin and initializes all the class variables. This function also defines all the hooks to hook into to make OTP verification possible. / public function handle_form() { $this->is_only_phone_reset = get_option( 'um_on_only_phone_reset' ); if ( $this->is_only_phone_reset ) { add_action( 'wp_ajax_nopriv_' . $this->generate_otp_action, array( $this, 'send_otp_request' ) ); add_action( 'wp_ajax_' . $this->generate_otp_action, array( $this, 'send_otp_request' ) ); add_action( 'wp_ajax_nopriv_' . $this->validate_otp_action, array( $this, 'verify_otp_request' ) ); add_action( 'wp_ajax_' . $this->validate_otp_action, array( $this, 'verify_otp_request' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'miniorange_register_um_script' ) ); add_action( 'um_reset_password_errors_hook', array( $this, 'um_reset_password_errors_hook' ), 39 ); add_action( 'um_reset_password_process_hook', array( $this, 'um_reset_password_process_hook' ), 1 ); } } / Send an OTP to the user's phone or email. @throws \Exception if exception occurs. / public function send_otp_request() { // Security: Use WordPress nonce action 'form_nonce' and key 'security' instead of variables. if ( ! check_ajax_referer( 'form_nonce', 'security', false ) ) { wp_send_json(); } $this->create_json(); $this->constants = ERROR_JSON_TYPE; $this->initialize_transaction( $this->form_session_var ); $data = VMUtils::sanitize_array( $_POST ); $username = VMUtils::sanitize_check( 'username', $data ); $user = $this->get_user_by( 'username', $username ); SessionUtils::add_user_in_session( $this->form_session_var, $username ); if ( ! $user ) { if ( $this->is_only_phone_reset ) { wp_send_json(); } $this->create_json(); $this->constants = ERROR_JSON_TYPE; $this->messages = show_message( VMMessages::USERNAME_NOT_EXIST ); $this->constants = ERROR_JSON_TYPE; return; } else { SessionUtils::add_user_in_session( $this->form_session_var, $username ); $phone = get_user_meta( $user->ID, $this->phone_key, true ); $this->start_otp_transaction( $user->user_email, $phone, $data ); } } / The function is called to start the OTP Transaction based on the OTP Type set by the admin in the settings. @param string $email The email associated with the user. @param string $phone_number The phone number associated with the user. @param array $data The data submitted during ajax call. / private function start_otp_transaction( $email, $phone_number, $data ) { $username = ( isset( $data['username'] ) ) ? sanitize_text_field( $data['username'] ) : ''; if ( isset( $this->type_form_tag ) && $this->type_form_tag === '1' ) { wp_send_json(); } $this->create_json(); $this->messages = show_message( VMMessages::PHONE_NOT_FOUND ); $this->constants = ERROR_JSON_TYPE; return; } if ( $this->send_challenge( $data['username'], $email, $phone_number, VerificationType::PHONE ) ) { $this->send_challenge( $data['username'], $email, $phone_number, VerificationType::EMAIL ); } } / This function registers the js file for enabling OTP Verification for Ultimate Member using AJAX calls. / public function miniorange_register_um_script() { wp_register_script( 'umotp', MINY_URL . '/includes/js/umpasswordreset.js', array( 'jquery' ), MINY_VERSION, true ); wp_localize_script( 'umotp', 'umotp', array( 'ajaxurl' => wp_ajax_url(), 'nonce' => wp_create_nonce( 'umotp' ), 'actionname' => $this->generate_otp_action, 'action' => array( 'send' => $this->generate_otp_action ), 'validate' => $this->validate_otp_action, 'fieldKey' => $this->field_key, 'resetlabel' => $this->messages, 'phone' => $this->is_only_phone_reset, 'reset' => RESET_LABEL_OP, 'phone' => $this->is_only_phone_reset, 'msg' => __( 'Enter Your Phone Number.', 'miniorange-otp-verification' ) . ' ' . __( 'Enter Your Email, Username or Phone Number.', 'miniorange-otp-verification' ), ) ); wp_enqueue_script( 'umotp' ); } / Checks if the verification has sta