CVE-2025-14799

Brevo - Email, SMS, Web Push, Chat, and more. <= 3.3.0 - Unauthenticated Authorization Bypass via Type Juggling

mediumAccess of Resource Using Incompatible Type ('Type Confusion')
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
3.3.1
Patched in
1d
Time to patch

Description

The Brevo - Email, SMS, Web Push, Chat, and more. plugin for WordPress is vulnerable to authorization bypass due to type juggling in all versions up to, and including, 3.3.0. This is due to the use of loose comparison (==) instead of strict comparison (===) when validating the installation ID in the `/wp-json/mailin/v1/mailin_disconnect` REST API endpoint. This makes it possible for unauthenticated attackers to disconnect the Brevo integration, delete the API key, remove all subscription forms, and reset plugin settings by sending a boolean `true` value for the `id` parameter, which bypasses the authorization check through PHP type juggling.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.3.0
PublishedFebruary 17, 2026
Last updatedFebruary 18, 2026
Affected pluginmailin

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2025-14799 Brevo Authorization Bypass via Type Juggling ## 1. Vulnerability Summary The **Brevo (formerly Sendinblue)** plugin for WordPress (<= 3.3.0) contains an authorization bypass vulnerability in its REST API implementation. The endpoint `/wp-json/mailin/v1/mailin_disconn…

Show full research plan

Research Plan: CVE-2025-14799 Brevo Authorization Bypass via Type Juggling

1. Vulnerability Summary

The Brevo (formerly Sendinblue) plugin for WordPress (<= 3.3.0) contains an authorization bypass vulnerability in its REST API implementation. The endpoint /wp-json/mailin/v1/mailin_disconnect is intended to allow the Brevo service to disconnect the integration. However, the code uses a loose comparison (==) to validate the provided installation ID against the stored ID. Because of PHP type juggling, providing a boolean true for the id parameter causes the comparison true == "any-stored-string" to evaluate to true. This allows an unauthenticated attacker to reset the plugin's configuration, delete the API key, and remove subscription forms.

2. Attack Vector Analysis

  • Endpoint: /wp-json/mailin/v1/mailin_disconnect (inferred from description).
  • Method: POST (standard for state-changing REST actions).
  • Payload Type: application/json (necessary to pass a boolean true).
  • Parameter: id
  • Authentication: Unauthenticated (The permission_callback likely returns __return_true or performs no capability check, relying entirely on the id validation).
  • Preconditions: The plugin must be active and ideally configured with an API key (to observe the "disconnect" effect).

3. Code Flow

  1. Registration: The plugin registers the REST route during the rest_api_init hook.
    • Likely file: includes/class-mailin-rest-api.php or mailin.php.
    • Expected code:
      register_rest_route( 'mailin/v1', '/mailin_disconnect', array(
          'methods' => 'POST',
          'callback' => array( $this, 'mailin_disconnect_callback' ),
          'permission_callback' => '__return_true', // Or similar weak check
      ));
      
  2. Callback Execution: The callback (e.g., mailin_disconnect_callback) retrieves the id from the WP_REST_Request.
  3. Vulnerable Comparison:
    • The plugin retrieves the stored Brevo ID (likely from get_option( 'mailin_id' ) or similar).
    • It performs a check: if ( $request_id == $stored_id ).
    • If the request body is {"id": true}, the $request_id becomes bool(true).
    • In PHP, true == "any-non-empty-string" is true.
  4. Action: Upon "successful" validation, the plugin calls a method to delete configuration options (e.g., delete_option('mailin_api_key')).

4. Nonce Acquisition Strategy

REST API endpoints in WordPress typically require a nonce (X-WP-Nonce header) only if the user is authenticated via cookie. For unauthenticated access to public-facing REST routes (like those intended for webhooks/service callbacks), nonces are usually not required or checked.

  • Check: If the exploit returns a 403 Rest Forbidden with a "Cookie nonce is invalid" message, a nonce is needed.
  • Acquisition (if needed):
    1. The Brevo plugin likely enqueues scripts on the admin dashboard or specific public pages.
    2. Use wp post create to create a page with a Brevo subscription form shortcode (likely [sib_form]).
    3. Navigate to that page using browser_navigate.
    4. Execute browser_eval("window.wpApiSettings?.nonce") to retrieve the nonce.

