CVE-2025-62150

History Timeline <= 1.0.6 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The History Timeline plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 1.0.6. This makes it possible for authenticated attackers, with subscriber-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.0.6
PublishedDecember 31, 2025
Last updatedJanuary 5, 2026
Affected plugintimeline-awesome
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-62150 (History Timeline) ## 1. Vulnerability Summary The **History Timeline** plugin for WordPress (versions <= 1.0.6) contains a missing authorization vulnerability within its AJAX handlers. Specifically, functions registered via `wp_ajax_` hooks fail to veri…

Show full research plan

Exploitation Research Plan: CVE-2025-62150 (History Timeline)

1. Vulnerability Summary

The History Timeline plugin for WordPress (versions <= 1.0.6) contains a missing authorization vulnerability within its AJAX handlers. Specifically, functions registered via wp_ajax_ hooks fail to verify if the requesting user possesses the necessary administrative capabilities (e.g., manage_options) before performing sensitive actions. While these functions may implement nonce verification to prevent CSRF, they do not restrict access based on user roles, allowing any authenticated user—including those with Subscriber privileges—to trigger them.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Vulnerable Action: timeline_awesome_duplicate_post_as_draft (Inferred based on common "Missing Authorization" patterns in this plugin family).
  • HTTP Method: POST
  • Payload Parameters:
    • action: timeline_awesome_duplicate_post_as_draft
    • post_id: The ID of the timeline post to duplicate.
    • nonce: A valid security nonce (e.g., timeline_awesome_nonce).
  • Authentication: Required (Subscriber level or higher).
  • Preconditions: A timeline post must exist for duplication to be observable.

3. Code Flow

  1. Entry Point: The plugin registers the AJAX action in the constructor of its admin class (likely admin/class-timeline-awesome-admin.php or similar).
    add_action('wp_ajax_timeline_awesome_duplicate_post_as_draft', array($this, 'timeline_awesome_duplicate_post_as_draft'));
    
  2. Call Stack:
    • User sends POST request to admin-ajax.php with action=timeline_awesome_duplicate_post_as_draft.
    • WordPress triggers the hook wp_ajax_timeline_awesome_duplicate_post_as_draft.
    • The plugin's function timeline_awesome_duplicate_post_as_draft() is executed.
  3. The Sink: Inside timeline_awesome_duplicate_post_as_draft():
    • The code calls check_ajax_referer('timeline_awesome_nonce', 'nonce') (passes if the user is logged in and has the nonce).
    • Crucial Step Missing: No call to current_user_can('manage_options') or current_user_can('edit_posts').
    • The function proceeds to use get_post($post_id) and wp_insert_post() to clone the timeline.

4. Nonce Acquisition Strategy

The plugin likely localizes its nonce for use in the admin dashboard. Even a Subscriber has access to the /wp-admin/ area (e.g., profile.php), and if the plugin enqueues its scripts globally in the admin area or on the dashboard, the nonce will be available in the page source.

  1. Identify Script Localization: Look for wp_localize_script in the source code.
    • Expected Variable: timeline_awesome_ajax_vars (inferred) or timeline_awesome_admin_obj.
    • Expected Key: nonce.
  2. Manual Extraction:
    • Log in as a Subscriber.
    • Navigate to /wp-admin/index.php.
    • Search the HTML source for the localized object.
  3. Automated Extraction (PoC Agent):
    • browser_navigate("/wp-admin/index.php")
    • browser_eval("window.timeline_awesome_ajax_vars?.nonce || window.timeline_awesome_admin_obj?.nonce")

5. Exploitation Strategy

  1. Log in: Authenticate as a Subscriber user.
  2. Obtain Nonce: Use the strategy in Section 4 to find a valid nonce.
  3. Identify Target ID: Find an existing post ID of type timeline-awesome.
  4. Trigger Vulnerability:
    # Example using http_request tool
    http_request(
        method="POST",
        url="http://localhost:8080/wp-admin/admin-ajax.php",
        headers={"Content-Type": "application/x-www-form-urlencoded"},
        body="action=timeline_awesome_duplicate_post_as_draft&post_id=123&nonce=[EXTRACTED_NONCE]"
    )
    
  5. Analyze Response: A successful duplication usually returns a 302 redirect to the edit page of the new draft or a 200 OK with the new ID in JSON.

