CVE-2026-57419

Stock Locations for WooCommerce <= 3.1.8 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.1.9
Patched in
7d
Time to patch

Description

The Stock Locations for WooCommerce plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 3.1.8. This makes it possible for authenticated attackers, with subscriber-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.1.8
PublishedJuly 8, 2026
Last updatedJuly 14, 2026

What Changed in the Fix

Changes introduced in v3.1.9

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-57419 ## 1. Vulnerability Summary The **Stock Locations for WooCommerce** plugin (versions <= 3.1.8) is vulnerable to **Missing Authorization** in several AJAX handlers. Specifically, functions like `slw_location_status`, `slw_location_assignment`, and `slw_m…

Show full research plan

Exploitation Research Plan - CVE-2026-57419

1. Vulnerability Summary

The Stock Locations for WooCommerce plugin (versions <= 3.1.8) is vulnerable to Missing Authorization in several AJAX handlers. Specifically, functions like slw_location_status, slw_location_assignment, and slw_map_status (located in inc/functions.php) verify a WordPress nonce but fail to perform a capability check (e.g., current_user_can('manage_options')).

Since these handlers are registered via the wp_ajax_ hook, any authenticated user with Subscriber-level access or above can trigger them if they obtain a valid nonce. This allows an attacker to modify term metadata for "Stock Locations," potentially disrupting inventory management or location-based availability.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Actions: slw_location_status, slw_location_assignment, slw_map_status.
  • Authentication: Required (Subscriber level or higher).
  • Vulnerable Parameters:
    • location_id: The ID of the location term (taxonomy: location).
    • status / assignment: 'yes' or 'no' string to toggle settings.
    • slw_nonce_field: The CSRF protection token (nonce).
  • Preconditions:
    • The plugin must be active.
    • At least one "Location" term must exist in the location taxonomy.
    • The attacker must be logged in as a Subscriber.

3. Code Flow

  1. Entry Point: The user sends a POST request to admin-ajax.php with action=slw_location_status.
  2. Hook Registration: In inc/functions.php, the action is registered:
    add_action('wp_ajax_slw_location_status', 'slw_location_status');
    
  3. Execution: The slw_location_status() function is called:
    • It checks if $_POST['status'] is set.
    • It verifies the nonce: wp_verify_nonce( $_POST['slw_nonce_field'], 'slw_nonce' ).
    • Crucially, it skips any current_user_can() check.
  4. Sink: The function calls update_term_meta($location_id, 'slw_location_status', $status), modifying the database based on user input.

4. Nonce Acquisition Strategy

The nonce action string is slw_nonce. This nonce is used across several plugin features. For a Subscriber to exploit this, they must find where the plugin localizes this nonce to the frontend or an accessible admin area.

  1. Identify Triggers: The plugin enqueues frontend scripts when the [slw_product_locations] shortcode is present on a page or when "Location Selection" is enabled in settings.
  2. Setup Trigger Page: Use WP-CLI to create a public page containing the shortcode:
    wp post create --post_type=page --post_status=publish --post_content='[slw_product_locations]' --post_title='Locations'
  3. Extraction:
    • Navigate to the page as a Subscriber.
    • Use browser_eval to search for the nonce. The plugin likely localizes data into a global JS object.
    • Look for variables like slw_vars or search the HTML for slw_nonce_field.
    • Script search: browser_eval("window.slw_vars?.nonce || document.querySelector('input[name=\"slw_nonce_field\"]')?.value").

5. Exploitation Strategy

Step 1: Target Identification

Find a valid location_id using WP-CLI or by inspecting the frontend.
wp term list location --fields=term_id,name

Step 2: Nonce Extraction

Login as a Subscriber and visit a page where the plugin is active (created in Step 6). Use browser_eval to grab the nonce value.

Step 3: Unauthorized Modification

Send a POST request to change the status of a location.

Request Details:

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=slw_location_status&status=no&location_id=[TERM_ID]&slw_nonce_field=[NONCE_VALUE]
    

6. Test Data Setup

  1. Create Location: wp term create location "Warehouse Alpha" --description="Primary storage"
  2. Identify ID: Note the term_id returned.
  3. Create User: wp user create attacker attacker@example.com --role=subscriber --user_pass=password
  4. Create Trigger Page: wp post create --post_type=page --post_status=publish --post_content='[slw_product_locations]'

7. Expected Results

  • The AJAX response should be 1 (success).
  • The location's status in the database will be updated despite the user only being a Subscriber.

8. Verification Steps

Verify the modification using WP-CLI:
wp term meta get [TERM_ID] slw_location_status
The value should be 0 (false/no) if the exploit was successful in setting it to 'no'.

9. Alternative Approaches

If slw_location_status fails, attempt the same process with:

  • Action: slw_location_assignment
    • Sink: Updates slw_location_assignment meta.
  • Action: slw_map_status
    • Sink: Updates slw_map_status meta.

