19 Jun 2019

If Facebook’s Handling of the Security of Their WordPress Plugins Is Any Indication, They Don’t Seem Too Concerned About Security

On Monday we discussed that two of Facebook’s plugins for WordPress contained vulnerabilities due to basic security failures (and mentioned in passing that another is also insecure due to the same type of issue). There attempts to resolve the vulnerabilities continued to show a lack of concern and or understanding of security, at least when it comes to WordPress plugins. It also makes you wonder what the people running the WordPress Plugin Directory are up to since they know these plugins were vulnerable and didn’t make sure they were properly fixed.

Missing Capabilities Check

With the less popular of the vulnerable plugins, Messenger Customer Chat, which has 20,000+ installs according to WordPress, we wrote this about the issue:

That is missing both a capabilities check to limit what type of users can access it and a nonce check to prevent cross-site request forgery (CSRF).

The code related to that looked like this in the version before a partial fix on Monday:

57
58
59
60
61
function fbmcc_update_options() {
  update_option( 'fbmcc_enabled', "1" );
  update_option( 'fbmcc_generatedCode', sanitize_textarea_field( $_POST['fbmcc_generatedCode'] ) );
  wp_die();
}

In the new version that was changed to add the nonce check:

58
59
60
61
62
63
function fbmcc_update_options() {
  check_ajax_referer( 'update_fmcc_code' );
  update_option( 'fbmcc_enabled', "1" );
  update_option( 'fbmcc_generatedCode', sanitize_textarea_field( $_POST['fbmcc_generatedCode'] ) );
  wp_die();
}

The capabilities check was not added, which is a common problem.

What was added would normally be enough to protect against exploitation, which is more than can be said about the other plugin.

An Example and a Referer Check is Not a Nonce

With the more popular of the vulnerable plugins, Facebook for WooCommerce, which has 200,000+ installs according to WordPress, we wrote this about the issue:

When we started looking over the plugin’s settings page what we noticed was that a nonce was not being sent with an AJAX requests from that, which is needed to prevent cross-site request forgery (CSRF). Looking at the underlying code we found that. at least, many of the AJAX accessible functions lack that. One such example is the function ajax_update_fb_option().

A key word there is an example and yet Facebook only attempted to fix the example we provided.

How they fixed it is odd.

The code related to that looked like this in the version before a change made on Tuesday:

2531
2532
2533
2534
2535
2536
2537
  function ajax_update_fb_option() {
    WC_Facebookcommerce_Utils::check_woo_ajax_permissions('update fb options', true);
    if (isset($_POST) && stripos($_POST['option'], 'fb_') === 0) {
      update_option(sanitize_text_field($_POST['option']), sanitize_text_field($_POST['option_value']));
    }
    wp_die();
  }

Here is it after:

2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
  function ajax_update_fb_option() {
    WC_Facebookcommerce_Utils::check_ajax_referer();
    WC_Facebookcommerce_Utils::check_woo_ajax_permissions('update fb options', true);
    $wpnonce = $_POST['_wpnonce'];
    if (isset($_POST) &&
        stripos($_POST['option'], 'fb_') === 0 &&
        wp_verify_nonce($wpnonce, 'wp_ajax_ajax_update_fb_option')) {
      update_option(sanitize_text_field($_POST['option']), sanitize_text_field($_POST['option_value']));
    }
    wp_die();
  }

There is nonce check added there like in the other plugin, but there is also this check:

2533
WC_Facebookcommerce_Utils::check_ajax_referer();

The function that calls is new and is this:

484
485
486
487
488
489
490
491
492
493
494
495
496
public static function check_ajax_referer() {
  $http_referer = null;
  if (!empty($_SERVER['HTTP_REFERER'])) {
	$http_referer = wp_unslash($_SERVER['HTTP_REFERER']);
  }
  $wp_site = get_site_url();
  $http_referer_url = parse_url($http_referer);
  $wp_site_url = parse_url($wp_site_url);
  if (!empty($http_referer_url) && !empty($wp_site_url) && $http_referer_url['host'] === $wp_site_url['host']) {
	return;
  }
  wp_die('FBE: bad requests!');
}

It isn’t clear what the purpose of that is supposed to be, at first we thought it was a misguided attempt to prevent cross-site request forgery (CSRF), but then we noticed the nonce check added later to the code. We can’t figure out what it supposed to accomplish since it looks like the referer being checked can be faked in a CSRF exploit attempt. Ultimately it looks it might in fact be attempted protection against CSRF that was added as well as the nonce check, which is a red flag to us.

Cross-Site Request Forgery (CSRF)/Settings Change

Let’s now provide another example of the issue and again this is just an example, there is more similarly vulnerable code.

The plugin handles changing its settings through the function wp_ajax_ajax_save_fb_settings(), which accessible to anyone logged in to WordPress through its AJAX functionality:

191
192
  add_action('wp_ajax_ajax_save_fb_settings',
	array($this, 'ajax_save_fb_settings'), self::FB_PRIORITY_MID);

That function first restricts access to the rest of the code to those with the “manage_woocommerce” capability using the function check_woo_ajax_permissions() and then start the process of handling the updating of settings:

1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
  function ajax_save_fb_settings() {
    WC_Facebookcommerce_Utils::check_woo_ajax_permissions('save settings', true);
 
    if (isset($_REQUEST)) {
      if (!isset($_REQUEST['facebook_for_woocommerce'])) {
        // This is not a request from our plugin,
        // some other handler or plugin probably
        // wants to handle it and wp_die() after.
        return;
      }
 
      if (isset($_REQUEST['api_key']) && ctype_alnum($_REQUEST['api_key'])) {
        $this->settings['fb_api_key'] = $_REQUEST['api_key'];
      }

What is noticeable absent though is a check for a valid nonce to prevent cross-site request forgery (CSRF), so it would be possible for an attacker to cause someone logged with the “manage_woocommerce” capability to make changes to the settings without intending it.

Proof of Concept

The following proof of concept will set the fb_api_key to “1234”, when logged in as a WordPress user with the “manage_woocommerce” capability. That can be confirmed by looking at the woocommerce_facebookcommerce_settings  entry in WordPress’  _options table.

Make sure to replace “[path to WordPress]” with the location of WordPress.

http://[path to WordPress]/wp-admin/admin-ajax.php?action=ajax_save_fb_settings&facebook_for_woocommerce=test&api_key=1234

Automattic and WooThemes Get Credit or Blame

The other change made in the new version of Facebook for WooCommerce to “Add more contributors to the plugin”, so now Automattic and WooThemes are attached with an insecure plugin, which seems reasonable since what brought us across Facebook’s plugins is that Automattic through the WooCommerce is installing by default this insecure plugin.


Concerned About The Security of the Plugins You Use?

When you are a paying customer of our service, you can suggest/vote for the WordPress plugins you use to receive a security review from us. You can start using the service for free when you sign up now. We also offer security reviews of WordPress plugins as a separate service.

One thought on “If Facebook’s Handling of the Security of Their WordPress Plugins Is Any Indication, They Don’t Seem Too Concerned About Security

  1. I don’t see how that WC_Facebookcommerce_Utils::check_ajax_referer method could possibly work at all. It uses a variable $wp_site_url which isn’t defined anywhere – presumably they meant to use $wp_site?

Leave a Reply to Ed Cancel reply

Your email address will not be published.