CubeWP – All-in-One Dynamic Content Framework <= 1.1.27 - Unauthenticated Information Exposure
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:NTechnical Details
<=1.1.27Source Code
WordPress.org SVN# 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 potentiallyPOSTdepending on implementation. - Authentication: None (Unauthenticated).
- Vulnerable Parameters: Query arguments such as
post_status,post_type,p(Post ID), andname(Slug). - Preconditions: The plugin must be active. Sensitive content (drafts, private posts) must exist in the database.
3. Code Flow (Inferred)
- Registration: The plugin registers REST routes in a class (likely related to posts or frontend queries) using
register_rest_routeduring therest_api_inithook. - Permission Check: The
permission_callbackfor these routes is likely set to__return_trueor fails to verify if the user has theedit_postscapability when requesting non-public statuses. - Data Processing: The controller function associated with the route retrieves user-supplied parameters from the
WP_REST_Requestobject. - Sinking into Query: These parameters are passed into a
WP_Queryinstance. If thepost_statusparameter is not explicitly forced topublish, or if the user-suppliedpost_statusis honored without capability checks,WP_Querywill return the requested sensitive posts. - 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.
- Check for Public Nonce: Navigate to the homepage and check for localized scripts.
- Target Variable (Inferred):
window.cubewp_paramsorwindow.cwp_query_vars. - Target Key:
nonce.
- Target Variable (Inferred):
- Manual Extraction:
- Action:
browser_navigate("http://localhost:8888") - Action:
browser_eval("window.cubewp_params?.nonce || window.cubewp_core?.nonce")
- Action:
- Bypass Check: If the
permission_callbackis__return_true, theX-WP-Nonceheader 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.
- Step 1: Discover Post IDs. Since we are unauthenticated, we can try to brute-force post IDs using the
pparameter. - 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
- URL:
- Step 3: Craft Payload for Drafts.
- URL:
/wp-json/cubewp-posts/v1/query-new?post_status=draft
- URL:
- Request Details:
- Method:
GET - Headers:
Content-Type: application/json - Tool:
http_request
- Method:
6. Test Data Setup
To confirm the vulnerability, we need non-public content:
- 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
- Draft Post:
wp post create --post_type=post --post_title='Confidential Draft' --post_content='This is a sensitive draft content.' --post_status=draft
- 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_contentorpost_excerptfields match the "sensitive" content created in Step 6. - Fail Case: A
403 Forbiddenor a200 OKwith an empty result set (indicating proper filtering).
8. Verification Steps
- Verify via CLI: Confirm the post exists and has the correct status.
wp post get [ID] --field=post_status
- Compare Output: Compare the
post_contentreturned in the HTTP response to the output of:wp post get [ID] --field=post_content
- Unauthenticated Check: Ensure the
http_requesttool is used without any session cookies orAuthorizationheaders to confirm unauthenticated access.
9. Alternative Approaches
If the query parameter names are different (e.g., the plugin uses custom mapping):
- Examine Source: Locate the function handling the REST route. Look for code accessing
$request->get_params()or$_GET. - Try
post_typeoverride: Attempt to queryattachmentor other internal types that might contain sensitive data. - Try
query-newendpoint: If/queryis restricted,/query-newmight use a newer, less-tested code path. - Fuzz status: Try
post_status[]=private&post_status[]=draftto see if array inputs bypass simple string checks.
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
@@ -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.