Vulnerability in Google WordPress Plugin Grants Attacker Search Console Access

On April 21st, our Threat Intelligence team discovered a vulnerability in Site Kit by Google, a WordPress plugin installed on over 300,000 sites. This flaw allows any authenticated user, regardless of capability, to become a Google Search Console owner for any site running the Site Kit by Google plugin.

We filed a security issue report with Google on April 21, 2020. A patch was released a few weeks later on May 7, 2020.

This is considered a critical security issue that could lead to attackers obtaining owner access to your site in Google Search Console. Owner access allows an attacker to modify sitemaps, remove pages from Google search engine result pages (SERPs), or to facilitate black hat SEO campaigns. We strongly recommend an immediate update to the latest version of this plugin. At the time of writing, that is version 1.8.0 of Site Kit by Google.

Wordfence Premium customers received a new firewall rule on April 21, 2020 to protect against exploits targeting this vulnerability. Free Wordfence users will receive this rule after thirty days, on May 21, 2020.

Description: Google Search Console Privilege Escalation
Affected Plugin: Site Kit by Google
Plugin Slug: google-site-kit
Affected Versions: <= 1.7.1
CVE ID: Will be updated once identifier is supplied.
CVSS Score: 9.1 (Critical)
CVSS Vector: CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:L
Fully Patched Version: 1.8.0

Site Kit by Google is a plugin used to obtain and display insights on a site’s visitors and search performance as well as advertising performance, page speed insights and other metrics from Google services in the WordPress dashboard. It does this by initially connecting to a Google Search Console account, later providing additional capabilities to connect to Analytics, AdSense, PageSpeed Insights, Optimize, and Tag Manager.

Site Kit by Google Dashboard.

In order to establish the first connection with Site Kit and Google Search Console, the plugin generates a proxySetupURL that is used to redirect a site’s administrator to Google OAuth and run the site owner verification process through a proxy.

proxySetupURL Disclosure

Due to the lack of capability checks on the admin_enqueue_scripts action, the proxySetupURL was displayed as part of the HTML source code of admin pages to any authenticated user accessing the /wp-admin dashboard.

More specifically, the admin_enqueue_scripts action triggers the enqueue_minimal_admin_script function which enqueues the googlesitekit-base assest and ultimately includes the ‘googlesitekit-base-data‘ that returns the inline base data. This includes the proxySetupURL.

	new Script_Data(
				'googlesitekit-base-data',
				array(
					'global'        => '_googlesitekitBaseData',
					'data_callback' => function () {
						return $this->get_inline_base_data();
					},
				)
			),

Here is a look at the inline_js_base_data function that retrieves the data to display in the WordPress dashboard’s source code as part of the the admin_enqueue_scripts action. This includes the proxySetupURL.

	/**
	 * Modifies the base data to pass to JS.
	 *
	 * @since 1.2.0
	 *
	 * @param array $data Inline JS data.
	 * @return array Filtered $data.
	 */
	private function inline_js_base_data( $data ) {
		$first_admin_id  = (int) $this->first_admin->get();
		$current_user_id = get_current_user_id();

		// If no first admin is stored yet and the current user is one, consider them the first.
		if ( ! $first_admin_id && current_user_can( Permissions::MANAGE_OPTIONS ) ) {
			$first_admin_id = $current_user_id;
		}
		$data['isFirstAdmin'] = ( $current_user_id === $first_admin_id );
		$data['splashURL']    = esc_url_raw( $this->context->admin_url( 'splash' ) );

		$auth_client = $this->get_oauth_client();
		if ( $auth_client->using_proxy() ) {
			$access_code                 = (string) $this->user_options->get( Clients\OAuth_Client::OPTION_PROXY_ACCESS_CODE );
			$data['proxySetupURL']       = esc_url_raw( $auth_client->get_proxy_setup_url( $access_code ) );
			$data['proxyPermissionsURL'] = esc_url_raw( $auth_client->get_proxy_permissions_url() );
		}

		return $data;
	}

A closer look at the data discoverable in the source code of the admin dashboard:

proxySetupURL found in the source code.

