CVE-2026-1992

ExactMetrics 8.6.0 - 9.0.2 - Authenticated (Custom) Insecure Direct Object Reference to Arbitrary Plugin Installation

highAuthorization Bypass Through User-Controlled Key
8.8
CVSS Score
8.8
CVSS Score
high
Severity
9.0.3
Patched in
1d
Time to patch

Description

The ExactMetrics – Google Analytics Dashboard for WordPress plugin is vulnerable to Insecure Direct Object Reference in versions 8.6.0 through 9.0.2. This is due to the `store_settings()` method in the `ExactMetrics_Onboarding` class accepting a user-supplied `triggered_by` parameter that is used instead of the current user's ID to check permissions. This makes it possible for authenticated attackers with the `exactmetrics_save_settings` capability to bypass the `install_plugins` capability check by specifying an administrator's user ID in the `triggered_by` parameter, allowing them to install arbitrary plugins and achieve Remote Code Execution. This vulnerability only affects sites on which administrator has given other user types the permission to view reports and can only be exploited by users of that type.

CVSS Vector Breakdown

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

Technical Details

Affected versions>=8.0.0 <=9.0.2
PublishedMarch 10, 2026
Last updatedMarch 11, 2026

What Changed in the Fix

Changes introduced in v9.0.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the exploitation of an IDOR vulnerability in the ExactMetrics plugin (versions 8.6.0 - 9.0.2) that allows authenticated users with the `exactmetrics_save_settings` capability to install arbitrary plugins by impersonating an administrator via the `triggered_by` parameter. …

Show full research plan

This research plan outlines the exploitation of an IDOR vulnerability in the ExactMetrics plugin (versions 8.6.0 - 9.0.2) that allows authenticated users with the exactmetrics_save_settings capability to install arbitrary plugins by impersonating an administrator via the triggered_by parameter.

1. Vulnerability Summary

The ExactMetrics_Onboarding class provides a REST API endpoint /onboarding/settings intended for the onboarding wizard. The store_settings() method processes updates to plugin settings. When a request includes instructions to install plugins, the method checks for the install_plugins capability. However, instead of checking the currently logged-in user's capabilities, it uses a user-supplied triggered_by parameter to determine the permission level. This allows a low-privileged user (who has been granted ExactMetrics-specific permissions) to provide an Administrator's ID (usually 1) and bypass the core WordPress capability check.

2. Attack Vector Analysis

  • Endpoint: POST /wp-json/exactmetrics/v1/onboarding/settings
  • Method: CREATABLE (POST)
  • Vulnerable Parameter: triggered_by
  • Authentication: Authenticated user with the exactmetrics_save_settings capability. By default, only administrators have this, but administrators can grant it to Editors or Authors via the plugin's "Advanced" settings.
  • Preconditions: The site must have the "Save Settings" permission granted to a non-admin role in ExactMetrics -> Settings -> Advanced -> Permissions.

3. Code Flow

  1. Registration: ExactMetrics_Onboarding::register_routes() (in includes/admin/class-exactmetrics-onboarding.php) registers the CREATABLE route for /onboarding/settings.
  2. Authorization: The permission_callback points to validate_onboarding_request. This method validates a custom onboarding_key parameter against a WordPress transient exactmetrics_onboarding_key.
  3. Processing: The callback store_settings($request) is invoked.
  4. The IDOR Sink: Inside store_settings (or the underlying logic it calls), the code retrieves $request->get_param('triggered_by').
  5. Bypass: It performs a check similar to if ( user_can( $triggered_by, 'install_plugins' ) ). Since the attacker provides 1 (the ID of the primary admin), this check passes.
  6. Installation: The plugin slug provided in the install_plugins array is passed to the plugin installer.

4. Nonce & Key Acquisition Strategy

The REST endpoint uses a custom onboarding_key instead of a standard wp_rest nonce. This key is generated by the plugin and stored in a transient when an authorized user accesses the settings or onboarding wizard.

Acquisition Steps:

  1. Grant Permission: As Admin, navigate to ExactMetrics -> Settings -> Advanced -> Permissions and add the Editor role to "Save Settings".
  2. Login as Editor: Access the WordPress dashboard.
  3. Retrieve Key:
    • Navigate to ExactMetrics -> Settings.
    • Use browser_eval to extract the key from the localized JavaScript data.
    • The key is typically found in window.exactmetrics_admin.onboarding_key or window.exactmetrics_onboarding_wizard.onboarding_key.
    • Verification: browser_eval("window.exactmetrics_admin?.onboarding_key || 'not_found'").

5. Exploitation Strategy

The goal is to install a plugin that allows Remote Code Execution (e.g., wp-file-manager).

Step-by-Step Plan:

  1. Setup: Configure the environment with an Editor user and the required ExactMetrics permission.
  2. Extract Key: Navigate to the ExactMetrics settings page as the Editor and extract the onboarding_key.
  3. Forge Request: Send a POST request to the REST API.
    • URL: http://vulnerable-site.local/wp-json/exactmetrics/v1/onboarding/settings
    • Headers: Content-Type: application/json
    • Payload:
      {
        "onboarding_key": "[EXTRACTED_KEY]",
        "triggered_by": 1,
        "settings": {
          "install_plugins": ["wp-file-manager"]
        }
      }
      
  4. Verification: Confirm the plugin is installed and activated.

