Brevo for WooCommerce <= 4.0.49 - Unauthenticated Stored Cross-Site Scripting
Description
The Brevo for WooCommerce plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the ‘user_connection_id’ parameter in all versions up to, and including, 4.0.49 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=4.0.49What Changed in the Fix
Changes introduced in v4.0.50
Source Code
WordPress.org SVNI cannot fulfill your request to produce a detailed exploitation research plan, specific payloads, or actionable HTTP requests for CVE-2025-14436. I am prohibited from generating content that facilitates the exploitation of vulnerabilities in real-world software or systems. For information on how t…
Show full research plan
I cannot fulfill your request to produce a detailed exploitation research plan, specific payloads, or actionable HTTP requests for CVE-2025-14436. I am prohibited from generating content that facilitates the exploitation of vulnerabilities in real-world software or systems.
For information on how to identify, prevent, and remediate Cross-Site Scripting (XSS) vulnerabilities within the WordPress ecosystem, I recommend consulting the following authoritative resources:
- WordPress Plugin Handbook: Security: The official guide for developers on securing plugins, covering input sanitization and output escaping.
- OWASP Cross-Site Scripting (XSS) Prevention Cheat Sheet: A comprehensive guide on defending against XSS in web applications.
- Common Vulnerabilities and Exposures (CVE) Database: For official descriptions and patch information regarding specific vulnerabilities.
If you are a developer looking to secure your plugin, ensure that all user-supplied data is sanitized using functions like sanitize_text_field() before being stored, and always use context-appropriate escaping functions such as esc_attr(), esc_url(), or esc_html() when rendering data in the browser.
Summary
The Brevo for WooCommerce plugin is vulnerable to unauthenticated Stored Cross-Site Scripting via its callback endpoint. An attacker can send a request containing a malicious payload in the 'user_connection_id' parameter, which is stored unsanitized in the database and later executed when an administrator visits the plugin's settings page.
Vulnerable Code
// woocommerce-sendinblue.php line 170-193 function sendinblue_woocommerce_callback() { $pageNameVar = get_query_var('pagename'); if ($pageNameVar == 'sendinblue-callback') { $result = array('status' => false); $user_connection_id = filter_input(INPUT_POST, 'user_connection_id'); if(empty($user_connection_id)) { $query_string = $_SERVER['QUERY_STRING'] ?? $_SERVER['QUERY_STRING']; if (!empty($query_string)) { parse_str($query_string, $queries); $user_connection_id = $queries['user_connection_id'] ?? $queries['user_connection_id']; } } if(isset($user_connection_id) && !empty($user_connection_id)) { (get_option(SENDINBLUE_WC_USER_CONNECTION_ID, null) !== null) ? update_option(SENDINBLUE_WC_USER_CONNECTION_ID, $user_connection_id) : add_option(SENDINBLUE_WC_USER_CONNECTION_ID, $user_connection_id); header('HTTP/1.1 200 OK', true); $result = array('status' => true); } wp_send_json($result); } } --- // src/managers/admin-manager.php line 59-64 public function adminOptions() { try { $user_connection_id = get_option(SENDINBLUE_WC_USER_CONNECTION_ID, null); if (!empty($user_connection_id)) { $settingsUrl = SendinblueClient::INTEGRATION_URL . $user_connection_id . SendinblueClient::SETTINGS_URL;
Security Fix
@@ -85,7 +85,6 @@ $query_params['consumerSecret'] = $key->consumer_secret; $query_params['language'] = current(explode("_", get_locale())); $query_params['url'] = get_home_url(); - $query_params['callback'] = $query_params['url'] . '/index.php?pagename=sendinblue-callback'; $connectUrl = SendinblueClient::INTEGRATION_URL . SendinblueClient::CONNECT_URL . '?' . http_build_query($query_params); @@ -287,7 +287,6 @@ $settings['shop_version'] = SENDINBLUE_WORDPRESS_SHOP_VERSION; $settings['plugin_version'] = SENDINBLUE_WC_PLUGIN_VERSION; $settings['language'] = current(explode("_", get_locale())); - $settings['callback'] = get_site_url() . '/index.php?pagename=sendinblue-callback'; $key = $this ->api_manager @@ -153,46 +153,12 @@ delete_option(SENDINBLUE_REDIRECT); wp_redirect(add_query_arg('page', 'sendinblue', admin_url('admin.php'))); } - add_filter('rewrite_rules_array', 'sendinblue_woocommerce_rewrites'); - add_action('template_redirect', 'sendinblue_woocommerce_callback'); load_plugin_textdomain( SENDINBLUE_WC_TEXTDOMAIN , false, dirname(plugin_basename(__FILE__)) . '/languages'); $admin_manager = new AdminManager(); $admin_manager->run(); } -function sendinblue_woocommerce_rewrites($wp_rules) -{ - add_rewrite_rule("sendinblue-callback\$", "index.php?pagename=sendinblue-callback"); - - return $wp_rules; -} - -function sendinblue_woocommerce_callback() -{ - $pageNameVar = get_query_var('pagename'); - if ($pageNameVar == 'sendinblue-callback') { - $result = array('status' => false); - $user_connection_id = filter_input(INPUT_POST, 'user_connection_id'); - - if(empty($user_connection_id)) { - $query_string = $_SERVER['QUERY_STRING'] ?? $_SERVER['QUERY_STRING']; - - if (!empty($query_string)) { - parse_str($query_string, $queries); - $user_connection_id = $queries['user_connection_id'] ?? $queries['user_connection_id']; - } - } - - if(isset($user_connection_id) && !empty($user_connection_id)) { - (get_option(SENDINBLUE_WC_USER_CONNECTION_ID, null) !== null) ? update_option(SENDINBLUE_WC_USER_CONNECTION_ID, $user_connection_id) : add_option(SENDINBLUE_WC_USER_CONNECTION_ID, $user_connection_id); - header('HTTP/1.1 200 OK', true); - $result = array('status' => true); - } - wp_send_json($result); - } -} - function sendinblue_woocommerce_activate() { if (!function_exists('is_plugin_active')) { @@ -206,7 +172,6 @@ return; } global $wp_rewrite; - add_filter('rewrite_rules_array', 'sendinblue_woocommerce_rewrites'); $wp_rewrite->flush_rules(); (get_option(SENDINBLUE_REDIRECT, null) !== null) ? update_option(SENDINBLUE_REDIRECT, true) : add_option(SENDINBLUE_REDIRECT, true); }
Exploit Outline
1. An unauthenticated attacker identifies the site's Brevo callback URL, which defaults to /index.php?pagename=sendinblue-callback. 2. The attacker sends an HTTP POST or GET request to this endpoint with a 'user_connection_id' parameter containing a JavaScript payload (e.g., "><script>alert(document.cookie)</script>). 3. The plugin's 'sendinblue_woocommerce_callback' function processes the request and saves the unsanitized 'user_connection_id' value directly into the WordPress options table (option name: 'sendinblue_woocommerce_user_connection_id'). 4. When a site administrator logs in and navigates to the Brevo settings page (wp-admin/admin.php?page=sendinblue), the 'adminOptions' function in 'AdminManager' retrieves the malicious value from the database. 5. The payload is concatenated into a URL string and rendered into the admin dashboard HTML without proper output escaping, leading to script execution in the administrator's browser session.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.