Interesting Arbitrary File Upload Vulnerability Patched in User Registration WordPress Plugin

On June 19, 2023, the Wordfence Threat Intelligence team identified and began the responsible disclosure process for an Arbitrary File Upload vulnerability in WPEverest’s User Registration plugin, which is actively installed on more than 60,000 WordPress websites. This vulnerability makes it possible for an authenticated attacker with minimal permissions, such as a subscriber, to upload arbitrary files, including PHP files, and achieve remote code execution on a vulnerable site’s server.

Wordfence PremiumWordfence Care, and Wordfence Response users received a firewall rule to protect against any exploits targeting this vulnerability on June 20, 2023. Sites still using the free version of Wordfence will receive the same protection on July 20, 2023.

We contacted WPEverest on June 19, 2023, and received a response the same day. After we provided full disclosure details, the developer released the first patch, which did not fully address the vulnerability, in version 3.0.2 on June 29, 2023. A fully patched version, 3.0.2.1, was released on July 4, 2023. We would like to commend the WPEverest development team for their prompt response and timely patch.

We urge users to update their sites with the latest patched version of User Registration, which is version 3.0.2.1 at the time of this writing, as soon as possible.

Vulnerability Summary from Wordfence Intelligence

Description: User Registration <= 3.0.2 – Authenticated (Subscriber+) Arbitrary File Upload
Affected Plugin: User Registration – Custom Registration Form, Login Form And User Profile For WordPress
Plugin Slug: user-registration
Affected Versions: <= 3.0.2
CVE ID: CVE-2023-3342
CVSS Score: 9.9 (Critical)
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
Researcher/s: Lana Codes
Fully Patched Version: 3.0.2.1

The User Registration plugin for WordPress is vulnerable to arbitrary file uploads due to a hardcoded encryption key and missing file type validation on the ‘ur_upload_profile_pic’ function in versions up to, and including, 3.0.2. This makes it possible for authenticated attackers with subscriber-level capabilities or above to upload arbitrary files on the affected site’s server which may make remote code execution possible. This was partially patched in version 3.0.2 and fully patched in version 3.0.2.1.

Technical Analysis

The User Registration plugin provides a versatile drag and drop registration form builder, with custom fields and unlimited customization options. It also provides a login form. After logging in, it provides users with a profile that allows various types of customization, including uploading a profile picture.

Examining the code reveals that the plugin uses two separate functions to set the profile picture. The first AJAX function uploads the profile picture to a temp folder, and the second request moves the file and sets the profile picture to the user.

The profile_pic_upload() function uses a generic image upload solution, which checks the file extension and then uploads the image to the temp folder. The interesting part is that the data from the uploaded file is encrypted within the AJAX response:

if ( move_uploaded_file( $upload['tmp_name'], $file_path ) ) {
	$files = array(
		'file_name'      => $file_name,
		'file_path'      => $file_path,
		'file_extension' => $file_extension,
	);

	$attachment_id = wp_rand();

	ur_clean_tmp_files();
	$url = UR_UPLOAD_URL . 'temp-uploads/' . sanitize_file_name( $file_name );
	wp_send_json_success(
		array(
			'attachment_id' => $attachment_id,
			'upload_files'  => crypt_the_string( maybe_serialize( $files ), 'e' ),
			'url'           => $url,
		)
	);
}

JSON response after the file upload in the profile_pic_upload() function

The encrypted upload_files data in the response is something like this:

Encryption is used due to the way the plugin handles uploads because the encrypted data is decrypted and used to determine the filename and filepath where the file is saved for the user. However, for data to be encrypted and decrypted, an encryption key is required. As a general rule, encryption keys should be confidential and unique to each website.

/**
 * Encrypt/Decrypt the provided string.
 * Encrypt while setting token and updating to database, decrypt while comparing the stored token.
 *
 * @param  string $string String to encrypt/decrypt.
 * @param  string $action Encrypt/decrypt action. 'e' for encrypt and 'd' for decrypt.
 * @return string Encrypted/Decrypted string.
 */
function crypt_the_string( $string, $action = 'e' ) {
	$secret_key = 'ur_secret_key';
	$secret_iv  = 'ur_secret_iv';

	$output         = false;
	$encrypt_method = 'AES-256-CBC';
	$key            = hash( 'sha256', $secret_key );
	$iv             = substr( hash( 'sha256', $secret_iv ), 0, 16 );

	if ( 'e' == $action ) {
		if ( function_exists( 'openssl_encrypt' ) ) {
			$output = base64_encode( openssl_encrypt( $string, $encrypt_method, $key, 0, $iv ) );
		} else {
			$output = base64_encode( $string );
		}
	} elseif ( 'd' == $action ) {
		if ( function_exists( 'openssl_decrypt' ) ) {
			$output = openssl_decrypt( base64_decode( $string ), $encrypt_method, $key, 0, $iv );
		} else {
			$output = base64_decode( $string );
		}
	}

	return $output;
}

We unfortunately found that the encryption key is hardcoded in vulnerable versions of the plugin in the crypt_the_string() function, which means that threat actors also had access to the key which was not unique per WordPress installation. This makes it possible for attackers to craft an uploaded files data array payload that can be used to modify the filename, path, and extension when saving the profile picture.

The plugin calls the function ur_upload_profile_pic() when saving the profile, which contains the following code:

$upload = maybe_unserialize( crypt_the_string( $upload_file, 'd' ) );
if ( isset( $upload['file_name'] ) && isset( $upload['file_path'] ) && isset( $upload['file_extension'] ) ) {
	$upload_path = $upload_path . '/';
	$file_name   = wp_unique_filename( $upload_path, $upload['file_name'] );
	$file_path   = $upload_path . sanitize_file_name( $file_name );
	// Check the type of file. We'll use this as the 'post_mime_type'.
	$filetype = wp_check_filetype( basename( $file_name ), null );
	$moved    = rename( $upload['file_path'], $file_path );

	if ( $moved ) {
		$attachment_id = wp_insert_attachment(
			array(
				'guid'           => $file_path,
				'post_mime_type' => $filetype['type'],
				'post_title'     => preg_replace( '/\.[^.]+$/', '', sanitize_file_name( $file_name ) ),
				'post_content'   => '',
				'post_status'    => 'inherit',
			),
			$file_path
		);

		if ( ! is_wp_error( $attachment_id ) ) {
			include_once ABSPATH . 'wp-admin/includes/image.php';

			// Generate and save the attachment metas into the database.
			wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file_path ) );
		}
	}
}

Rename and move the file in ur_upload_profile_pic() function

The function decrypts the encrypted file data, which is specified in the save request. Based on this data, the file in the temp folder is moved and renamed using the rename() php function. Unfortunately, however, there is no file type check before it, which means that the image file can be renamed to a file with any type of extension, such as .php, .phtml, .html, and more.

Exploit Possibilities

Exploiting the vulnerability requires multiple complex steps, as two separate functions and requests must be used to upload and move the file:

  • Register a user
  • Log in as the user (since it is only possible to upload a profile picture for the user)
  • Upload the malicious exploit.png image file
  • Retrieve the encrypted file data from the response
  • Decrypt the file data
  • Modify the file extension to php in the file name
  • Encrypt the file data
  • Save the profile with the encrypted and modified file data

Since it is only possible to upload an image file during the upload process, because its extension is checked, the initial step involves uploading a file named, for example, exploit.png as a profile picture with the following request:

In this scenario, the attacker uploads an exploit.png file, which is actually a PHP script, but with a .png extension:

<?php
echo 'md5("exploit"): ' . md5( 'exploit' );

The response will be a json payload containing the encrypted result returned from the profile_pic_upload() function. The ‘upload_files’ parameter needs to be decrypted using the hardcoded key, which will return a serialized array, similar to this:

The attacker would then change the file name from exploit.png to exploit.php and re-encrypt the array. The newly encrypted result can then be used when saving the profile in the next request:

The exploit renames the image file to php and moves it to the profile pictures folder.

The complete exploit process looks like this:

Wordfence Firewall

The following graphic demonstrates the steps to exploitation an attacker might take and at which point the Wordfence firewall would block an attacker from successfully exploiting the vulnerability.

Disclosure Timeline

June 19, 2023 – Discovery of the Arbitrary File Upload vulnerability in User Registration.
June 19, 2023 – We initiate contact with the plugin vendor asking that they confirm the inbox for handling the discussion.
June 19, 2023 – The vendor confirms the inbox for handling the discussion.
June 19, 2023 – We send over the full disclosure details. The vendor acknowledges the report and begins working on a fix.
June 20, 2023Wordfence PremiumCare, and Response users receive a firewall rule to provide protection against any exploits that may target this vulnerability.
June 29, 2023 – A partial patch is released in version 3.0.2.
July 4, 2023 – A fully patched version of the plugin, 3.0.2.1, is released.
July 20, 2023 – Wordfence Free users receive the same protection.

Conclusion

In this blog post, we detailed an Arbitrary File Upload vulnerability within the User Registration plugin affecting versions 3.0.2 and earlier. This vulnerability allows authenticated threat actors with subscriber-level permissions or higher to upload arbitrary files, including PHP backdoors, and execute those files on the server. The vulnerability has been fully addressed in version 3.0.2.1 of the plugin.

We encourage WordPress users to verify that their sites are updated to the latest patched version of User Registration.

Wordfence PremiumWordfence Care, and Wordfence Response users received a firewall rule to protect against any exploits targeting this vulnerability on June 20, 2023. Sites still using the free version of Wordfence will receive the same protection on July 20, 2023.

If you know someone who uses this plugin on their site, we recommend sharing this advisory with them to ensure their site remains secure, as this vulnerability poses a significant risk.

For security researchers looking to disclose vulnerabilities responsibly and obtain a CVE ID, you can submit your findings to Wordfence Intelligence and potentially earn a spot on our leaderboard.

Did you enjoy this post? Share it!

Comments

3 Comments
  • Great work guys! I LOVE the visual representation of how the exploit works and how Wordfence prevents it.

  • Can I just disable my edit profile functionality as a workaround to prevent this exploit ?
    Is it still possible to exploit the vulnerability if edit profile functionality is disabled in the plugin?

    • Hi Jennifer,

      We are not aware of a way to exploit this vulnerability if edit profile functionality is disabled, but that doesn't mean there isn't one. The simplest solution is to upgrade the plugin to the latest patched version, which is 3.0.2.1 as of this writing. This will prevent the exploit more effectively than disabling functionality.