ExactMetrics 8.6.0 - 9.0.2 - Authenticated (Custom) Insecure Direct Object Reference to Arbitrary Plugin Installation
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:HTechnical Details
>=8.0.0 <=9.0.2What Changed in the Fix
Changes introduced in v9.0.3
Source Code
WordPress.org SVNThis 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_settingscapability. 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
- Registration:
ExactMetrics_Onboarding::register_routes()(inincludes/admin/class-exactmetrics-onboarding.php) registers theCREATABLEroute for/onboarding/settings. - Authorization: The
permission_callbackpoints tovalidate_onboarding_request. This method validates a customonboarding_keyparameter against a WordPress transientexactmetrics_onboarding_key. - Processing: The callback
store_settings($request)is invoked. - The IDOR Sink: Inside
store_settings(or the underlying logic it calls), the code retrieves$request->get_param('triggered_by'). - Bypass: It performs a check similar to
if ( user_can( $triggered_by, 'install_plugins' ) ). Since the attacker provides1(the ID of the primary admin), this check passes. - Installation: The plugin slug provided in the
install_pluginsarray 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:
- Grant Permission: As Admin, navigate to
ExactMetrics -> Settings -> Advanced -> Permissionsand add theEditorrole to "Save Settings". - Login as Editor: Access the WordPress dashboard.
- Retrieve Key:
- Navigate to
ExactMetrics -> Settings. - Use
browser_evalto extract the key from the localized JavaScript data. - The key is typically found in
window.exactmetrics_admin.onboarding_keyorwindow.exactmetrics_onboarding_wizard.onboarding_key. - Verification:
browser_eval("window.exactmetrics_admin?.onboarding_key || 'not_found'").
- Navigate to
5. Exploitation Strategy
The goal is to install a plugin that allows Remote Code Execution (e.g., wp-file-manager).
Step-by-Step Plan:
- Setup: Configure the environment with an Editor user and the required ExactMetrics permission.
- Extract Key: Navigate to the ExactMetrics settings page as the Editor and extract the
onboarding_key. - 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"] } }
- URL:
- Verification: Confirm the plugin is installed and activated.
6. Test Data Setup
- Install ExactMetrics 9.0.2: Ensure the vulnerable version is active.
- Create Users:
- Administrator:
admin/password(ID 1) - Editor:
attacker/password(ID 2)
- Administrator:
- 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).
- Generate Key: Navigate to
/wp-admin/admin.php?page=exactmetrics_settingsonce as the Editor to ensure the transient is generated.
7. Expected Results
- Response: The REST API should return a
200 OKresponse, possibly containing a success message or the updated settings object. - Action: The
wp-file-managerplugin (or the requested slug) will be downloaded, installed, and potentially activated.
8. Verification Steps
- Check Plugin Directory:
wp plugin is-installed wp-file-manager- Or:
ls -la /var/www/html/wp-content/plugins/wp-file-manager
- Check Activation:
wp plugin status wp-file-manager
9. Alternative Approaches
If store_settings structure differs slightly in the JSON nesting:
- Try placing
install_pluginsat the top level of the JSON body. - Try
triggered_byas a query parameter:POST /wp-json/exactmetrics/v1/onboarding/settings?triggered_by=1. - If the
onboarding_keyis not found in global JS, checkExactMetrics_Onboarding::get_info(GET /wp-json/exactmetrics/v1/onboarding/settings?onboarding_key=...) to see if it refreshes or leaks more configuration info.
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
@@ -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.