CVE-2026-2301

Post Duplicator <= 3.0.8 - Missing Authorization to Authenticated (Contributor+) Protected Post Meta Insertion via 'customMetaData' Parameter

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.0.9
Patched in
1d
Time to patch

Description

The Post Duplicator plugin for WordPress is vulnerable to unauthorized arbitrary protected post meta insertion in all versions up to, and including, 3.0.8. This is due to the `duplicate_post()` function in `includes/api.php` using `$wpdb->insert()` directly to the `wp_postmeta` table instead of WordPress's standard `add_post_meta()` function, which would call `is_protected_meta()` to prevent lower-privileged users from setting protected meta keys (those starting with `_`). This makes it possible for authenticated attackers, with Contributor-level access and above, to inject arbitrary protected post meta keys such as `_wp_page_template`, `_wp_attached_file`, and other sensitive meta keys on duplicated posts via the `customMetaData` JSON array parameter in the `/wp-json/post-duplicator/v1/duplicate-post` REST API endpoint.

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<=3.0.8
PublishedFebruary 24, 2026
Last updatedFebruary 25, 2026
Affected pluginpost-duplicator

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2026-2301 - Post Meta Insertion via Post Duplicator ## 1. Vulnerability Summary The **Post Duplicator** plugin (versions <= 3.0.8) contains a missing authorization vulnerability in its REST API handler for duplicating posts. The function `duplicate_post()` in `includes/api.php`…

Show full research plan

Research Plan: CVE-2026-2301 - Post Meta Insertion via Post Duplicator

1. Vulnerability Summary

The Post Duplicator plugin (versions <= 3.0.8) contains a missing authorization vulnerability in its REST API handler for duplicating posts. The function duplicate_post() in includes/api.php processes a customMetaData parameter and inserts the provided data directly into the wp_postmeta table using $wpdb->insert().

Because it bypasses the standard WordPress add_post_meta() or update_post_meta() functions, it also bypasses the internal is_protected_meta() check. This allows users with Contributor level permissions or higher to inject "protected" post meta keys (those prefixed with an underscore, such as _wp_page_template or _wp_attached_file) which are normally restricted to Administrators or Editors.

2. Attack Vector Analysis

  • REST API Endpoint: /wp-json/post-duplicator/v1/duplicate-post (inferred from description).
  • HTTP Method: POST
  • Vulnerable Parameter: customMetaData (JSON array).
  • Authentication: Authenticated (Contributor level and above).
  • Preconditions: At least one post/page must exist that the Contributor has permission to "read" (to duplicate it).

3. Code Flow

  1. Entry Point: The REST API route /wp-json/post-duplicator/v1/duplicate-post is registered (likely in includes/api.php or the main plugin file via the rest_api_init hook).
  2. Callback: The route maps to a callback function, likely named mtphr_post_duplicator_api_duplicate_post or similar, which calls duplicate_post().
  3. Parameter Extraction: The function retrieves the post_id to duplicate and the customMetaData JSON array from the request.
  4. Vulnerable Sink: Inside includes/api.php, the duplicate_post() function iterates through the customMetaData array. Instead of using update_post_meta(), it executes:
    $wpdb->insert(
        $wpdb->postmeta,
        array(
            'post_id'    => $new_post_id,
            'meta_key'   => $meta_key,
            'meta_value' => $meta_value
        )
    );
    
  5. Bypass: Direct SQL insertion avoids the is_protected_meta check inside add_metadata(), allowing the injection of sensitive keys.

4. Nonce Acquisition Strategy

WordPress REST API requires a X-WP-Nonce header for authenticated requests using cookie authentication.

  1. User Setup: Create a Contributor user and log in.
  2. Shortcode/Page Check: Check if the plugin enqueues a specific script. However, since this is a REST API vulnerability, any valid wp_rest nonce will work.
  3. Extraction:
    • Navigate to the WordPress Admin Dashboard (/wp-admin/) as the Contributor.
    • Use browser_eval to extract the REST nonce often stored in the wpApiSettings object or localized by the plugin.
    • Target Variable: window.wpApiSettings.nonce
    • Alternative: If the plugin localizes its own data, look for window.mtphrPostDuplicator?.nonce.

5. Exploitation Strategy

  1. Identify Target Post: Find a post ID (e.g., ID 1) that the Contributor can view.
  2. Prepare Payload:
    • Create a JSON body containing the source post_id and the malicious customMetaData.
    • Goal: Inject _wp_page_template to point to a non-existent or sensitive file, proving the bypass of protected meta.
  3. Execute HTTP Request:
    • Tool: http_request
    • URL: http://localhost:8080/wp-json/post-duplicator/v1/duplicate-post
    • Method: POST
    • Headers:
      • Content-Type: application/json
      • X-WP-Nonce: [EXTRACTED_NONCE]
      • Cookie: [CONTRIBUTOR_COOKIES]
    • Body:
      {
        "post_id": 1,
        "customMetaData": [
          {
            "key": "_wp_page_template",
            "value": "pwned-template.php"
          }
        ]
      }
      
      (Note: The exact structure of the customMetaData objects—e.g., key/value vs meta_key/meta_value—should be verified by checking includes/api.php if source is available, otherwise tested.)

6. Test Data Setup

  1. Create User:
    • wp user create attacker attacker@example.com --role=contributor --user_pass=password
  2. Create Target Post:
    • wp post create --post_type=post --post_title="Victim Post" --post_status=publish (Note the ID, e.g., 1).
  3. Plugin Activation:
    • Ensure post-duplicator is active.

7. Expected Results

  • The REST API should return a 200 OK or 201 Created response.
  • The response body should contain the ID of the newly created (duplicated) post.
  • The duplicated post will have a record in the wp_postmeta table with meta_key = '_wp_page_template' and meta_value = 'pwned-template.php'.

8. Verification Steps

  1. Identify New Post ID: Get the ID from the HTTP response or via CLI:
    • wp post list --post_type=post --orderby=ID --order=DESC --limit=1
  2. Verify Meta Injection:
    • wp post meta get [NEW_ID] _wp_page_template
  3. Success Criteria: If the command returns pwned-template.php, the vulnerability is confirmed, as a Contributor should never be able to set a meta key starting with an underscore.

9. Alternative Approaches

  • Different Meta Keys: If _wp_page_template is filtered elsewhere, try _wp_attached_file or _edit_lock.
  • Parameter Fuzzing: If the JSON body structure [{"key": "...", "value": "..."}] fails, try a flat associative array: {"customMetaData": {"_wp_page_template": "pwned.php"}}.
  • Check for Non-REST Entry Points: If the REST API is disabled, check for admin-ajax.php actions. The description specifically mentions the REST API, but plugins often register both. Search for wp_ajax_mtphr_duplicate_post.

Check if your site is affected.

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