Sentence To SEO (keywords, description and tags) <= 1.0 - Cross-Site Request Forgery to Stored Cross-Site Scripting via Settings Page Parameters
Description
The Sentence To SEO (keywords, description and tags) plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to, and including, 1.0. This is due to missing or incorrect nonce validation on the create_admin_page() function. This makes it possible for unauthenticated attackers to inject malicious web scripts and update plugin settings via a forged request granted they can trick a site administrator into performing an action such as clicking on a link.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:NTechnical Details
This exploitation research plan outlines the technical steps required to demonstrate the Cross-Site Request Forgery (CSRF) to Stored Cross-Site Scripting (XSS) vulnerability in the **Sentence To SEO** plugin (<= 1.0). --- # Exploitation Research Plan: CVE-2026-6391 ## 1. Vulnerability Summary The…
Show full research plan
This exploitation research plan outlines the technical steps required to demonstrate the Cross-Site Request Forgery (CSRF) to Stored Cross-Site Scripting (XSS) vulnerability in the Sentence To SEO plugin (<= 1.0).
Exploitation Research Plan: CVE-2026-6391
1. Vulnerability Summary
The Sentence To SEO plugin fails to implement proper Cross-Site Request Forgery (CSRF) protections (nonces) in its administrative settings management logic. Specifically, the create_admin_page() function handles the processing and saving of plugin settings without verifying a security nonce. This allows an attacker to forge a request on behalf of an authenticated administrator. Because the settings parameters (such as keywords and descriptions) are subsequently stored and rendered in the WordPress admin dashboard without sufficient output sanitization or escaping, the CSRF leads to Stored Cross-Site Scripting (XSS).
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin.php?page=sentence-to-seo(Inferred slug based on plugin name). - Vulnerable Action: The POST request processed by the
create_admin_page()function (or the logic it calls to handle$_POSTdata). - Authentication Level: Requires a victim with
administratorprivileges to be logged in and interact with a malicious link or page. - Payload Parameters:
sentence_to_seo_keywords(Inferred)sentence_to_seo_description(Inferred)sentence_to_seo_tags(Inferred)
3. Code Flow (Inferred)
- Registration: The plugin registers an admin page via
add_menu_page()oradd_options_page()in theadmin_menuhook, pointing tocreate_admin_page()as the callback. - Processing: Inside
create_admin_page(), the code checks if a form has been submitted:if ( isset( $_POST['submit'] ) ). - Missing Check: The function lacks a call to
check_admin_referer()orwp_verify_nonce(). - Storage: User-provided input from
$_POSTis passed directly toupdate_option(). - Sink: When the settings page is reloaded, the stored options are retrieved via
get_option()and echoed into the HTML input fields or the page body withoutesc_attr()oresc_html().
4. Nonce Acquisition Strategy
Based on the vulnerability description ("missing or incorrect nonce validation"), there are two possibilities:
Case A: Nonce check is entirely missing
If check_admin_referer is absent, no nonce is required. This is the most likely scenario for this vulnerability type.
Case B: Nonce is present but validation is flawed
If the plugin uses a nonce but exposes it on the page:
- Use
browser_navigateto go to/wp-admin/admin.php?page=sentence-to-seo. - Use
browser_evalto extract the nonce from the hidden form field:browser_eval("document.querySelector('input[name=\"_wpnonce\"]')?.value")
Note: Since the goal is CSRF, the attacker cannot typically read this nonce from a different origin. However, if the plugin enqueues a script on the frontend that includes the nonce via wp_localize_script, it could be retrieved there.
5. Exploitation Strategy
The goal is to update the plugin settings with a malicious script payload using a forged POST request.
Step 1: Identify Parameters
First, the researcher must identify the exact keys used in the update_option calls.
- Action: Inspect the HTML form on the settings page or use
wp-clito list options:wp option list --search="sentence_to_seo*".
Step 2: Construct the CSRF Exploit
A malicious HTML page will be served to the administrator.
Payload Request (Internal to PoC Agent):
POST /wp-admin/admin.php?page=sentence-to-seo HTTP/1.1
Host: [TARGET_HOST]
Content-Type: application/x-www-form-urlencoded
Cookie: [ADMIN_COOKIES]
sentence_to_seo_keywords=%3Cscript%3Ealert%28document.domain%29%3C%2Fscript%3E&submit=Save+Changes
Step 3: Trigger Execution
- The agent uses
http_requestto simulate the POST request (representing the result of the CSRF). - The agent then navigates to the plugin settings page to trigger the Stored XSS.
6. Test Data Setup
- Plugin Installation: Ensure
sentence-to-seoversion 1.0 is active. - User Setup: Create a standard administrator user.
- Default State: Confirm the settings are currently empty or set to benign values.
7. Expected Results
- The
POSTrequest should return a302 Redirectback to the settings page or a200 OKindicating success, despite the absence of a nonce. - Upon navigating to the settings page, the browser should execute the JavaScript in the payload (e.g., triggering an alert or a
console.log).
8. Verification Steps
After performing the HTTP request, verify the injection using wp-cli:
# Check if the option in the database contains the payload
wp option get sentence_to_seo_keywords
Expected output: <script>alert(document.domain)</script>
9. Alternative Approaches
If the settings are not output on the admin page, they might be output on the frontend in the <head> section as meta tags.
- Frontend Check: Use
http_requestto GET the site's homepage. - Verification: Search the response body for:
<meta name="keywords" content="<script>alert(document.domain)</script>"> - Bypass Attempt: If
check_admin_refererexists, check if it only validates the nonce if the parameter is present (Bypass 3 in the Knowledge Base). Attempt the request by omitting the nonce parameter entirely.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.