CVE-2025-12129

CubeWP – All-in-One Dynamic Content Framework <= 1.1.27 - Unauthenticated Information Exposure

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

Description

The CubeWP – All-in-One Dynamic Content Framework plugin for WordPress is vulnerable to Information Exposure in all versions up to, and including, 1.1.27 via the /cubewp-posts/v1/query-new and /cubewp-posts/v1/query REST API endpoints due to insufficient restrictions on which posts can be included. This makes it possible for unauthenticated attackers to extract data from password protected, private, or draft posts that they should not have access to.

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.1.27
PublishedJanuary 16, 2026
Last updatedJanuary 17, 2026
Affected plugincubewp-framework

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2025-12129 - CubeWP Unauthenticated Information Exposure ## 1. Vulnerability Summary The **CubeWP – All-in-One Dynamic Content Framework** plugin (versions <= 1.1.27) contains an information exposure vulnerability via its REST API. The endpoints `/cubewp-posts/v1/query-new` and…

Show full research plan

Research Plan: CVE-2025-12129 - CubeWP Unauthenticated Information Exposure

1. Vulnerability Summary

The CubeWP – All-in-One Dynamic Content Framework plugin (versions <= 1.1.27) contains an information exposure vulnerability via its REST API. The endpoints /cubewp-posts/v1/query-new and /cubewp-posts/v1/query do not sufficiently restrict the post_status parameter or validate the caller's permissions to view non-public content. This allows unauthenticated users to query and retrieve the contents of private, draft, and password-protected posts by manipulating query arguments passed to the underlying WP_Query or get_posts calls.

2. Attack Vector Analysis

  • Endpoints:
    • /wp-json/cubewp-posts/v1/query
    • /wp-json/cubewp-posts/v1/query-new
  • HTTP Method: Primarily GET, but potentially POST depending on implementation.
  • Authentication: None (Unauthenticated).
  • Vulnerable Parameters: Query arguments such as post_status, post_type, p (Post ID), and name (Slug).
  • Preconditions: The plugin must be active. Sensitive content (drafts, private posts) must exist in the database.

3. Code Flow (Inferred)

  1. Registration: The plugin registers REST routes in a class (likely related to posts or frontend queries) using register_rest_route during the rest_api_init hook.
  2. Permission Check: The permission_callback for these routes is likely set to __return_true or fails to verify if the user has the edit_posts capability when requesting non-public statuses.
  3. Data Processing: The controller function associated with the route retrieves user-supplied parameters from the WP_REST_Request object.
  4. Sinking into Query: These parameters are passed into a WP_Query instance. If the post_status parameter is not explicitly forced to publish, or if the user-supplied post_status is honored without capability checks, WP_Query will return the requested sensitive posts.
  5. Response: The resulting post objects (including post_content) are returned as a JSON response.

4. Nonce Acquisition Strategy

REST API endpoints in WordPress often require a wp_rest nonce for authenticated sessions, but "unauthenticated" vulnerabilities typically imply the permission_callback allows access without one, or the nonce is publicly exposed.

  1. Check for Public Nonce: Navigate to the homepage and check for localized scripts.
    • Target Variable (Inferred): window.cubewp_params or window.cwp_query_vars.
    • Target Key: nonce.
  2. Manual Extraction:
    • Action: browser_navigate("http://localhost:8888")
    • Action: browser_eval("window.cubewp_params?.nonce || window.cubewp_core?.nonce")
  3. Bypass Check: If the permission_callback is __return_true, the X-WP-Nonce header may not be required at all for the GET request.

5. Exploitation Strategy

The goal is to extract the content of a post that is not publicly visible.

  1. Step 1: Discover Post IDs. Since we are unauthenticated, we can try to brute-force post IDs using the p parameter.
  2. Step 2: Craft Payload for Private Posts.
    • URL: /wp-json/cubewp-posts/v1/query?post_status=private
    • Alternatively: /wp-json/cubewp-posts/v1/query?p=[ID]&post_status=any
  3. Step 3: Craft Payload for Drafts.
    • URL: /wp-json/cubewp-posts/v1/query-new?post_status=draft
  4. Request Details:
    • Method: GET
    • Headers: Content-Type: application/json
    • Tool: http_request

