CVE-2026-32373

SMS Alert Order Notifications <= 3.9.0 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.9.1
Patched in
57d
Time to patch

Description

The SMS Alert Order Notifications plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 3.9.0. 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<=3.9.0
PublishedFebruary 18, 2026
Last updatedApril 15, 2026
Affected pluginsms-alert

What Changed in the Fix

Changes introduced in v3.9.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-32373 ## 1. Vulnerability Summary The **SMS Alert Order Notifications** plugin (versions <= 3.9.0) contains a missing authorization vulnerability. The plugin registers several administrative actions via an `admin_init` or `init` hook that processes a custom `…

Show full research plan

Exploitation Research Plan - CVE-2026-32373

1. Vulnerability Summary

The SMS Alert Order Notifications plugin (versions <= 3.9.0) contains a missing authorization vulnerability. The plugin registers several administrative actions via an admin_init or init hook that processes a custom option parameter. These actions lack proper capability checks (e.g., current_user_can( 'manage_options' )), allowing any authenticated user with access to the WordPress dashboard—specifically users with the Subscriber role—to perform sensitive plugin operations such as syncing groups, creating groups, or logging the administrator out of the SMS Alert service.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin.php or any admin page that triggers admin_init (e.g., /wp-admin/profile.php).
  • Hook: Likely admin_init or init within the smsalert_WC_Order_SMS class.
  • Query Parameters:
    • option: Identifies the action to perform (e.g., smsalert-group-sync, smsalert-woocommerce-logout).
    • nonce: A WordPress nonce (required for some actions).
    • Other action-specific parameters like grp_name.
  • Authentication: Required (Subscriber level or higher).
  • Preconditions: The plugin must be active. For the "logout" action, the administrator should ideally be "logged in" to the SMS Alert service (API credentials configured).

3. Code Flow

  1. The plugin initializes and hooks a routing function to admin_init or init.
  2. The routing function checks for the presence of the option parameter in $_GET.
  3. Based on the value of option, the code calls specific handler functions:
    • smsalert-group-sync -> Likely calls a method to sync contacts.
    • smsalert-woocommerce-logout -> Calls a method to clear plugin credentials.
  4. The handlers may check a nonce using wp_verify_nonce() or check_ajax_referer(), but they fail to check for administrative capabilities via current_user_can().
  5. Since a Subscriber can access /wp-admin/profile.php, they trigger the admin_init hook, allowing them to execute these functions if they possess a valid nonce.

4. Nonce Acquisition Strategy

The plugin localizes a nonce for the smsalert object in the WordPress admin dashboard.

  1. Identify Localization: In js/admin.js, the functions verifyUser, logout, and create_group all use smsalert.nonce.
  2. Access for Subscriber: Subscribers can access the WordPress admin via /wp-admin/profile.php.
  3. Extraction:
    • Use browser_navigate to go to /wp-admin/profile.php as a Subscriber.
    • Use browser_eval to extract the nonce: window.smsalert?.nonce.
  4. Special Case: The doSASyncNow function (handling smsalert-group-sync) in js/admin.js (line 45) appears to send a request without a nonce:
    data:"option=smsalert-group-sync&grp_name="+t
    If this action is verified on the backend without a nonce check, it is a zero-precondition authenticated attack.

5. Exploitation Strategy

Target Action: smsalert-woocommerce-logout

This action is chosen because it demonstrates a disruptive "unauthorized action" by clearing the site's SMS service configuration.

Step-by-Step:

  1. Get Nonce:
    • Log in as a Subscriber.
    • Navigate to /wp-admin/profile.php.
    • Extract window.smsalert.nonce.
  2. Execute Request:
    • Send a GET request to /wp-admin/admin.php?option=smsalert-woocommerce-logout&nonce=[NONCE].
    • Use the http_request tool.
  3. Analyze Response:
    • A successful logout typically returns a JSON response: {"status":"success"} or similar, and the plugin's credentials will be cleared from the database.

Target Action: smsalert-group-sync (Alternative)

If logout fails due to nonce issues, try the sync action which may lack a nonce entirely.

  • Request: GET /wp-admin/admin.php?option=smsalert-group-sync&grp_name=attack_group

6. Test Data Setup

  1. Install and activate WooCommerce.
  2. Install and activate SMS Alert Order Notifications v3.9.0.
  3. Configure Plugin: Go to SMS Alert settings and enter dummy API credentials (e.g., username testuser, password testpass) so that a "logout" action has a visible effect.
  4. Create Attacker: Create a user with the subscriber role.

7. Expected Results

  • Logout Action: The HTTP response should be a JSON success message. Post-exploit, the plugin settings for smsalert_gateway or similar should be empty or reset.
  • Sync Action: The HTTP response should indicate a success status or a "sync completed" message in JSON.

8. Verification Steps

  1. Check Plugin Options via WP-CLI:
    wp option get smsalert_gateway
    
    Compare the value before and after the smsalert-woocommerce-logout request. If the credentials/sender ID are removed, the exploit is successful.
  2. Check for Missing Capabilities:
    Verify that the Subscriber can indeed trigger the response by checking the HTTP status code (should be 200, not 403).

9. Alternative Approaches

