On July 23, 2020, Wordfence Threat Intelligence team discovered a vulnerability present in two themes by Elegant Themes, Divi and Extra, as well as Divi Builder, a WordPress plugin. Combined, these products are installed on an estimated 700,000 sites. This flaw gave authenticated attackers, with contributor-level or above capabilities, the ability to upload arbitrary files, including PHP files, and achieve remote code execution on a vulnerable site’s server.
We initially reached out to Elegant Themes on July 23, 2020 and, after establishing an appropriate communication channel, we provided the full disclosure details on July 28, 2020. The developers responded on June 29, 2020 to let us know a patch would be coming in the next version. Patches were released yesterday, on August 3, 2020, in version 4.5.3 for all products.
This is considered a critical security issue that could lead to remote code execution on a vulnerable site’s server. If you haven’t already updated, and you are running Divi versions 3.0 and above, Extra versions 2.0 and above, or Divi Builder versions 2.0 and above, we highly recommend updating to the patched version, 4.5.3 , immediately. Alternatively, you can use their Security Patcher Plugin until you can update safely.
Both Wordfence Premium and free users are protected against any attacks attempting to exploit this vulnerability due to the Wordfence firewall’s built-in malicious file upload protection.
Affected Products: Divi Theme, Extra Theme, and Divi Builder plugin
Theme Slugs: divi, extra
Plugin slug: divi-builder
Affected Versions: (Divi): 3.0 – 4.5.2
Affected Versions: (Extra): 2.0 – 4.5.2
Affected Versions: (Divi Builder): 2.0 – 4.5.2
CVE ID: Pending.
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
Fully Patched Version (same for all products): 4.5.3
Elegant Themes is the creator behind one of the most popular premium themes, Divi. One of the features of the Divi theme is that it comes with the Divi Page Builder that makes the site design and editing process easy and customizable. In addition to the Divi theme, Elegant Themes offers an alternative theme, Extra, that includes the Divi Builder. The standalone Divi Builder plugin is also available and can be used with any theme.
As part of the Divi Builder functionality, users that have the ability to create posts can import and export Divi page templates using the portability feature.
Unfortunately, we discovered that although this feature used a client-side file type verification check, it was missing a server-side verification check. This flaw made it possible for authenticated attackers to easily bypass the JavaScript client-side check and upload malicious PHP files to a targeted website. An attacker could easily use a malicious file uploaded via this method to completely take over a site.
What went wrong?
Taking a closer look at the code, we can see that the portability import
function was triggered with the use of the et_core_portability_import
AJAX action and corresponding et_core_portability_ajax_import
function, which does have nonce and capability check.
add_action( 'wp_ajax_et_core_portability_import', 'et_core_portability_ajax_import' );
The core of the problematic code could be found within the import
function of the builder’s portability.php
file. Since the plugin had a client-side JavaScript-based file extension check for .json files, the developers might have missed adding a server-side file-type check here prior to using the file’s contents during the import, or assumed the client-side check would be sufficient protection.
public function import( $file_context = 'upload' ) { global $shortname;</pre> <code></code> $this->prevent_failure(); self::$_doing_import = true; $timestamp = $this->get_timestamp(); $filesystem = $this->set_filesystem(); $temp_file_id = sanitize_file_name( $timestamp ); $temp_file = $this->has_temp_file( $temp_file_id, 'et_core_import' ); $include_global_presets = isset( $_POST['include_global_presets'] ) ? wp_validate_boolean( $_POST['include_global_presets'] ) : false; $global_presets = ''; if ( $temp_file ) { $import = json_decode( $filesystem->get_contents( $temp_file ), true ); } else { if ( ! isset( $_FILES['file'] ) ) { return false; } if ( ! in_array( $file_context, array( 'upload', 'sideload' ) ) ) { $file_context = 'upload'; }
Analyzing the code further, we see that the file is temporarily uploaded using wp_handle_upload
, with test_type
set to false, overriding the wp_check_filetype_and_ext
function that checks a file’s type and determines if it is a safe file to upload based on a list of allowed mime types.
This meant that the wp_handle_upload
function did not test the file type during the upload, essentially disabling the extensive file-type checking protection built-in to the function.
$handle_file = "wp_handle_{$file_context}"; $upload = $handle_file( $_FILES['file'], array( 'test_size' => false, 'test_type' => false, 'test_form' => false, ) );
do_action( 'et_core_portability_import_file', $upload['file'] );
From there, the file’s content was checked to see if it could be used for the import. If the file’s content did not appear to be usable JSON data for an import, then the process was killed and the message ‘importContextFail’
was returned.
$temp_file = $this->temp_file( $temp_file_id, 'et_core_import', $upload['file'] ); $import = json_decode( $filesystem->get_contents( $temp_file ), true ); $import = $this->validate( $import ); $import['data'] = $this->apply_query( $import['data'], 'set' );</pre> <code></code> if ( ! isset( $import['context'] ) || ( isset( $import['context'] ) && $import['context'] !== $this->instance->context ) ) { return array( 'message' => 'importContextFail' ); }
Toward the end of the function, there was a hook to the function ‘delete_temp_files’
that was intended to delete any JSON files used for the import once completed. However, since the import died for files without usable JSON content before getting to this function, the files remained in the uploads directory until a legitimate JSON file was imported.
$this->delete_temp_files( 'et_core_import' );
This flaw made it possible for authenticated users with the edit_posts
capability, like contributors, editors, and authors, to upload arbitrary files. An attacker could easily upload malicious PHP files and access them from the uploads directory. This could ultimately result in remote code execution and complete compromise of a vulnerable site’s hosting account.
The wp_ajax_et_theme_builder_api_import_theme_builder
AJAX action and corresponding function used to import a theme builder template was also susceptible to arbitrary file uploads due to the same issues, however, exploiting this would have required administrative privileges thus significantly reducing it’s severity.
Fortunately, Elegant Themes was very quick to respond and release a patch that not only prevented all files except .json files from being uploaded, but also ensured that files would be sufficiently deleted at any stage of the process once no longer used.
How to Update your Elegant Themes Product
As long as you have supplied your Elegant Themes Username and API key on your WordPress site, then you can take care of your updates directly in the updates area on your site. To do so, log into your site, and navigate to the “Updates” area. Select the Elegant Themes product you would like to update and just click “Update Plugin” or “Update Theme” depending on which product you are updating.
Also, please note that Elegant Themes has made this patch available to users, even if your account is expired.
If you are unable to update fully, you can install Elegant Themes Security Patcher Plugin that will temporarily patch the vulnerability until you are able to do a complete update.
Another way to stay protected
As mentioned, in our post last week, Wordfence has a feature to disable code execution in the uploads directory. Even if you’re not using one of Elegant Themes’ vulnerable products, we highly recommend enabling this setting as it will provide additional protection against vulnerabilities like this one that may erroneously allow PHP files to be uploaded into the uploads directory.
With this option enabled, attackers will not be able to execute PHP files uploaded into the uploads directory, providing an extra layer of security and assisting in thwarting attacks like this one. In the event that a zero-day vulnerability is discovered and actively exploited prior to the creation of a custom firewall rule, having this feature enabled can help keep your site protected.
Source Credit – Critical Vulnerability Exposes over 700,000 Sites Using Divi, Extra, and Divi Builder