Both follow the same logic in inc/functions.php and use the same slw_nonce.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Stock Locations for WooCommerce plugin fails to perform capability checks in several AJAX handlers, including those for location status, assignment, and map visibility. This allows authenticated attackers with subscriber-level access or higher to modify stock location metadata by exploiting functions that only verify a CSRF nonce but lack authorization controls.

Vulnerable Code

// inc/functions.php @ 3.1.8

add_action('wp_ajax_slw_location_assignment', 'slw_location_assignment');

if (!function_exists('slw_location_assignment')) {
    function slw_location_assignment() {
        if (!empty($_POST) && isset($_POST['assignment'])) {
            if (!isset($_POST['slw_nonce_field']) || !wp_verify_nonce($_POST['slw_nonce_field'], 'slw_nonce')) {
               echo '0';	
            } else {
                $assignment = ($_POST['assignment'] == 'yes');
                $location_id = sanitize_slw_data($_POST['location_id']);

                update_term_meta($location_id, 'slw_location_assignment', $assignment);

				echo '1';

            }
        }

        wp_die();
    }
}

---

// inc/functions.php @ 3.1.8

add_action('wp_ajax_slw_location_status', 'slw_location_status');

if(!function_exists('slw_location_status')){
	function slw_location_status(){
		if(!empty($_POST) && isset($_POST['status'])){
			if (! isset( $_POST['slw_nonce_field'] ) || ! wp_verify_nonce( $_POST['slw_nonce_field'], 'slw_nonce' )	) {	
				echo '0';		
			} else {
				$status = ($_POST['status']=='yes');
				$location_id = sanitize_slw_data($_POST['location_id']);
				update_term_meta($location_id, 'slw_location_status', $status);				
				echo '1';
			}
		}

		wp_die();
	}
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/stock-locations-for-woocommerce/3.1.8/inc/functions.php /home/deploy/wp-safety.org/data/plugin-versions/stock-locations-for-woocommerce/3.1.9/inc/functions.php
--- /home/deploy/wp-safety.org/data/plugin-versions/stock-locations-for-woocommerce/3.1.8/inc/functions.php	2026-05-28 21:31:16.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/stock-locations-for-woocommerce/3.1.9/inc/functions.php	2026-06-03 05:25:30.000000000 +0000
@@ -1,579 +1,519 @@
 <?php if ( ! defined( 'ABSPATH' ) ){ exit; }else{ clearstatcache(); }
 
-if(!function_exists('pre')){
-	function pre($data){
-		if(isset($_GET['debug'])){
-		  pree($data);
-		}
-	}
-}
+function slw_verify_admin_request($key='slw_nonce', $field='slw_nonce_field') {
+
+	check_ajax_referer( $key, $field );
+
+	if ( ! current_user_can( 'manage_options' ) ) {
+		wp_send_json_error(
+			array(
+				'message' => __( 'Unauthorized', 'stock-locations-for-woocommerce' ),
+			),
+			403
+		);
+	}
+}
...
+add_action('wp_ajax_slw_location_assignment', 'slw_location_assignment');
+
+if (!function_exists('slw_location_assignment')) {
+	function slw_location_assignment() {
+		slw_verify_admin_request();
+		
+		if (!empty($_POST) && isset($_POST['assignment'])) {
+			$assignment = ($_POST['assignment'] == 'yes');
+			$location_id = sanitize_slw_data($_POST['location_id']);
+			update_term_meta($location_id, 'slw_location_assignment', $assignment);
+			echo '1';
+		}
+
+		wp_die();
+	}
+}
+add_action('wp_ajax_slw_location_status', 'slw_location_status');
+
+if(!function_exists('slw_location_status')){
+	function slw_location_status(){
+		slw_verify_admin_request();
+		
+		if(!empty($_POST) && isset($_POST['status'])){
+			$status = ($_POST['status']=='yes');
+			$location_id = sanitize_slw_data($_POST['location_id']);
+			update_term_meta($location_id, 'slw_location_status', $status);				
+			echo '1';
+		}
+
+		wp_die();
+	}
+}

Exploit Outline

1. Login as an authenticated user with Subscriber-level privileges. 2. Locate a valid Stock Location term ID (taxonomy: 'location'), which can often be found by inspecting the HTML source of a product page or a page containing the [slw_product_locations] shortcode. 3. Extract the 'slw_nonce' from the frontend. This is commonly localized in the 'slw_vars' global JS object or within a hidden input field named 'slw_nonce_field'. 4. Send a POST request to /wp-admin/admin-ajax.php with the following payload shape: - action: slw_location_status (or slw_location_assignment/slw_map_status) - slw_nonce_field: [extracted_nonce] - location_id: [target_term_id] - status: 'no' (to disable a location) 5. Observe that the server returns '1' and updates the term metadata in the database despite the low privilege level of the attacker.

Check if your site is affected.

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