CVE-2025-69091

Demo Importer Plus <= 2.0.8 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.0.9
Patched in
10d
Time to patch

Description

The Demo Importer Plus plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 2.0.8. This makes it possible for authenticated attackers, with Subscriber-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.0.8
PublishedJanuary 5, 2026
Last updatedJanuary 14, 2026
Affected plugindemo-importer-plus

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the systematic approach for analyzing and exploiting **CVE-2025-69091** in the **Demo Importer Plus** plugin. This vulnerability involves a missing capability check, allowing Subscriber-level users to perform unauthorized actions via AJAX. --- ### 1. Vulnerability Summa…

Show full research plan

This research plan outlines the systematic approach for analyzing and exploiting CVE-2025-69091 in the Demo Importer Plus plugin. This vulnerability involves a missing capability check, allowing Subscriber-level users to perform unauthorized actions via AJAX.


1. Vulnerability Summary

  • Vulnerability: Missing Authorization (IDOR/Broken Access Control).
  • Location: Likely within an AJAX handler registered via add_action( 'wp_ajax_...' ).
  • Cause: The function handling the AJAX request fails to verify the user's permissions using current_user_can( 'manage_options' ). While it may (or may not) check a nonce, nonces in the WordPress admin area are often accessible to any authenticated user, including Subscribers.
  • Impact: An authenticated Subscriber can trigger administrative actions defined in the vulnerable function, such as modifying plugin settings, initiating/modifying demo imports, or potentially altering site content depending on the specific function's logic.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Authentication: Subscriber-level credentials (minimum).
  • Protocol: HTTP POST.
  • Vulnerable Action: (Inferred) An AJAX action related to demo content management or plugin configuration. Likely prefixed with dip_ or demo_importer_.
  • Preconditions: The plugin must be active. The attacker must be logged in as a Subscriber.

3. Code Flow (Inferred)

  1. The plugin registers an AJAX action during initialization (likely in includes/class-demo-importer-plus.php or similar):
    add_action( 'wp_ajax_VULNERABLE_ACTION', array( $this, 'vulnerable_function' ) );
  2. A Subscriber sends a POST request to admin-ajax.php with action=VULNERABLE_ACTION.
  3. WordPress routes the request to vulnerable_function().
  4. Inside vulnerable_function(), the code may check a nonce using check_ajax_referer() or wp_verify_nonce().
  5. Critical Failure: The function proceeds to execute logic (e.g., updating options, downloading files, or processing data) without calling current_user_can().

4. Nonce Acquisition Strategy

If the function requires a nonce, a Subscriber can likely find it because many plugins enqueue their administrative scripts (and nonces) on all admin pages, or specifically on the Profile page which Subscribers can access.

  1. Identify the Script/Variable: Use grep to find where the nonce is created and localized:
    grep -r "wp_create_nonce" .
    grep -r "wp_localize_script" .
  2. Locate the Key: Look for a pattern like:
    wp_localize_script( 'dip-admin-js', 'dip_ajax_obj', [
        'ajax_url' => admin_url( 'admin-ajax.php' ),
        'nonce'    => wp_create_nonce( 'dip_nonce_action' )
    ]);
    
  3. Extraction (Agent Steps):
    • Log in as a Subscriber.
    • Navigate to the Subscriber's profile: /wp-admin/profile.php.
    • Check if the variable dip_ajax_obj (or similar) is present in the DOM.
    • Use browser_eval("window.dip_ajax_obj?.nonce") to extract the value.

5. Exploitation Strategy

The goal is to trigger the unauthorized action. We will assume the action is related to a demo import step or a configuration update.

  1. Discovery: Run the following within the plugin directory:
    # Find all AJAX actions
    grep -r "wp_ajax_" .
    
    # Identify functions associated with those actions and check for current_user_can
    # Example: If 'wp_ajax_dip_save_settings' is found, check its function body.
    
  2. Target Selection: Look for an action that performs an "Update" or "Save" operation without a capability check.
  3. HTTP Request (via http_request):
    • Method: POST
    • URL: http://[target]/wp-admin/admin-ajax.php
    • Headers: Content-Type: application/x-www-form-urlencoded, Cookie: [Subscriber Cookies]
    • Body: action=VULNERABLE_ACTION&nonce=[EXTRACTED_NONCE]&payload_param=malicious_value

6. Test Data Setup

  1. Plugin: Install and activate Demo Importer Plus version 2.0.8.
  2. User: Create a Subscriber user:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password
  3. Content: If the action modifies specific demo settings, ensure at least one demo configuration is present or the plugin is in its default state.

7. Expected Results

  • Success: The server returns a success code (e.g., {"success":true} or 1) and the unauthorized action is performed (e.g., an option is changed in the database).
  • Failure: The server returns 403 Forbidden or -1 (if the nonce fails) or the action is ignored because a capability check was actually present.

8. Verification Steps

After the exploit attempt, use WP-CLI to verify the change:

  1. Check Options: If the action modifies a setting:
    wp option get dip_settings (or the relevant option name found during research).
  2. Check Site State: If the action initiates a demo import task:
    wp post list (to see if new content was added) or wp option get [plugin_task_status].

9. Alternative Approaches

  • Bypass Nonce Check: If check_ajax_referer is called with the third parameter $die = false, check if the return value is actually validated. If not, the nonce can be omitted or sent as an invalid string.
  • REST API: Check if the plugin registers any REST API routes (register_rest_route) with permission_callback set to __return_true or missing entirely. These are often easier to exploit than AJAX.
    grep -r "register_rest_route" .

Check if your site is affected.

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