CVE-2026-42736

Better Messages – Chat Rooms, Group Chat, Private Messages & AI Chat Bots <= 2.14.16 - Unauthenticated Insecure Direct Object Reference

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

Description

The Better Messages – Chat Rooms, Group Chat, Private Messages & AI Chat Bots plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 2.14.16 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<=2.14.16
PublishedMay 27, 2026
Last updatedJune 2, 2026
Affected pluginbp-better-messages

What Changed in the Fix

Changes introduced in v2.15.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-42736 (Better Messages IDOR) ## 1. Vulnerability Summary The **Better Messages** plugin (up to 2.14.16) contains an **Insecure Direct Object Reference (IDOR)** vulnerability in its AI Addon's REST API callback handling. The vulnerability exists because an unau…

Show full research plan

Exploitation Research Plan: CVE-2026-42736 (Better Messages IDOR)

1. Vulnerability Summary

The Better Messages plugin (up to 2.14.16) contains an Insecure Direct Object Reference (IDOR) vulnerability in its AI Addon's REST API callback handling. The vulnerability exists because an unauthenticated REST endpoint, designed to receive callbacks from the "Better Messages Cloud AI" service, fails to properly validate a "key" parameter provided by the user. An attacker can provide an arbitrary message_id and a dummy key to trigger internal actions, such as overwriting message translations or metadata, without authentication.

2. Attack Vector Analysis

  • Endpoint: POST /wp-json/better-messages/v1/cloud-ai/callback
  • Authentication: Unauthenticated (REST permission_callback returns true).
  • Vulnerable Parameter: key (used for authorization but not validated) and message_id (the object reference).
  • Action Type: translate (and potentially others like moderate or transcribe).
  • Preconditions: The AI module must be active (usually requires PHP 8.1+ as seen in addons/ai/ai.php).

3. Code Flow

  1. Route Registration: In Better_Messages_AI::rest_api_init (located in addons/ai/ai.php), a REST route is registered: /better-messages/v1/cloud-ai/callback.
  2. Request Handling: The handler for this route (e.g., cloud_ai_callback) accepts a JSON payload containing action, message_id, and key.
  3. Flawed Validation: The code checks if a key is present in the request but fails to verify that this key matches the site's secret cloudAiKey or a specific job token.
  4. Action Trigger: Based on the action parameter (e.g., translate), it triggers the WordPress action better_messages_cloud_ai_translate_result via do_action().
  5. Sink: The function Better_Messages_AI::handle_translate_callback( $message_id, $data ) (hooked at addons/ai/ai.php:122) is executed.
  6. Data Modification: handle_translate_callback uses Better_Messages()->functions->update_message_meta() to store the "translated" text provided in the request payload into the database for the specified message_id.

4. Nonce Acquisition Strategy

The targeted REST endpoint /wp-json/better-messages/v1/cloud-ai/callback is designed for server-to-server communication and typically uses the permission_callback => '__return_true' pattern, meaning no WordPress nonce is required for the exploit.

5. Exploitation Strategy

The goal is to modify the content (translation) of a private message.

Step 1: Identify a Target Message

Use WP-CLI to find a valid message ID in the system.

wp db query "SELECT id FROM wp_bp_messages_messages LIMIT 1"

Step 2: Perform the Injection

Send a specially crafted POST request to the callback endpoint.

  • URL: http://localhost:8080/wp-json/better-messages/v1/cloud-ai/callback
  • Method: POST
  • Headers: Content-Type: application/json
  • Payload:
{
  "action": "translate",
  "message_id": [TARGET_MESSAGE_ID],
  "key": "anything",
  "result": {
    "translation": "ATTACKER_CONTROLLED_TEXT_OVERWRITE",
    "language": "en"
  }
}

6. Test Data Setup

  1. Ensure AI Addon is Loaded: The plugin requires PHP 8.1+ for the AI component to initialize (addons/ai/ai.php:24).
  2. Enable AI Translation:
    # Settings are stored in a serialized array in the options table
    wp option get bp-better-messages-settings --format=json > settings.json
    # Ensure "aiTranslationEnabled" is set to "1" in the JSON and update
    wp option update bp-better-messages-settings --format=json < settings.json
    
  3. Create a Test Message:
    Use a subscriber account to send
Research Findings
Static analysis — not yet PoC-verified

Summary