This was fixed in the latest version of the plugin by the addition of a capability check on the admin_enqueue_scripts action. This prohibits the inline_js_base_data from being included in administrative pages for users who do not have the appropriate privileges, such as subscribers. This prevents the proxySetupURL from being displayed to unauthorized users.

	add_action( 'admin_enqueue_scripts', $register_callback );
		add_action( 'wp_enqueue_scripts', $register_callback );

		add_action(
			'admin_enqueue_scripts',
			function() {
				if ( ! current_user_can( Permissions::AUTHENTICATE ) ) {
					return;
				}
				$this->enqueue_minimal_admin_script();
			}
		);

Unprotected Verification

In addition to displaying the proxySetupURL to any authenticated user, we discovered that the verification request used to verify a site’s ownership was a registered admin action that, again, did not have any capability checks. This allowed verification requests to come from any authenticated WordPress user, including those with minimal permissions.

The admin_action_googlesitekit_proxy_setup action was registered here.

	add_action(
			'admin_action_' . Google_Proxy::ACTION_SETUP,
			function () {
				$this->verify_proxy_setup_nonce();
			},
			-1
		);

		add_action(
			'admin_action_' . Google_Proxy::ACTION_SETUP,
			function () {
				$code      = $this->context->input()->filter( INPUT_GET, 'googlesitekit_code', FILTER_SANITIZE_STRING );
				$site_code = $this->context->input()->filter( INPUT_GET, 'googlesitekit_site_code', FILTER_SANITIZE_STRING );

				$this->handle_site_code( $code, $site_code );
				$this->redirect_to_proxy( $code );
			}
		);

This is the function that handles the verification request:

	/**
	 * Handles receiving a verification token for a user by the authentication proxy.
	 *
	 * @since 1.1.0
	 * @since 1.1.2 Runs on `admin_action_googlesitekit_proxy_setup` and no longer redirects directly.
	 */
	private function handle_verification_token() {
		$verification_token = $this->context->input()->filter( INPUT_GET, 'googlesitekit_verification_token', FILTER_SANITIZE_STRING );
		$verification_type  = $this->context->input()->filter( INPUT_GET, 'googlesitekit_verification_token_type', FILTER_SANITIZE_STRING );
		$verification_type  = $verification_type ?: self::VERIFICATION_TYPE_META;

		if ( empty( $verification_token ) ) {
			return;
		}

		switch ( $verification_type ) {
			case self::VERIFICATION_TYPE_FILE:
				$this->authentication->verification_file()->set( $verification_token );
				break;
			case self::VERIFICATION_TYPE_META:
				$this->authentication->verification_meta()->set( $verification_token );
		}

		add_filter(
			'googlesitekit_proxy_setup_url_params',
			function ( $params ) use ( $verification_type ) {
				return array_merge(
					$params,
					array(
						'verify'              => 'true',
						'verification_method' => $verification_type,
					)
				);
			}
		);
	}

Here’s a look at the verification request sent during the process:
/wp-admin/index.php?action=googlesitekit_proxy_setup&googlesitekit_code=[SITEKIT-CODE]&googlesitekit_verification_token=[VERIFICATION TOKEN]&googlesitekit_verification_token_type=FILE&nonce=[NONCE]

This was fixed in the latest version with a capability check added on the handle_verification_token function to verify that a verification request occurred during a legitimate authenticated session with a user that had administrative permissions to SETUP the Site Kit by Google plugin.

		if ( ! current_user_can( Permissions::SETUP ) ) {
			wp_die( esc_html__( 'Sorry, you are not allowed to do that.', 'google-site-kit' ), 403 );
		}

These two flaws made it possible for subscriber-level users to become Google Search Console owners on any affected site.

The Impact

When a new owner for a property in Google Search Console is set up, a message is sent via email saying, “Property owners can change critical settings that affect how Google Search interacts with your site or app.”

There are several ways an attacker could make use of Google Search Console owner access for a site. For malicious attackers, the ability to manipulate search engine result pages through Blackhat SEO is particularly attractive. Additionally, an attacker could use search console access in conjunction with another exploit that involves malicious content being injected on a site for monetization.

An owner in Google Search Console can do things like request that URLs be removed from the Google Search engine, view competitive performance data, modify sitemaps, and more.