5. Exploitation Strategy

  1. Preparation: Verify the plugin is active and has a mock API key set.
  2. Exploit Request: Send a POST request to the REST API with a JSON body containing id: true.
  3. Target URL: http://localhost:8080/wp-json/mailin/v1/mailin_disconnect
  4. Headers:
    • Content-Type: application/json
  5. Body:
    {
      "id": true
    }
    

6. Test Data Setup

  1. Activate Plugin: wp plugin activate mailin
  2. Set Mock Configuration:
    wp option update mailin_api_key "xkeysib-test-key-123456789"
    wp option update mailin_activate "yes"
    # Create a dummy Brevo ID that the plugin would check against
    wp option update mailin_id "brevo_prod_instance_abc123"
    
  3. Verify Setup: wp option get mailin_api_key should return the key.

7. Expected Results

  • HTTP Response: 200 OK or 204 No Content.
  • Side Effect: The plugin settings are wiped. Specifically, the mailin_api_key option should be deleted or emptied.

8. Verification Steps

  1. Check Options via WP-CLI:
    wp option get mailin_api_key
    
    • Success Condition: The command returns an error (Option does not exist) or an empty string.
  2. Check Plugin State:
    wp option get mailin_activate
    
    • Success Condition: Should be empty or "no".

9. Alternative Approaches

  • Parameter Sources: If the JSON body fails, try the payload as a URL query parameter: /wp-json/mailin/v1/mailin_disconnect?id=1 (though 1 is less likely to juggle to a complex string than true in a JSON context).
  • Endpoint Discovery: If /mailin_disconnect is not exact, query the REST index to find the correct route:
    http_request("GET", "http://localhost:8080/wp-json/mailin/v1")
  • Method Spoofing: If POST is denied, try GET or adding ?_method=POST to the URL.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Brevo plugin for WordPress is vulnerable to an unauthenticated authorization bypass via PHP type juggling in the /wp-json/mailin/v1/mailin_disconnect REST API endpoint. By sending a JSON body with a boolean true value for the 'id' parameter, an attacker can satisfy a loose comparison (==) against the stored installation ID, allowing them to remotely disconnect the plugin and wipe its configuration.

Vulnerable Code

// File: includes/class-mailin-rest-api.php (inferred)

register_rest_route( 'mailin/v1', '/mailin_disconnect', array(
    'methods' => 'POST',
    'callback' => array( $this, 'mailin_disconnect_callback' ),
    'permission_callback' => '__return_true', // Relying on ID check instead of capability check
));

---

// File: includes/class-mailin-rest-api.php (inferred callback logic)

public function mailin_disconnect_callback( $request ) {
    $params = $request->get_params();
    $request_id = isset( $params['id'] ) ? $params['id'] : '';
    $stored_id  = get_option( 'mailin_id' );

    // Vulnerable loose comparison
    if ( $request_id == $stored_id ) {
        // Logic to disconnect and delete options
        delete_option( 'mailin_api_key' );
        delete_option( 'mailin_activate' );
        return new WP_REST_Response( array( 'success' => true ), 200 );
    }

    return new WP_REST_Response( array( 'success' => false ), 403 );
}

Security Fix

--- includes/class-mailin-rest-api.php
+++ includes/class-mailin-rest-api.php
@@ -10,1 +10,1 @@
-    if ( $request_id == $stored_id ) {
+    if ( ! empty( $stored_id ) && $request_id === $stored_id ) {

Exploit Outline

The exploit targets the `/wp-json/mailin/v1/mailin_disconnect` REST API endpoint, which is used to reset the plugin configuration. An unauthenticated attacker sends a POST request with the header 'Content-Type: application/json' and a payload of '{"id": true}'. Because the plugin uses a loose comparison (==) to validate the provided ID against the stored 'mailin_id' string, the boolean 'true' evaluates as equal to the string, bypassing the check. This results in the deletion of the Brevo API key and the removal of all plugin subscription forms and settings.

Check if your site is affected.

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