CVE-2026-39701

ShopWP <= 5.2.4 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The ShopWP plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 5.2.4. This makes it possible for unauthenticated attackers to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=5.2.4
PublishedMarch 1, 2026
Last updatedApril 15, 2026
Affected pluginwpshopify
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-39701 (ShopWP Missing Authorization) ## 1. Vulnerability Summary The **ShopWP** (slug: `wpshopify`) plugin for WordPress (versions <= 5.2.4) contains a missing authorization vulnerability. This issue arises because specific AJAX or REST API handlers fail to pe…

Show full research plan

Exploitation Research Plan: CVE-2026-39701 (ShopWP Missing Authorization)

1. Vulnerability Summary

The ShopWP (slug: wpshopify) plugin for WordPress (versions <= 5.2.4) contains a missing authorization vulnerability. This issue arises because specific AJAX or REST API handlers fail to perform capability checks (e.g., current_user_can('manage_options')) before executing sensitive actions. This allows unauthenticated attackers to trigger functionality that should be restricted to administrators, such as syncing product data from Shopify, clearing plugin caches, or modifying plugin settings.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php (for AJAX) or /wp-json/wpshopify/v1/... (for REST).
  • Vulnerable Action: wps_sync_products or wps_clear_cache (inferred; agent must verify via grep).
  • HTTP Parameter: action=wps_sync_products (AJAX) or a POST request to a REST route.
  • Authentication: Unauthenticated (leveraging wp_ajax_nopriv_ hooks or poorly secured REST routes).
  • Preconditions: The plugin must be active. Some actions may require a valid nonce if check_ajax_referer is used without a capability check.

3. Code Flow

  1. Entry Point: The plugin registers a handler via add_action('wp_ajax_nopriv_[ACTION_NAME]', ...) or register_rest_route.
  2. Hook Registration: Look in includes/class-ajax.php or includes/class-rest-api.php for the registration.
  3. Missing Check: The handler function is called. It may call check_ajax_referer() (verifying the nonce) but fails to call current_user_can().
  4. Sink: The function proceeds to execute a sensitive operation, such as:
    • ShopWP\Sync\Product_Sync::start()
    • delete_transient() or wp_cache_flush()
    • update_option('wpshopify_settings', ...)

4. Nonce Acquisition Strategy

If the vulnerable endpoint requires a nonce, follow these steps to retrieve it unauthenticated:

  1. Identify Shortcode: ShopWP typically enqueues its scripts when the [wps_products] shortcode is present.
  2. Create Trigger Page: Use WP-CLI to create a public page containing the shortcode:
    wp post create --post_type=page --post_title="Shop" --post_status=publish --post_content='[wps_products]'
    
  3. Navigate and Extract:
    • Navigate to the newly created page using browser_navigate.
    • Use browser_eval to extract the nonce from the global JavaScript object localized by ShopWP.
    • Target Variable: Look for window.wpshopify_vars or window.shopwp.
    • Example Command: browser_eval("window.wpshopify_vars?.nonce") or browser_eval("window.shopwp?.settings?.nonce").
  4. Verify Action String: In the source code, check the second argument of check_ajax_referer( 'action_string', 'nonce_key' ). The extracted nonce must match this action string.

5. Exploitation Strategy

Once the action and (if necessary) the nonce are identified:

  1. Identify Target Action: Grep for wp_ajax_nopriv_ in the plugin directory to find the specific vulnerable action. Let's assume it is wps_sync_data.
  2. Construct Payload:
    • Method: POST
    • URL: http://[TARGET]/wp-admin/admin-ajax.php
    • Content-Type: application/x-www-form-urlencoded
    • Body: action=wps_sync_data&nonce=[EXTRACTED_NONCE]&other_params=...
  3. Execute Request: Use the http_request tool to send the payload.
  4. Verify Action: Observe the response. A successful trigger of a sync or cache clear usually returns a JSON success message (e.g., {"success": true}).

6. Test Data Setup

  1. Install and activate ShopWP <= 5.2.4.
  2. (Optional) Configure a dummy Shopify API key if the vulnerable function requires a connection to proceed.
  3. Create the nonce-triggering page:
    wp post create --post_type=page --post_status=publish --post_content='[wps_products]'
    

7. Expected Results

  • Response Code: 200 OK.
  • Response Body: JSON containing {"success":true} or evidence of the action (e.g., "Sync started").
  • Side Effects: The site's product data is modified, transients are deleted, or settings are changed.

8. Verification Steps

After sending the exploit request, verify the impact using WP-CLI:

  • If Sync Triggered: Check for newly created or updated posts in the wps_products custom post type:
    wp post list --post_type=wps_products
    
  • If Cache Cleared: Check if specific transients associated with ShopWP have been removed:
    wp transient get [TRANSIENT_NAME]
    
  • If Settings Modified: Check the option value:
    wp option get wpshopify_settings
    

9. Alternative Approaches

  • REST API: If the AJAX endpoint is secure, check the REST API routes. Grep for register_rest_route and look for permission_callback values like __return_true or functions that only return true without checking current_user_can.
  • Different Nonce Actions: If the nonce for the specific action isn't found, check if the plugin uses a generic nonce (action -1) elsewhere that can be repurposed.
  • Subscriber Access: If unauthenticated access is blocked, test if a user with Subscriber role can access the wp_ajax_ (authenticated) version of the hook, as "Missing Authorization" often allows any logged-in user to execute admin actions.
Research Findings
Static analysis — not yet PoC-verified

Summary

The ShopWP plugin for WordPress (versions <= 5.2.4) fails to perform adequate authorization checks on its AJAX and REST API handlers. This allows unauthenticated attackers to trigger sensitive administrative functionality, such as syncing product data from Shopify or clearing internal caches, by exploiting endpoints that lack capability checks.

Exploit Outline

To exploit this vulnerability, an attacker first identifies a public page on the target site that loads the ShopWP frontend scripts (typically a page containing a plugin shortcode like [wps_products]). From the page source, the attacker extracts a valid nonce from the global JavaScript variables (e.g., window.wpshopify_vars.nonce). With this nonce, the attacker sends a POST request to the /wp-admin/admin-ajax.php endpoint with an 'action' parameter corresponding to a vulnerable hook registered via wp_ajax_nopriv_ (such as wps_sync_products). Since the handler for this action lacks a current_user_can() check, the server executes the sensitive function regardless of the attacker's authentication status.

Check if your site is affected.

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