Authenticated Privilege Escalation Vulnerability in Essential Addons for Elementor

Published 15 September 2023
Updated 2 October 2023
Rafie Muhammad
Security Researcher at Patchstack
Table of Contents

This blog post is about the vulnerability in Essential Addons for Elementor. If you're an Essential Addons for Elementor user, please update the plugin to at least version 5.8.9.

Patchstack Developer and Business users are protected from the vulnerability. You can also 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 Essential Addons for the Elementor plugin

The plugin Essential Addons for Elementor (versions <= 5.8.8, free version), which has over 1 million active installations, is known as the most popular Elementor addons plugins in WordPress.

Vulnerability in Essential Addons for Elementor

This WordPress plugin enhances the Elementor page building experience with 90+ creative elements and extensions. This plugin adds powers to our page builder using the easy-to-use elements that were designed to make our next WordPress page and posts design easier and prettier than ever before.

The security vulnerability in Essential Addons for Elementor

This plugin suffers from an authenticated privilege escalation vulnerability. It allows a user with a minimum of a Contributor role to escalate their privilege to that of any role on the WordPress site including the administrator role.

It is possible to create a custom registration form using the widget provided by Essential addons for Elementor plugin. This vulnerability occurs because a user with the Contributor role is able to set up an arbitrary role for the user registration form. The described vulnerability was fixed in version 5.8.9 and assigned CVE-2023-41955.

The initial step of discovering this vulnerability came from observing the register_user function that handles the custom user registration widget form process:

public function register_user() {
    $ajax = wp_doing_ajax();

---------------------- CUTTED HERE ----------------------

    // validate & sanitize the request data
    if ( empty( $_POST['eael-register-nonce'] ) ) {
        if ( $ajax ) {
            wp_send_json_error( __( 'Insecure form submitted without security token', 'essential-addons-for-elementor-lite' ) );
        }

        if (isset($_SERVER['HTTP_REFERER'])) {
            wp_safe_redirect($_SERVER['HTTP_REFERER']);
            exit();
        }
    }

    // handle registration...
    $user_data = [
        'user_login' => $username,
        'user_pass'  => $password,
        'user_email' => $email,
    ];

    if ( ! empty( $_POST['first_name'] ) ) {
        $user_data['first_name'] = self::$email_options['firstname'] = sanitize_text_field( $_POST['first_name'] );
    }
    if ( ! empty( $_POST['last_name'] ) ) {
        $user_data['last_name'] = self::$email_options['lastname'] = sanitize_text_field( $_POST['last_name'] );
    }
    if ( ! empty( $_POST['website'] ) ) {
        $user_data['user_url'] = self::$email_options['website'] = esc_url_raw( $_POST['website'] );
    }

    if ( ! empty( $_POST['eael_phone_number'] ) ) {
        $user_data['eael_phone_number'] = self::$email_options['eael_phone_number'] = sanitize_text_field( $_POST['eael_phone_number'] );
    }

    if( count( $eael_custom_profile_fields_text ) ){
        foreach( $eael_custom_profile_fields_text as $eael_custom_profile_field_text_key => $eael_custom_profile_field_text_value ){
            self::$email_options[$eael_custom_profile_field_text_key] = '';

            if ( ! empty( $_POST[ $eael_custom_profile_field_text_key ] ) ) {
                $user_data[$eael_custom_profile_field_text_key] = self::$email_options[$eael_custom_profile_field_text_key] = sanitize_text_field( $_POST[ $eael_custom_profile_field_text_key ] );
            }
        }
    }

    $register_actions    = [];
    $custom_redirect_url = '';
    if ( !empty( $settings) ) {
        $register_actions    = ! empty( $settings['register_action'] ) ? (array) $settings['register_action'] : [];
        $custom_redirect_url = ! empty( $settings['register_redirect_url']['url'] ) ? esc_url_raw( $settings['register_redirect_url']['url'] ) : '/';
        if ( ! empty( $settings['register_user_role'] ) ) {
            $user_data['role'] = sanitize_text_field( $settings['register_user_role'] );
        }
---------------------- CUTTED HERE ----------------------
    }

    $custom_redirect_url = apply_filters( 'eael/login-register/register-redirect-url', $custom_redirect_url, $this );

    $user_data = apply_filters( 'eael/login-register/new-user-data', $user_data );

    do_action( 'eael/login-register/before-insert-user', $user_data );
    $user_default_role = get_option( 'default_role' );

    if(!empty($user_default_role) && empty($user_data['role'])){
        $user_data['role'] = $user_default_role;
    }

    if ('administrator' == strtolower($user_data['role'])) {
        $user_data['role'] = !empty($settings['register_user_role']) ? wp_strip_all_tags( $settings['register_user_role'] ) : get_option('default_role');
    }

    $user_id = wp_insert_user( $user_data );
---------------------- CUTTED HERE ----------------------

Notice that we can set the $user_data['role'] to the value of $settings['register_user_role']. The $user_data itself comes from the user input but we cannot control the role parameter, while the $settings comes from the user registration form widget setting.

There is also no protection on the register_user_role setting configuration. The code only displays the option of the site's default role if the user who created the user registration form doesn't have permission to create users. This can be seen from init_content_register_options_controls function:

protected function init_content_register_options_controls() {
---------------------- CUTTED HERE ----------------------
    if(current_user_can('create_users')){
        $user_role = $this->get_user_roles();
    }else{
        $user_role = [
            get_option( 'default_role' ) =>  ucfirst(get_option( 'default_role' ))
        ];
    }

    $this->add_control( 'register_user_role', [
        'label'     => __( 'New User Role', 'essential-addons-for-elementor-lite' ),
        'type'      => Controls_Manager::SELECT,
        'default'   => '',
        'options'   => $user_role,
        'separator' => 'before',
    ] );

    $this->end_controls_section();
}

As we can see on the UI, a user with a Contributor role only provided the role option of the site's default role. But the restriction only exists on the front-end and the user can still supply any role to the field setting.

Vulnerability in Essential Addons for Elementor

The patch for vulnerability in Essential Addons for Elementor

The vulnerability in Essential Addons for Elementor can be patched by simply restricting the role configuration of the user registration form process only to an administrator user. The vendor decided to implement a couple of checks on the registration role field. The patch can be seen here:

Vulnerability in Essential Addons for Elementor
Vulnerability in Essential Addons for Elementor

Conclusion

For custom user registration processes that allow a user other than an administrator to assign a custom registration role, make sure to secure the registration role configuration on the form.

We recommend directly assigning the site's default registration role and only allowing a user with the administrator role to adjust this value or create a new user capability to assign a custom role on the user registration form.

Disclosure Note

Note that this vulnerability could be easily exploited by the Contributor role user without any additional requirement such as publishing capabilities since the form could be submitted in a drafted state.

The only configuration needed is the user registration feature in the WordPress site and the user registration form widget from Essential Addons for Elementor needs to be enabled. We also decided to publish this article early since we detected that a third party already releasing information regarding this vulnerability.

Timeline

05 September, 2023We found the vulnerability and reached out to the plugin vendor.
14 September, 2023Essential Addons for Elementor plugin version 5.8.9 released to patch the reported issue.
15 September, 2023Added the vulnerabilities to the Patchstack vulnerability database. Security advisory article publicly released.

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.

The latest in Security Advisories

Looks like your browser is blocking our support chat widget. Turn off adblockers and reload the page.
crossmenu