Unwarranted Google Search Console owner access on a site has the potential to hurt the visibility of a site in Google search results and impact revenue as an attacker removes URLs from search results. More specifically, it could be used to aid a competitor who wants to hurt the ranking and reputation of a site to better improve their own reputation and ranking.

Verifying the Integrity of Google Search Console Ownership

Fortunately, there are ways to verify and monitor if any new Google Search Console owners have been added and the Wordfence firewall provides your site with protection.

As our site cleaning team often sees malicious property owners in Google Search Console added to hacked sites, we’ve included some guidance from them.

Monitoring

Google will alert you via email whenever a new Search Console owner has been added. If you receive one of these emails and have not recently added a new Google Search Console owner, take immediate action and remove the unknown owner.

Verifying and Removing Unwanted Owners

Even if you haven’t received any emails from Google alerting you to the presence of a new Google Search Console owner, you can verify owners from the Google Search Console dashboard.

Checking for Google Search Console users:

  1. Log into your Google Search Console and go to “Settings.” This is where you will find “Users and Permissions.”

    Google Search Console settings location.

  2. Click on “Users and Permissions.” You will see a list of all the Google Search Console users with their name, email address, and role.
  3. Review the users listed. Verify that there are no unknown owners listed.

If you do discover a rogue Google Search Console owner, please take the following actions:

  1. Click the three dots next to the site owner you would like to remove.

    Removing owner.

  2. Click on “Manage property Owners.” Here you can see a log of verification requests and can determine when owners were added, along with the ability to unverify any site owners.

    Managing property owners.

  3. Click “Unverify” for any rogue owner you would like to remove. This will un-verify that account and revoke access to the search console account.

    Un-verifying site owner.

Extra Precaution

Site Kit by Google provides functionality to reset a site’s connection with Site Kit. If you discover that a rogue Google Search Console owner has been added, then we recommend taking the extra step to reset Site Kit by Google on your WordPress site.

Resetting Site Kit by Google:

  1. Log into your WordPress site, and navigate to the Site Kit by Google’s “Settings.”

    Site Kit by Google settings.

  2. Navigate to “Admin Settings” and click on “Reset Site Kit.” Confirm the reset by clicking on “Reset” when prompted. This will reset your Site Kit connection and require you to reconnect any Google services that were previously connected.

    Resetting Site Kit by Google.

Wordfence is Keeping you Protected.

We released a firewall rule on April 21, 2020 to Wordfence Premium users that will block any verification attempts that do not come from administrators, which ultimately provides protection against the creation of any unwarranted Google Search Console owners.

Due to some limitations, the firewall cannot prevent the ProxySetupURL from being displayed in the source code. However, the firewall rule blocking verification requests is enough to provide optimal protection on a site using a vulnerable version of Site Kit by Google.

Free Wordfence users will receive this rule on May 21, 2020.

Proof of Concept Walkthrough

Disclosure Timeline

April 21, 2020 – Initial discovery and analysis of vulnerability. Firewall rule was released for Wordfence Premium customers. We submitted the vulnerability disclosure through Google’s Vulnerability Reward Program, as this appears to be their primary vulnerability disclosure channel.
April 22, 2020 – The vulnerability report was triaged and assigned by the Google Security team.
April 30, 2020 – The Google Security team notifies us that the vulnerability was verified and a bug was filed.
May 4, 2020A commit appears in Site Kit by Google Github Repository that appears to provide a patch for the vulnerability.
May 7, 2020A patch was released.
May 21, 2020 – Free Wordfence users receive firewall rule.

Conclusion

In today’s post, we detailed a flaw that allowed low-level WordPress users the ability to become a Google Search Console owner by exploiting a vulnerability in the Site Kit by Google plugin. This flaw has been fully patched in version 1.8.0. We recommend that users immediately update to the latest version available.

Sites running Wordfence Premium have been protected from attacks against this vulnerability since April 21, 2020. Sites running the free version of Wordfence will receive this firewall rule update on May 21, 2020. If you know a friend or colleague using this plugin on their site, we strongly recommend forwarding this advisory to them so that they can update and protect their Google Search Console account and WordPress site.

Did you enjoy this post? Share it!

Comments

No Comments