Warning, there is a vulnerability in Elementor Pro version 3.11.6 or older, which could result in your WordPress site getting hacked. If you use Elementor pro, update now, making sure you have at least version 3.11.7. If you use Elementor free, your website is safe.
Table of Contents
Earlier today, an attack took place that exploited this flaw. Our friends at wp-ok notified us right away, and we are letting our customers know accordingly.
TLDR
On March 12, a vulnerability in Elementor Pro was discovered and reported.
The new version 3.11.7 was released by Elementor Ltd on March 22nd, closing this security hole.
Today, a large attack was noticed using this flaw, so it’s time to run for cover.
Solution
The solution to prevent your site from being hacked is updating Elementor Pro as soon as possible.
Since the security hole was fixed with the 3.11.7 version of the plugin, all you have to do is update the plugin to the latest version to sleep peacefully.
Cause of the problem
This vulnerability only affects the Pro version of Elementor. If you use Elementor free, your site is safe and you have nothing to worry about.
When WooCommerce is activated, Elementor loads the file “Elementor-pro/modules/woocommerce/module.php” which adds 2 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' ] ); }
The action pro_woocommerce_update_page_option is used by the Elementor editor.
This action calls the function update_option which is used to change the WordPress options in the database:
/** * 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'] ); }
As you see, this function modifies the option on the database using two values sent by the user.
This feature is for the admin or shop manager to change some WooCommerce settings in the database.
The problem is that there is no check on the data entered by the user, nor is there a check on the role that the user must have to use this function.
Elementor uses its own ajax hadler for most of its ajax calls, so even for pro_woocommerce_update_page_option
.
This handler can be found in the file “ Elementor/core/common/modules/ajax/module.php”:
/** * 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 ); } [...]
This function checks the nonce to prevent attackers from exploiting a vulnerability.
Elementor free loads all javascript scripts using the hook admin_enqueue_scripts
with the file “elementor/core/common/app.php”:
add_action( 'admin_enqueue_scripts', [ $this, 'register_scripts' ] );
So if you use Elementor free, from the browser js console, you can use:
alert( elementorCommon.ajax.getSettings("nonce") );
To see the nonce like this:
![Elementor Pro Vulnerability Exposes Nonce](https://supporthost.com/wp-content/uploads/2023/04/elementor-pro-vulnerability-exposes-nonce.png)
I checked and this is fixed in the latest Elementor version.
An attacker can in this way retrieve the nonce, and make an ajax call, all from the browser console and modify the WordPress settings.
At this point it is only up to the attacker’s creativity to decide what to do.
For example, you can change the setting users_can_register
to enable new registrations and set the new role as administrator using the setting admin_email
to then register via the WordPress form and be an administrator.
Or you could change the setting siteurl
to redirect all traffic to another site.
Alternative solution
As I explained above, the solution is to update Elementor Pro as soon as possible.
In some cases it is possible that the user using WooCommerce and Elementor Pro is unable to update Elementor Pro.
I don’t want in any way to favor those who use Elementor Pro without a license and therefore cannot update directly from the WordPress admin area.
This solution is meant to be a patch for those users who use old versions due to incompatibility between add-ons or other plugins. I do not recommend it, and I would suggest solving the issue as soon as possible in order to keep everything updated correctly.
You can change the Elementor Pro file “Elementor-pro/modules/woocommerce/module.php”.
Look for this part:
/** * 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'] ); }
And replace with:
/** * 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 ) { $is_admin = current_user_can( 'manage_options' ); $is_shop_manager = current_user_can( 'manage_woocommerce' ); $is_allowed = $is_admin || $is_shop_manager; if ( ! $is_allowed ) { return new \WP_Error( 401 ); } $allowed_options = [ 'woocommerce_checkout_page_id', 'woocommerce_cart_page_id', 'woocommerce_myaccount_page_id', 'elementor_woocommerce_purchase_summary_page_id', ]; $option_name = $data['option_name']; $post_id = absint( $data['editor_post_id'] ); if ( ! in_array( $option_name, $allowed_options, true ) ) { return new \WP_Error( 400 ); } update_option( $option_name, $post_id ); }
It basically does the same thing, but it checks that the user has permissions to modify the options, and allows only some options to be modified.
A note on editing plugins
This is a bad practice, and I will be the first to advise against modifying themes and plugins.
While with themes we have the possibility to create a child theme, this is not possible with plugins.
In this specific case I’m talking about the impossibility of updating Elementor Pro, whatever the reason. In addition, I am applying the same code from version 3.11.7 of Elementor Pro that fixes this vulnerability.
Basically we are modifying the code of a plugin that we cannot update. And if we update it, the new version has the same change, so in fact the problem is minimised.
Non-resolving solutions
There are some solutions on the net, which I do not consider decisive. Let’s see what they are and why they don’t work.
Block the IP range
It was recommended to block the IP range 193.169.194.**
This is a patch that made sense until it was figured out what was going on since the attackers were using that IP range.
Obviously, it doesn’t take that long to change IPs, so it is a measure that protects, but as you can see, it protects within certain limits.
Set the changelog to 404
It was recommended to set the Elementor pro changelog to 404, you can do it with a simple .htaccess directive
RewriteRule /wp-content/plugins/elementor-pro/changelog.txt - [L,R=404]
This is also a solution, although not definitive.
The attack is done in an automated way, so if the bot sees a 404 for this file (which it presumably uses to see what version of Elementor you’re running) it might move on to the next site.
But nothing prevents the bot from checking the resources of the page, just look for something like this:
https://domain.com/wp-content/plugins/elementor-pro/assets/js/nav-menu.bb5cce0a50480cdf695d.bundle.min.js
To understand that the site is using Elementor Pro and try the attack anyway.
Signs of a site under attack
So far, these are the symptoms that have been noticed on attacked sites, if you see any of these things, it is possible that your site has been attacked:
- The site redirects to another site. It might not be visible because of the cache, so add a random parameter to the url, like domain.it?exsrcdtfvbgy=rdcftvbgy
- In WordPress settings you see that anyone can register and the default role is not subscriber:
![Elementor Pro Vulnerability Check WordPress Settings](https://supporthost.com/wp-content/uploads/2023/04/elementor-pro-vulnerability-check-wordpress-settings.png)
- You’ll see users you don’t recognize. They are most likely administrators.
Example of an attack: server logs
Below is what we found in the server logs after an attempted attack:
/var/log/apache2/domlogs/hidden/hidden.net.hidden.it:193.169.194.63 - - [30/Mar/2023:14:35:05 +0200] "GET / HTTP/1.1" 301 707 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:35:05 +0200] "GET / HTTP/2" 200 51892 "http://hidden.net" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:35:06 +0200] "POST //wp-admin/admin-post.php HTTP/2" 200 20 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:35:07 +0200] "GET //wp-content/plugins/elementor-pro/changelog.txt HTTP/2" 200 25778 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:35:08 +0200] "GET / HTTP/2" 200 51892 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:35:09 +0200] "GET //wp-login.php?action=register HTTP/2" 302 20 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:35:10 +0200] "GET /wp-login.php?registration=disabled HTTP/2" 200 2839 "https://hidden.net//wp-login.php?action=register" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:35:11 +0200] "GET //my-account HTTP/2" 301 20 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:35:11 +0200] "GET /my-account/ HTTP/2" 200 31322 "https://hidden.net//my-account" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:35:12 +0200] "GET //my-account HTTP/2" 301 20 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:35:13 +0200] "GET /my-account/ HTTP/2" 200 31322 "https://hidden.net//my-account" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:35:14 +0200] "GET /my-account/ HTTP/2" 200 31322 "https://hidden.net/my-account/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:35:15 +0200] "POST / HTTP/2" 200 52314 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:35:17 +0200] "POST / HTTP/2" 200 52297 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36" /var/log/apache2/domlogs/hidden/hidden.net.hidden.it-ssl_log:193.169.194.63 - - [30/Mar/2023:14:37:07 +0200] "POST / HTTP/2" 200 51845 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36"
How to check the log
cPanel offers a tool: Raw access log.
![Elementor Pro Vulnerability Raw Access Log](https://supporthost.com/wp-content/uploads/2023/04/elementor-pro-vulnerability-raw-access-log.png)
From here you see a list of sites on your account, just click on the name of the log to download, you will probably need to download the version that indicates SSL in brackets:
![Elementor Pro Vulnerability Download Raw Access Logs](https://supporthost.com/wp-content/uploads/2023/04/elementor-pro-vulnerability-download-raw-access-logs-1024x250.png)
Unzip the file, open it with a text editor and look for the string:
/wp-content/plugins/elementor-pro/changelog.txt
Or IPs starting with:
193.169.194.
Note that the IP address may change, if you notice different IPs being used, please let us know so we can update this article.
Conclusion
Your site is only at risk if you use WooCommerce and Elementor Pro version 3.11.6 or older.
You can fix the vulnerability issue by updating Elementor Pro.
If you can’t update Elementor Pro for whatever reason, you can check the workaround and edit some Elementor Pro files.
Was this article helpful? Do you have any doubts, questions or suggestions for improving this article and preventing other sites from being hacked? Leave a comment and let me know!
Leave a Reply