The Better Messages plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) via its AI Addon's REST API callback endpoint. The vulnerability allows unauthenticated attackers to manipulate message metadata and translations for arbitrary message IDs because the endpoint fails to validate a user-controlled 'key' against a site secret.

Vulnerable Code

// addons/ai/ai.php (within rest_api_init method)
register_rest_route('better-messages/v1', '/cloud-ai/callback', array(
    'methods' => 'POST',
    'callback' => array( $this, 'cloud_ai_callback' ),
    'permission_callback' => '__return_true',
));

---

// addons/ai/ai.php (inferred implementation of cloud_ai_callback)
public function cloud_ai_callback( WP_REST_Request $request )
{
    $params = $request->get_json_params();

    if ( ! isset( $params['key'] ) ) {
        return new WP_Error( 'rest_forbidden', __( 'Forbidden', 'bp-better-messages' ), array( 'status' => 403 ) );
    }

    // The vulnerability: The code checks for the existence of 'key' but fails to verify if its value matches the secret cloudAiKey
    $action = $params['action'];
    $message_id = $params['message_id'];
    $result = $params['result'];

    do_action( 'better_messages_cloud_ai_' . $action . '_result', $message_id, $result );

    return array( 'success' => true );
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/bp-better-messages/2.14.16/addons/ai/ai.php /home/deploy/wp-safety.org/data/plugin-versions/bp-better-messages/2.15.0/addons/ai/ai.php
--- /home/deploy/wp-safety.org/data/plugin-versions/bp-better-messages/2.14.16/addons/ai/ai.php	2026-04-07 08:11:34.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/bp-better-messages/2.15.0/addons/ai/ai.php	2026-04-30 06:15:22.000000000 +0000
@@ -50,6 +50,7 @@
                 add_action( 'better_messages_before_message_delete', array( $this, 'before_delete_message' ), 10 , 3 );
 
                 add_action( 'bp_better_messages_new_thread_created', array( $this, 'on_new_thread_created'), 10, 2 );
+                add_filter( 'better_messages_ai_bot_instruction', array( $this, 'apply_placeholders_to_instruction' ), 10, 3 );
                 add_filter( 'better_messages_can_send_message', array( $this, 'block_reply_if_needed' ), 20, 3 );
                 add_filter( 'better_messages_can_send_message', array( $this, 'check_ai_bot_balance' ), 21, 3 );
                 add_action( 'better_messages_before_new_thread',  array( $this, 'restrict_new_thread_if_needed'), 10, 2 );
@@ -191,23 +192,191 @@
 
             $recipients = Better_Messages()->functions->get_recipients( $thread_id );
 
-            if( count( $recipients ) === 2 ) {
-                foreach ($recipients as $user) {
-                    $user_id = $user->user_id;
-                    if ($user_id < 0) {
-                        $guest_id = absint($user_id);
-                        $guest = Better_Messages()->guests->get_guest_user($guest_id);
+            if( count( $recipients ) !== 2 ) {
+                return;
+            }
+
+            $bot_id = 0;
+            $other_user_id = 0;
+
+            foreach ( $recipients as $user ) {
+                $user_id = (int) $user->user_id;
+                $candidate_bot_id = $user_id < 0 ? $this->get_bot_id_from_user( $user_id ) : false;
+
+                if ( $candidate_bot_id && $this->bot_exists( $candidate_bot_id ) ) {
+                    Better_Messages()->functions->update_thread_meta( $thread_id, 'ai_bot_thread', $candidate_bot_id );
+                    $bot_id = $candidate_bot_id;
+                } else {
+                    $other_user_id = $user_id;
+                }
+            }
+
+            if ( $bot_id > 0 && $other_user_id !== 0 ) {
+                $this->maybe_send_welcome_message( $thread_id, $bot_id, $other_user_id, $message_id );
+            }
+        }
+... (truncated)

Exploit Outline

1. Attacker constructs a POST request to '/wp-json/better-messages/v1/cloud-ai/callback'. 2. The request is unauthenticated as the 'permission_callback' for this route returns true. 3. The attacker provides a target 'message_id' belonging to a private conversation they do not have access to. 4. The attacker sets the 'key' parameter to an arbitrary string (e.g., 'random_key'). 5. The attacker sets the 'action' parameter to 'translate' and provides a 'result' object containing malicious or misleading text. 6. Because the server does not verify the 'key' against the legitimate Cloud AI secret, it executes handle_translate_callback(), which uses update_message_meta() to overwrite the message's translation data in the database with the attacker's payload.

Check if your site is affected.

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