The vulnerability in Elementor was originally reported by Hồng Quân (luk6785 at VNPT-VCI) to our alliance program. We are collaborating with the researcher to release the content of this security advisory article.
This blog post is about an Elementor plugin vulnerability. If you’re an Elementor user, please update the plugin to at least version 3.18.2.
You can sign up for the Patchstack Community plan to be notified about vulnerabilities as soon as they become disclosed.
For plugin developers, we have security audit services and Threat Intelligence Feed API for hosting companies.
About the Elementor Plugin
The plugin Elementor (free version), which has over 5 million active installations, is known as the most popular website builder plugin in WordPress.
Elementor is known to be the leading website-building platform for WordPress, enabling web creators to build professional, pixel-perfect websites with an intuitive visual builder. The plugin could quickly create amazing websites for clients or businesses with complete control over every piece, without writing a single line of code.
The security vulnerability in Elementor
This plugin suffers from an authenticated arbitrary file upload vulnerability. This vulnerability allows accounts with edit post permissions such as Contributor role, to upload arbitrary files, including php files, that could lead to remote code execution. The described vulnerability was introduced in version 3.3.0 and fixed in version 3.18.2 and assigned CVE-2023-48777.
Arbitrary File Upload
The underlying vulnerable code exists in the handle_elementor_upload
function :
public function handle_elementor_upload( array $file, $allowed_file_extensions = null ) {
// If $file['fileData'] is set, it signals that the passed file is a Base64 string that needs to be decoded and
// saved to a temporary file.
if ( isset( $file['fileData'] ) ) {
$file = $this->save_base64_to_tmp_file( $file );
}
$validation_result = $this->validate_file( $file, $allowed_file_extensions );
if ( is_wp_error( $validation_result ) ) {
return $validation_result;
}
return $file;
}
This function will call the save_base64_to_tmp_file
function to save the file :
private function save_base64_to_tmp_file( $file ) {
$file_content = base64_decode( $file['fileData'] ); // phpcs:ignore
// If the decode fails
if ( ! $file_content ) {
return new \WP_Error( 'file_error', self::INVALID_FILE_CONTENT );
}
$temp_filename = $this->create_temp_file( $file_content, $file['fileName'] );
if ( is_wp_error( $temp_filename ) ) {
return $temp_filename;
}
return [
// the original uploaded file name
'name' => $file['fileName'],
// The path to the temporary file
'tmp_name' => $temp_filename,
];
}
Notice that there is a call to save the $file_content
and $file['fileName']
using the create_temp_file
function :
public function create_temp_file( $file_content, $file_name ) {
$temp_filename = $this->create_unique_dir() . $file_name;
/**
* Temp File Path
*
* Allows modifying the full path of the temporary file.
*
* @since 3.7.0
*
* @param string full path to file
*/
$temp_filename = apply_filters( 'elementor/files/temp-file-path', $temp_filename );
file_put_contents( $temp_filename, $file_content ); // phpcs:ignore
return $temp_filename;
}
The file will be saved into the tmp directory under the wp-content/uploads/elementor/tmp
path. Note that there is no check on the uploaded $file_name
and the file is uploaded directly using the file_put_contents
function. With that case, the user could supply a path traversal payload with a .php filename so it will not be saved inside the tmp directory and instead will be saved to the main wp-content/uploads
directory for example.
The interesting part is the check on the allowed file extensions using the validate_file
function is only performed after the file has already been uploaded.
Note that this vulnerability can be reproduced with the Contributor role on a default installation of the Elementor plugin without any additional conditions or requirements.
The patch
The vulnerability was originally found in version 3.17.3. The Elementor team then decided to release a patch in version 3.18.1. Unfortunately, the patch was incomplete since the team only implemented sanitize_file_name
on the $file_name
variable.
This patch will prevent the user from saving the file outside the tmp directory, but users are still able to upload arbitrary files such as .php files to the tmp directory. The uploaded file will not be removed from the tmp directory. However, in this condition, the attacker still needs to guess or predict the tmp directory to access the uploaded .php files. The patch on version 3.18.1 can be seen below:
The Elementor then fixes the incomplete patch and releases the fully patched version 3.18.2. In this version, a proper file name and extension check using is_file_type_allowed
function is implemented inside the save_base64_to_tmp_file
function. The patch can be seen below :
Conclusion
Always check every process of file saving where the content and the file name are fully or partially controlled by the user. Implement both file type and file name checks on the saved file. For the file name check, we recommend using a whitelist check approach, rather than a blacklist approach.
Timeline
Help us make the Internet a safer place
Making the WordPress ecosystem more secure is a team effort, and we believe that plugin developers and security researchers should work together.
- If you’re a plugin developer, join our mVDP program that makes it easier to report, manage and address vulnerabilities in your software.
- If you’re a security researcher, join Patchstack Alliance to report vulnerabilities & earn rewards.