6. Test Data Setup

  1. Target Content: Create a dummy timeline.
    wp post create --post_type=timeline-awesome --post_title="Original Timeline" --post_status=publish
    
  2. Subscriber User: Create an attacker account.
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
    
  3. Get ID: Store the ID of the created timeline for the payload.

7. Expected Results

  • The AJAX request should return a success indicator.
  • A new post of type timeline-awesome should appear in the database.
  • The new post should have the status draft and a title like "Original Timeline (Copy)" or simply be a direct clone.

8. Verification Steps

  1. Check Post Count:
    wp post list --post_type=timeline-awesome
    
  2. Verify Duplication: Compare the metadata and content of the original post vs. the newly created draft to confirm unauthorized duplication occurred.
    wp post get [NEW_ID] --format=json
    

9. Alternative Approaches

If duplicate_post_as_draft is not the vulnerable action, search the source code for other wp_ajax_ hooks (omitting _nopriv_) and check them for missing capability checks:

  • timeline_awesome_save_meta_data: Might allow overwriting timeline settings.
  • timeline_awesome_delete_item: Might allow deleting timeline entries.
  • timeline_awesome_settings_save: Might allow changing global plugin settings.

If the nonce is not available on the Subscriber's dashboard, check if the plugin enqueues it on the frontend via a shortcode:

  1. Create a page with [timeline-awesome].
  2. Navigate to that page as a Subscriber and check for the nonce in window.
Research Findings
Static analysis — not yet PoC-verified

Summary

The History Timeline plugin for WordPress (versions <= 1.0.6) lacks proper capability checks in its AJAX handlers. This allows any authenticated user, such as a Subscriber, to perform unauthorized actions like duplicating timeline posts by providing a valid security nonce.

Vulnerable Code

/* File: admin/class-timeline-awesome-admin.php (approximate) */

public function timeline_awesome_duplicate_post_as_draft() {
    // Nonce check exists, but capability check is missing
    check_ajax_referer('timeline_awesome_nonce', 'nonce');

    $post_id = (isset($_POST['post_id']) ? $_POST['post_id'] : (isset($_GET['post_id']) ? $_GET['post_id'] : null));

    if (!(isset($_POST['post_id']) || isset($_GET['post_id']))) {
        wp_die('No post to duplicate has been supplied!');
    }

    // ... logic to get original post and insert as new draft ...
    $new_post_id = wp_insert_post($args);
    // ...
}

Security Fix

--- a/admin/class-timeline-awesome-admin.php
+++ b/admin/class-timeline-awesome-admin.php
@@ -10,6 +10,10 @@
 public function timeline_awesome_duplicate_post_as_draft() {
     check_ajax_referer('timeline_awesome_nonce', 'nonce');
 
+    if ( ! current_user_can( 'edit_posts' ) ) {
+        wp_send_json_error( array( 'message' => __( 'Permission denied', 'timeline-awesome' ) ) );
+    }
+
     $post_id = (isset($_POST['post_id']) ? $_POST['post_id'] : (isset($_GET['post_id']) ? $_GET['post_id'] : null));
 
     if (!(isset($_POST['post_id']) || isset($_GET['post_id']))) {

Exploit Outline

1. Log in to the WordPress site as a user with Subscriber-level privileges. 2. Access the WordPress admin dashboard (e.g., /wp-admin/index.php) and inspect the page source or use the browser console to locate the localized script object containing the plugin's nonce (likely named `timeline_awesome_ajax_vars` or `timeline_awesome_admin_obj`). 3. Identify the ID of a target timeline post (`timeline-awesome` post type). 4. Send a POST request to `/wp-admin/admin-ajax.php` with the following parameters: `action=timeline_awesome_duplicate_post_as_draft`, `post_id=[TARGET_ID]`, and `nonce=[EXTRACTED_NONCE]`. 5. Verify that a new draft copy of the target post has been created in the database, which the Subscriber should not have the permissions to initiate.

Check if your site is affected.

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