CVE-2025-69394

Cnvrse < 026.02.10.20 - Unauthenticated Insecure Direct Object Reference

mediumAuthorization Bypass Through User-Controlled Key
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
026.02.10.20
Patched in
64d
Time to patch

Description

The Cnvrse plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and excluding, 026.02.10.20 due to missing validation on a user controlled key. This makes it possible for unauthenticated attackers to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<026.02.10.20
PublishedFebruary 11, 2026
Last updatedApril 15, 2026
Affected plugincnvrse

What Changed in the Fix

Changes introduced in v026.02.10.20

Loading patch diff...

Source Code

WordPress.org SVN
Vulnerable v025.12.24.01
Patched v026.02.10.20
Research Plan
Unverified

This vulnerability is an **Insecure Direct Object Reference (IDOR)** in the Cnvrse plugin, specifically within its REST API implementation. The plugin fails to validate whether the requester has the authority to interact with a specific "object" (likely a conversation or message) identified by a use…

Show full research plan

This vulnerability is an Insecure Direct Object Reference (IDOR) in the Cnvrse plugin, specifically within its REST API implementation. The plugin fails to validate whether the requester has the authority to interact with a specific "object" (likely a conversation or message) identified by a user-controlled key (ID).

Because the CVSS vector shows Integrity: Low and Confidentiality: None, the vulnerability likely allows an unauthenticated attacker to modify the state of a conversation (e.g., closing it, marking it as read, or changing its status) rather than reading private chat content.

1. Vulnerability Summary

  • Vulnerability: IDOR (Insecure Direct Object Reference).
  • Location: REST API endpoints registered by the plugin (likely within src/ or includes/ not provided in the snippet, typically under the cnvrse/v1 namespace).
  • Cause: The permission_callback for a sensitive REST route either returns true (allowing unauthenticated access) or fails to verify that the provided conversation/visitor ID belongs to the current session.
  • Impact: Unauthenticated attackers can manipulate the status of any active chat conversation by guessing or iterating through IDs.

2. Attack Vector Analysis

  • Endpoint: A REST API route, likely POST /wp-json/cnvrse/v1/conversations/(?P<id>\d+)/status or POST /wp-json/cnvrse/v1/conversation/update.
  • Parameter: A user-controlled "key" or "id" representing a conversation record.
  • Authentication: None required (Unauthenticated).
  • Precondition: At least one active conversation must exist in the system for the attacker to target.

3. Code Flow (Inferred)

  1. Route Registration: The plugin calls register_rest_route( 'cnvrse/v1', '/conversation/(?P<id>\d+)', ... ).
  2. Missing Authorization: The permission_callback is set to __return_true to allow the chat widget (public visitors) to use it.
  3. Missing Ownership Check: In the controller method (e.g., update_conversation), the code retrieves the conversation object using the id from the request: $conversation = get_post( $request['id'] );.
  4. Unauthorized Action: The code proceeds to update the post status or metadata without checking if the visitor's session cookie or a secret "visitor key" matches the owner of that conversation.

4. Nonce Acquisition Strategy

REST API endpoints in WordPress usually require a _wpnonce (for the wp_rest action) if the user is authenticated. For unauthenticated "public" endpoints, a nonce is often required to prevent CSRF.

  1. Identify Script: The plugin likely enqueues a script for the chat widget. Search for wp_localize_script in the codebase.
  2. Create Trigger Page: Create a page where the chat widget is active.
    wp post create --post_type=page --post_status=publish --post_title="Chat Page" --post_content="Contact us!"
    
  3. Extract Nonce:
    Navigate to the page and use browser_eval to find the localization object.
    • Candidate Variable: window.cnvrseSettings or window.cnvrse_data.
    • Command: browser_eval("window.cnvrseSettings?.nonce") or browser_eval("window.cnvrse_data?.rest_nonce").

5. Exploitation Strategy

Step 1: Discover the Target ID
Since conversations are stored as Custom Post Types (inferred from readme), they use sequential IDs. The attacker can target a known ID or iterate.

Step 2: Identify the REST Endpoint
Use grep to find the specific route:

grep -r "register_rest_route" .

Look for routes in the cnvrse/v1 namespace that handle "status", "close", or "update".

Step 3: Perform the IDOR Attack
Assuming the endpoint is /wp-json/cnvrse/v1/conversations/<id>/close:

  • Request Method: POST
  • URL: http://vulnerable-test.local/wp-json/cnvrse/v1/conversations/123/close (where 123 is the target)
  • Headers: Content-Type: application/json, X-WP-Nonce: <obtained_nonce>
  • Payload: { "status": "closed" } (if applicable)

Step 4: Use http_request (Example)

await http_request({
  url: "http://localhost:8080/wp-json/cnvrse/v1/conversations/123",
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-WP-Nonce": "a1b2c3d4e5"
  },
  body: JSON.stringify({
    "status": "closed"
  })
});

6. Test Data Setup

  1. Activate Plugin: Ensure Cnvrse Lite is active.
  2. Simulate Victim: Open the site as a regular visitor and send a message through the chat widget. This creates a conversation record in the database.
  3. Identify Victim ID: Use WP-CLI to find the ID of the newly created conversation.
    # Find the post type first
    wp post-type list
    # List posts of the chat type (e.g., cnvrse_conversation)
    wp post list --post_type=cnvrse_conversation
    

7. Expected Results

  • The REST API returns a 200 OK or 201 Created response.
  • The response body confirms the status change (e.g., {"success": true}).
  • The attacker (who does not own conversation 123) successfully changed its state.

8. Verification Steps

After sending the exploit request, check the database state via WP-CLI:

# Check if the post status or a specific meta field changed
wp post get <ID> --field=post_status
# Or check meta if status is stored there
wp post meta list <ID>

9. Alternative Approaches

If the sequential ID is protected or validated, check for a "Visitor Key" IDOR:

  1. Many chat plugins use a UUID in a cookie (e.g., cnvrse_visitor_id).
  2. Check if the API accepts this key as a parameter.
  3. Attempt to use your session's nonce but another visitor's UUID in the request body to see if the plugin validates the link between the session and the UUID.

Check if your site is affected.

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