High severity vulnerability fixed in WordPress Elementor Pro plugin.

Elementor Pro, a popular page builder plugin for WordPress, fixed a broken access control vulnerability affecting versions <=3.11.6 that could allow full site takeover.

Authenticated Arbitrary WordPress Options Change

CVSS v3.1: 8.8 (High)

Note: The vulnerability affects the premium version of the plugin, not the free one available at wordpress.org.

When Elementor Pro is installed on a site that has WooCommerce activated, it loads its “elementor-pro/modules/woocommerce/module.php” component, which registers a couple of AJAX actions:

/**
 * Register Ajax Actions.
 *
 * Registers ajax action used by the Editor js.
 *
 * @since 3.5.0
 *
 * @param Ajax $ajax
 */
public function register_ajax_actions( Ajax $ajax ) {
   // `woocommerce_update_page_option` is called in the editor save-show-modal.js.
   $ajax->register_ajax_action( 'pro_woocommerce_update_page_option', [ $this, 'update_page_option' ] );
   $ajax->register_ajax_action( 'pro_woocommerce_mock_notices', [ $this, 'woocommerce_mock_notices' ] );
}

One of them is pro_woocommerce_update_page_option, which is used by Elementor’s built-in editor. It calls update_option, a function that can be used to modify WordPress options in the database, with two user-submitted input:

/**
 * Update Page Option.
 *
 * Ajax action can be used to update any WooCommerce option.
 *
 * @since 3.5.0
 *
 * @param array $data
 */
public function update_page_option( $data ) {
   update_option( $data['option_name'], $data['editor_post_id'] );
}

This function is supposed to allow the Administrator or the Shop Manager to update some specific WooCommercerce options, but user input aren’t validated and the function lacks a capability check to restrict its access to a high privileged user only.

Elementor uses its own AJAX handler to manage most of its AJAX actions, including pro_woocommerce_update_page_option, with the global elementor_ajax action. It is located in the “elementor/core/common/modules/ajax/module.php” script of the free version (which is required to run Elementor Pro) :

/**
 * Handle ajax request.
 *
 * Verify ajax nonce, and run all the registered actions for this request.
 *
 * Fired by `wp_ajax_elementor_ajax` action.
 *
 * @since 2.0.0
 * @access public
 */
public function handle_ajax_request() {
   if ( ! $this->verify_request_nonce() ) {
      $this->add_response_data( false, esc_html__( 'Token Expired.', 'elementor' ) )
         ->send_error( Exceptions::UNAUTHORIZED );
   }
   ...

We can see that it includes a nonce check that could potentially prevent bad actors from exploiting the vulnerability. But the nonce and all JS code related to it is loaded via the admin_enqueue_scripts hook in “elementor/core/common/app.php”:

add_action( 'admin_enqueue_scripts', [ $this, 'register_scripts' ] );

It therefore leaks in the source of the page to all logged in users:

An authenticated attacker can leverage the vulnerability to create an administrator account by enabling registration (users_can_register) and setting the default role (default_role) to “administrator”, change the administrator email address (admin_email) or, as shown below, redirect all traffic to an external malicious website by changing siteurl among many other possibilities:

MariaDB [example]> SELECT * FROM `wp_options` WHERE `option_name`='siteurl';
+-----------+-------------+------------------+----------+
| option_id | option_name | option_value     | autoload |
+-----------+-------------+------------------+----------+
|         1 | siteurl     | https://evil.com | yes      |
+-----------+-------------+------------------+----------+
1 row in set (0.001 sec)

Because the vulnerable component requires WooCommerce to be installed, an unauthenticated user can create a WooCommerce customer account, log in and exploit the vulnerability too (WooCommerce customers can access the back-end by adding wc-ajax=1 to the query, e.g., https://example.com/wp-admin/?wc-ajax=1).

Timeline

The vulnerability was discovered and reported to the authors on March 18, 2023, and a new version 3.11.7 was released on March 22, 2023.

Recommendations

Update immediately if you have version 3.11.6 or below installed. If you are using our web application firewall for WordPress, NinjaFirewall WP Edition (free) and NinjaFirewall WP+ Edition (premium), you are protected against this vulnerability.
Additionally, users who had the vulnerable plugin installed on their website have been notified in real time about the security update.

Stay informed about the latest vulnerabilities