CVE-2026-24634

Ultimate Reviews <= 3.2.16 - Unauthenticated Insecure Direct Object Reference

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

Description

The Ultimate Reviews plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 3.2.16 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<=3.2.16
PublishedJanuary 6, 2026
Last updatedFebruary 6, 2026
Affected pluginultimate-reviews

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-24634 (Ultimate Reviews IDOR) ## 1. Vulnerability Summary The **Ultimate Reviews** plugin (up to version 3.2.16) is vulnerable to an **Insecure Direct Object Reference (IDOR)**. The vulnerability exists in the unauthenticated AJAX handlers responsible for mana…

Show full research plan

Exploitation Research Plan: CVE-2026-24634 (Ultimate Reviews IDOR)

1. Vulnerability Summary

The Ultimate Reviews plugin (up to version 3.2.16) is vulnerable to an Insecure Direct Object Reference (IDOR). The vulnerability exists in the unauthenticated AJAX handlers responsible for managing reviews (e.g., editing or deleting). The plugin fails to verify that the Review_Key provided by the user actually belongs to the Review_ID being modified. This allows an unauthenticated attacker to perform unauthorized actions on any review by simply knowing its numeric ID.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: urp_delete_review or urp_edit_review (inferred from plugin naming conventions).
  • Vulnerable Parameter: Review_ID (The object ID) and Review_Key (The bypassable authorization key).
  • Authentication: None (Unauthenticated via wp_ajax_nopriv_ hooks).
  • Preconditions:
    • A review must exist on the site (at least one).
    • The review ID must be known or enumerable.

3. Code Flow

  1. Entry Point: The plugin registers unauthenticated AJAX hooks in the main class or a dedicated AJAX loader (e.g., Functions/AJAX_Functions.php).
    add_action( 'wp_ajax_urp_delete_review', 'urp_delete_review' );
    add_action( 'wp_ajax_nopriv_urp_delete_review', 'urp_delete_review' );
    
  2. Data Acquisition: The handler urp_delete_review() retrieves Review_ID and Review_Key from the $_POST superglobal.
  3. Flawed Validation: The code checks if Review_Key is present, but fails to query the database to ensure get_post_meta( $Review_ID, 'Review_Key', true ) === $Review_Key.
  4. Sink: The plugin calls wp_delete_post( $Review_ID ) or wp_update_post() based solely on the presence of an arbitrary key.

4. Nonce Acquisition Strategy

The plugin uses a localized nonce for its AJAX operations.

  1. Shortcode Identification: The plugin typically uses the [ultimate-reviews] or [submit-review] shortcode to display/submit reviews.
  2. Page Creation: Create a page containing the submission form to ensure scripts are enqueued.
    wp post create --post_type=page --post_status=publish --post_title="Review Test" --post_content='[submit-review]'
    
  3. Extraction:
    • Use browser_navigate to visit the newly created page.
    • Use browser_eval to extract the nonce from the urp_script_vars or urp_settings object.
    • JS Identifier (Inferred): window.urp_script_vars?.urp_nonce or window.urp_settings?.nonce.

5. Exploitation Strategy

The goal is to delete a review belonging to another user (or any review) without knowing its correct secret key.

Step 1: Identify Target Review ID
Submit a dummy review and capture its ID via the UI or wp post list --post_type=urp_review.

Step 2: Prepare Payload

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Body:
    action=urp_delete_review&Review_ID=[TARGET_ID]&Review_Key=ANY_STRING&urp_nonce=[EXTRACTED_NONCE]
    

Step 3: Execute Request
Use the http_request tool to send the payload.

6. Test Data Setup

  1. Install Plugin: wp plugin install ultimate-reviews --version=3.2.16 --activate
  2. Configure Settings: Ensure "Allow Guest Reviews" is enabled in the plugin settings.
  3. Create Target Review:
    # Manually create a review post via WP-CLI to simulate an existing review
    wp post create --post_type=urp_review --post_status=publish --post_title="Target Review" --post_content="This should not be deleted"
    # Capture the ID of this post
    

7. Expected Results

  • Response: The server returns a success indicator (e.g., {"success":true} or a 1).
  • Effect: The post with the specified Review_ID is moved to the trash or permanently deleted from the wp_posts table.

8. Verification Steps

After sending the HTTP request, verify the deletion via WP-CLI:

# Check if the post still exists
wp post exists [TARGET_ID]
# Alternatively, check post status (should be 'trash' or empty)
wp post get [TARGET_ID] --field=post_status

9. Alternative Approaches

If urp_delete_review is not the vulnerable action, check for:

  1. urp_edit_review: Attempt to change the post_content or rating of a review by providing a target Review_ID and an arbitrary Review_Key.
  2. REST API: Check if the plugin registers routes under wp-json/urp/v1/ and fails to include a permission_callback in the route definition.
  3. Key Omission: Test if the request succeeds when the Review_Key parameter is omitted entirely.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Ultimate Reviews plugin for WordPress (<= 3.2.16) is vulnerable to an unauthenticated Insecure Direct Object Reference (IDOR) via its AJAX handlers. The plugin fails to verify that the user-provided Review_Key actually belongs to the Review_ID being modified or deleted, allowing attackers to manipulate any review post by knowing its ID.

Vulnerable Code

// Inferred from Functions/AJAX_Functions.php and research plan logic

add_action( 'wp_ajax_urp_delete_review', 'urp_delete_review' );
add_action( 'wp_ajax_nopriv_urp_delete_review', 'urp_delete_review' );

function urp_delete_review() {
    $Review_ID = $_POST['Review_ID'];
    $Review_Key = $_POST['Review_Key'];

    // Vulnerability: Only checks if the key is present, not if it matches the object
    if ( ! empty( $Review_ID ) && ! empty( $Review_Key ) ) {
        wp_delete_post( $Review_ID );
        echo json_encode( array( 'success' => true ) );
    }
    wp_die();
}

Security Fix

--- a/Functions/AJAX_Functions.php
+++ b/Functions/AJAX_Functions.php
@@ -10,7 +10,8 @@
     $Review_ID = $_POST['Review_ID'];
     $Review_Key = $_POST['Review_Key'];
-    if ( ! empty( $Review_ID ) && ! empty( $Review_Key ) ) {
+    $Stored_Key = get_post_meta( $Review_ID, 'Review_Key', true );
+    if ( ! empty( $Review_ID ) && ! empty( $Review_Key ) && $Stored_Key === $Review_Key ) {
         wp_delete_post( $Review_ID );
         echo json_encode( array( 'success' => true ) );
     }

Exploit Outline

An unauthenticated attacker can exploit this IDOR vulnerability by targeting the plugin's AJAX endpoints. First, the attacker visits any page containing a review form (e.g., via the [submit-review] shortcode) to extract a valid AJAX nonce from the 'urp_script_vars' or 'urp_settings' JavaScript object. Second, the attacker identifies a target Review_ID (post ID). Finally, the attacker sends a POST request to /wp-admin/admin-ajax.php with the 'urp_delete_review' action, the target Review_ID, the extracted nonce, and an arbitrary string for the Review_Key. Because the plugin does not validate the key against the database record for that ID, the request succeeds and deletes the review.

Check if your site is affected.

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