If /wp-admin/admin.php redirects the Subscriber (due to wp-admin access restrictions often implemented by security plugins), use /wp-admin/admin-post.php or /wp-admin/profile.php as the base URL, as both will still trigger the admin_init hook where the option parameter is likely processed:

  • GET /wp-admin/profile.php?option=smsalert-woocommerce-logout&nonce=[NONCE]

If the option is processed via init rather than admin_init, the exploit can be launched from the frontend:

  • GET /?option=smsalert-woocommerce-logout&nonce=[NONCE]
Research Findings
Static analysis — not yet PoC-verified

Summary

The SMS Alert Order Notifications plugin for WordPress is vulnerable to unauthorized access and actions because several functions lack proper capability checks and nonce validation. Authenticated attackers with subscriber-level permissions or higher can exploit these flaws to send custom SMS messages, sync contact groups, or disconnect the plugin from the SMS Alert service.

Vulnerable Code

// handler/forms/woocommerce/wc-checkout.php line 1488
public function sendCustomSms( $data )
{
    $order_id = empty($_POST['order_id']) ? '' : sanitize_text_field(wp_unslash($_POST['order_id']));
    $sms_body = empty($_POST['sms_body']) ? '' : sanitize_textarea_field(wp_unslash($_POST['sms_body']));

    $buyer_sms_data             = array();
    if ( version_compare( WC_VERSION, '7.1', '<' ) ) {
      $buyer_sms_data['number']   = get_post_meta( $order_id, '_billing_phone', true );
    } else {
      $order       = wc_get_order($order_id);
      $buyer_sms_data['number']   = !empty($order->get_billing_phone())?$order->get_billing_phone():$order->get_shipping_phone();
    }
    
    $buyer_sms_data['sms_body'] = $sms_body;
    $buyer_sms_data             = apply_filters('sa_wc_order_sms_customer_before_send', $buyer_sms_data, $order_id);
    wp_send_json(SmsAlertcURLOTP::sendsms($buyer_sms_data));
    exit();
}

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/sms-alert/3.9.0/handler/forms/woocommerce/wc-checkout.php	2026-01-03 05:02:44.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/sms-alert/3.9.1/handler/forms/woocommerce/wc-checkout.php	2026-02-11 02:18:12.000000000 +0000
@@ -1488,21 +1488,24 @@
      */
     public function sendCustomSms( $data )
     {
-		$order_id = empty($_POST['order_id']) ? '' : sanitize_text_field(wp_unslash($_POST['order_id']));
-		$sms_body = empty($_POST['sms_body']) ? '' : sanitize_textarea_field(wp_unslash($_POST['sms_body']));
+		if(current_user_can('manage_options') && wp_verify_nonce( $_POST['sa_custom_nonce'], 'sacustom_wp_nonce' ))
+        {
+			$order_id = empty($_POST['order_id']) ? '' : sanitize_text_field(wp_unslash($_POST['order_id']));
+			$sms_body = empty($_POST['sms_body']) ? '' : sanitize_textarea_field(wp_unslash($_POST['sms_body']));
 
-		$buyer_sms_data             = array();
-		if ( version_compare( WC_VERSION, '7.1', '<' ) ) {
-		  $buyer_sms_data['number']   = get_post_meta( $order_id, '_billing_phone', true );
-		} else {
-		  $order       = wc_get_order($order_id);
-		  $buyer_sms_data['number']   = !empty($order->get_billing_phone())?$order->get_billing_phone():$order->get_shipping_phone();
+			$buyer_sms_data             = array();
+			if ( version_compare( WC_VERSION, '7.1', '<' ) ) {
+			  $buyer_sms_data['number']   = get_post_meta( $order_id, '_billing_phone', true );
+			} else {
+			  $order       = wc_get_order($order_id);
+			  $buyer_sms_data['number']   = !empty($order->get_billing_phone())?$order->get_billing_phone():$order->get_shipping_phone();
+			}
+			
+			$buyer_sms_data['sms_body'] = $sms_body;
+			$buyer_sms_data             = apply_filters('sa_wc_order_sms_customer_before_send', $buyer_sms_data, $order_id);
+			wp_send_json(SmsAlertcURLOTP::sendsms($buyer_sms_data));
+			exit();
 		}
-		
-		$buyer_sms_data['sms_body'] = $sms_body;
-		$buyer_sms_data             = apply_filters('sa_wc_order_sms_customer_before_send', $buyer_sms_data, $order_id);
-		wp_send_json(SmsAlertcURLOTP::sendsms($buyer_sms_data));
-		exit();
     }

Exploit Outline

The exploit involves an authenticated attacker with Subscriber-level access or higher. 1. Log in to the WordPress dashboard as a Subscriber. 2. For actions like `logout` or `group-sync`, obtain the `smsalert.nonce` by inspecting the global JavaScript object on an admin page (e.g., `/wp-admin/profile.php`). Note that some actions may not require a nonce in vulnerable versions. 3. To trigger the sensitive operations, send a request to a dashboard endpoint (like `/wp-admin/admin.php` or via AJAX). For sending custom SMS, use a POST request to the AJAX endpoint with `action=wc_sms_alert_sms_send_order_sms`, an existing `order_id`, and the desired `sms_body`. 4. For service disruption, send a GET request to `/wp-admin/admin.php?option=smsalert-woocommerce-logout&nonce=[NONCE]`, which clears the plugin's API credentials without verifying the user has administrative privileges.

Check if your site is affected.

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