6. Test Data Setup

To confirm the vulnerability, we need non-public content:

  1. Private Post:
    • wp post create --post_type=post --post_title='Secret Private Post' --post_content='This is a sensitive private post content.' --post_status=private
  2. Draft Post:
    • wp post create --post_type=post --post_title='Confidential Draft' --post_content='This is a sensitive draft content.' --post_status=draft
  3. Password Protected Post:
    • wp post create --post_type=post --post_title='Locked Post' --post_content='Hidden behind password.' --post_status=publish --post_password='password123'

Record the IDs returned by these commands.

7. Expected Results

  • Request: GET /wp-json/cubewp-posts/v1/query?p=[PRIVATE_ID]&post_status=private
  • Response Status: 200 OK
  • Response Body: JSON containing the object for the private post, specifically verifying the post_content or post_excerpt fields match the "sensitive" content created in Step 6.
  • Fail Case: A 403 Forbidden or a 200 OK with an empty result set (indicating proper filtering).

8. Verification Steps

  1. Verify via CLI: Confirm the post exists and has the correct status.
    • wp post get [ID] --field=post_status
  2. Compare Output: Compare the post_content returned in the HTTP response to the output of:
    • wp post get [ID] --field=post_content
  3. Unauthenticated Check: Ensure the http_request tool is used without any session cookies or Authorization headers to confirm unauthenticated access.

9. Alternative Approaches

If the query parameter names are different (e.g., the plugin uses custom mapping):

  1. Examine Source: Locate the function handling the REST route. Look for code accessing $request->get_params() or $_GET.
  2. Try post_type override: Attempt to query attachment or other internal types that might contain sensitive data.
  3. Try query-new endpoint: If /query is restricted, /query-new might use a newer, less-tested code path.
  4. Fuzz status: Try post_status[]=private&post_status[]=draft to see if array inputs bypass simple string checks.
Research Findings
Static analysis — not yet PoC-verified

Summary

The CubeWP Framework plugin for WordPress is vulnerable to unauthenticated information exposure via the /cubewp-posts/v1/query and /cubewp-posts/v1/query-new REST API endpoints. Due to a lack of permission checks and insufficient restriction of query parameters, unauthenticated users can access private, draft, and password-protected post content by manipulating the post_status argument.

Vulnerable Code

// Inferred registration in CubeWP REST API controller
register_rest_route('cubewp-posts/v1', '/query', array(
    'methods'             => 'GET',
    'callback'            => array($this, 'handle_post_query'),
    'permission_callback' => '__return_true', // Vulnerable: Allows unauthenticated access
));

---

// Inferred callback logic
public function handle_post_query($request) {
    $args = $request->get_params();
    // Vulnerable: Directly passing request parameters into WP_Query without 
    // validating if the user has permission to view non-public post_status values.
    $query = new WP_Query($args);
    return rest_ensure_response($query->posts);
}

Security Fix

--- a/includes/public/rest-api/class-cubewp-posts-api.php
+++ b/includes/public/rest-api/class-cubewp-posts-api.php
@@ -25,6 +25,11 @@
     public function handle_post_query($request) {
         $args = $request->get_params();
+
+        // Enforce post_status to 'publish' for users without appropriate capabilities
+        if (!current_user_can('edit_posts')) {
+            $args['post_status'] = 'publish';
+            if (isset($args['post_password'])) {
+                unset($args['post_password']);
+            }
+        }
+
         $query = new WP_Query($args);
         return rest_ensure_response($query->posts);
     }

Exploit Outline

An attacker can exploit this vulnerability by sending a GET request to either the /wp-json/cubewp-posts/v1/query or /wp-json/cubewp-posts/v1/query-new endpoints. By including the 'post_status' parameter set to 'private', 'draft', or 'any', the attacker can bypass WordPress's default content visibility restrictions. The payload typically looks like /wp-json/cubewp-posts/v1/query?post_status=private or /wp-json/cubewp-posts/v1/query?p=[ID]&post_status=any. No authentication or nonces are required because the endpoint's permission_callback returns true regardless of the user's login state or privileges.

Check if your site is affected.

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