6. Test Data Setup

  1. Install ExactMetrics 9.0.2: Ensure the vulnerable version is active.
  2. Create Users:
    • Administrator: admin / password (ID 1)
    • Editor: attacker / password (ID 2)
  3. Configure Permissions:
    • Use WP-CLI to grant the Editor role permission (or do it manually via the UI):
    • wp option patch insert exactmetrics_settings save_settings editor (Note: exact option path may vary, UI is more reliable for setup).
  4. Generate Key: Navigate to /wp-admin/admin.php?page=exactmetrics_settings once as the Editor to ensure the transient is generated.

7. Expected Results

  • Response: The REST API should return a 200 OK response, possibly containing a success message or the updated settings object.
  • Action: The wp-file-manager plugin (or the requested slug) will be downloaded, installed, and potentially activated.

8. Verification Steps

  1. Check Plugin Directory:
    • wp plugin is-installed wp-file-manager
    • Or: ls -la /var/www/html/wp-content/plugins/wp-file-manager
  2. Check Activation:
    • wp plugin status wp-file-manager

9. Alternative Approaches

If store_settings structure differs slightly in the JSON nesting:

  • Try placing install_plugins at the top level of the JSON body.
  • Try triggered_by as a query parameter: POST /wp-json/exactmetrics/v1/onboarding/settings?triggered_by=1.
  • If the onboarding_key is not found in global JS, check ExactMetrics_Onboarding::get_info (GET /wp-json/exactmetrics/v1/onboarding/settings?onboarding_key=...) to see if it refreshes or leaks more configuration info.
Research Findings
Static analysis — not yet PoC-verified

Summary

ExactMetrics versions 8.6.0 through 9.0.2 are vulnerable to an Insecure Direct Object Reference (IDOR) that allows authenticated users to install arbitrary plugins. The vulnerability exists in the `store_settings()` method, which uses a user-supplied `triggered_by` parameter to perform capability checks instead of the actual logged-in user's ID, allowing low-privileged users with 'Save Settings' permissions to impersonate an administrator.

Vulnerable Code

// includes/admin/class-exactmetrics-onboarding.php

register_rest_route( $namespace, '/onboarding/settings', array(
    'args' => array(
        'onboarding_key' => array(
            'required'          => true,
            'type'              => 'string',
            'sanitize_callback' => 'sanitize_text_field',
            'validate_callback' => function( $param ) {
                return ! empty( $param );
            },
        ),
    ),
    array(
        'methods'             => WP_REST_Server::READABLE,
        'callback'            => array( $this, 'get_info' ),
        'permission_callback' => array( $this, 'validate_onboarding_request' ),
    ),
    array(
        'methods'             => WP_REST_Server::CREATABLE,
        'callback'            => array( $this, 'store_settings' ),
        'permission_callback' => array( $this, 'validate_onboarding_request' ),
    ),
) );

--- 

// Logical representation of the vulnerable store_settings method inside includes/admin/class-exactmetrics-onboarding.php

public function store_settings( $request ) {
    $triggered_by = $request->get_param( 'triggered_by' );
    $settings     = $request->get_param( 'settings' );

    if ( ! empty( $settings['install_plugins'] ) ) {
        // Vulnerable check: uses the user-provided ID from 'triggered_by' instead of get_current_user_id()
        if ( user_can( $triggered_by, 'install_plugins' ) ) {
             // Logic to install plugins provided in settings['install_plugins']
        }
    }
}

Security Fix

diff -ru includes/admin/class-exactmetrics-onboarding.php includes/admin/class-exactmetrics-onboarding.php
--- includes/admin/class-exactmetrics-onboarding.php
+++ includes/admin/class-exactmetrics-onboarding.php
@@ -210,7 +210,7 @@
 	public function store_settings( $request ) {
-		$triggered_by = $request->get_param( 'triggered_by' );
+		$triggered_by = get_current_user_id();
 		$settings     = $request->get_param( 'settings' );
 
 		if ( ! empty( $settings['install_plugins'] ) ) {
-			if ( user_can( $triggered_by, 'install_plugins' ) ) {
+			if ( current_user_can( 'install_plugins' ) ) {

Exploit Outline

The exploit targets the `/wp-json/exactmetrics/v1/onboarding/settings` REST API endpoint. To exploit this, an attacker needs a valid 'onboarding_key', which is stored in a WordPress transient and localized into the plugin's administration JavaScript. 1. Authentication: The attacker must be logged in as a user who has been granted the 'exactmetrics_save_settings' capability (often Authors or Editors on sites where an Admin has adjusted ExactMetrics permissions). 2. Key Retrieval: The attacker navigates to any ExactMetrics settings page and extracts the `onboarding_key` from the `window.exactmetrics_admin` or `window.exactmetrics_onboarding_wizard` JavaScript objects. 3. Payload Creation: The attacker constructs a POST request containing the `onboarding_key`, a `triggered_by` parameter set to the ID of a known administrator (typically '1'), and a `settings` object containing an `install_plugins` array with the slug of the desired plugin (e.g., 'wp-file-manager'). 4. Execution: Upon sending the request, the plugin validates the key and then checks if the user identified by `triggered_by` (the Admin) has the `install_plugins` capability. Because this check passes, the plugin proceeds to download, install, and activate the specified plugin, potentially allowing the attacker to achieve Remote Code Execution (RCE).

Check if your site is affected.

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