CVE-2026-1295

Buy Now Plus <= 1.0.2 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
1.0.3
Patched in
1d
Time to patch

Description

The Buy Now Plus – Buy Now buttons for Stripe plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'buynowplus' shortcode in all versions up to, and including, 1.0.2 due to insufficient input sanitization and output escaping on shortcode 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: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.2
PublishedJanuary 27, 2026
Last updatedJanuary 28, 2026
Affected pluginbuy-now-plus

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2026-1295 - Authenticated Stored XSS in Buy Now Plus ## 1. Vulnerability Summary The **Buy Now Plus** plugin (versions <= 1.0.2) is vulnerable to **Stored Cross-Site Scripting (XSS)** via the `[buynowplus]` shortcode. The vulnerability exists because the plugin fails to properl…

Show full research plan

Research Plan: CVE-2026-1295 - Authenticated Stored XSS in Buy Now Plus

1. Vulnerability Summary

The Buy Now Plus plugin (versions <= 1.0.2) is vulnerable to Stored Cross-Site Scripting (XSS) via the [buynowplus] shortcode. The vulnerability exists because the plugin fails to properly sanitize or escape attributes passed to the shortcode before rendering them into the page's HTML. Authenticated users with Contributor-level permissions or higher can exploit this by embedding a malicious shortcode into a post or page, which will execute JavaScript in the browser of any user (including administrators) who views the content.

2. Attack Vector Analysis

  • Shortcode: [buynowplus]
  • Vulnerable Parameters: Shortcode attributes (likely button_id, class, or style). Based on typical Stripe button implementations, the button_id or a custom class attribute are primary candidates.
  • Authentication Level: Authenticated (Contributor+). Contributors can create posts and use shortcodes but cannot use unfiltered_html.
  • Preconditions: The plugin must be active, and the attacker must have the ability to save a post containing a shortcode.

3. Code Flow (Inferred)

  1. Registration: The plugin registers the shortcode in the main plugin file or an initialization class:
    add_shortcode( 'buynowplus', 'buynowplus_shortcode_callback' ); (inferred)
  2. Processing: The callback function (e.g., buynowplus_shortcode_callback($atts)) receives the attributes.
  3. Parsing: It likely uses shortcode_atts() to merge user input with defaults.
  4. Sink: The function constructs an HTML string (e.g., an <a> tag or a <div> for the Stripe button) and concatenates the attributes directly into the HTML without using esc_attr() or esc_html().
    • Example Vulnerable Pattern: return '<a class="' . $atts['class'] . '" href="...">Buy Now</a>';
  5. Output: The unsanitized HTML is returned by the shortcode handler and rendered by WordPress.

4. Nonce Acquisition Strategy

While the exploitation of the XSS occurs when a user views the page (no nonce required), the injection of the payload requires saving a post as a Contributor.

  1. Login: Log in as a Contributor user using the browser_navigate and browser_type tools.
  2. Access Post Editor: Navigate to wp-admin/post-new.php.
  3. Extract Nonce: WordPress requires a nonce (_wpnonce) to save a post. This can be extracted from the page source or via browser_eval:
    • browser_eval("document.querySelector('#_wpnonce').value")
  4. Save Post: Use the http_request tool to send a POST request to wp-admin/post.php with the payload in the content parameter.

5. Exploitation Strategy

The goal is to inject a payload into a shortcode attribute that breaks out of the HTML attribute and injects a script.

Step 1: Create the XSS Payload

We will use a standard attribute breakout payload. Since we don't have the exact attribute names, we will test the most likely candidate: button_id.

  • Target Shortcode: [buynowplus button_id='"><img src=x onerror=alert(document.domain)>']
  • Alternative (if class is supported): [buynowplus button_id="123" class='"><script>alert(1)</script>']

Step 2: Inject via Post Creation

Send a request to create a post containing the payload.

  • URL: http://localhost:8080/wp-admin/post.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Body Parameters:
    • action: editpost
    • post_ID: (obtained from the post-new.php page)
    • post_title: XSS Test
    • post_content: [buynowplus button_id='"><img src=x onerror=alert(document.domain)>']
    • _wpnonce: (extracted from editor)
    • save: Publish

Step 3: Trigger the XSS

Navigate to the newly created post as an administrator.

6. Test Data Setup

  1. Plugin Installation: Ensure Buy Now Plus v1.0.2 is installed and active.
  2. User Creation: Create a user with the contributor role.
    • wp user create attacker attacker@example.com --role=contributor --user_pass=password
  3. Authentication: Log in to the attacker account to capture valid session cookies.

7. Expected Results

  • When the post is viewed, the shortcode handler will generate HTML similar to:
    <div id="buynowplus-button-"><img src=x onerror=alert(document.domain)>" ...>
  • The browser will attempt to load the image with the invalid src=x, triggering the onerror event.
  • An alert box displaying the document domain will appear, confirming Stored XSS.

8. Verification Steps

  1. Browser Verification: Use browser_navigate to the post URL and check if the alert is triggered.
  2. HTML Inspection: Use browser_eval to check the DOM for the injected payload:
    • browser_eval("document.body.innerHTML.includes('onerror=alert')")
  3. Database Check: Use wp post get <ID> --field=post_content via WP-CLI to confirm the shortcode is stored exactly as sent.

9. Alternative Approaches

If the button_id attribute is sanitized but others are not, try the following attributes:

  • class: [buynowplus button_id="1" class='"><script>alert(1)</script>']
  • style: [buynowplus button_id="1" style='width:expression(alert(1))'] (for older IE) or [buynowplus button_id="1" style='";background-image:url("javascript:alert(1)")']
  • Attribute Probing: If the attributes are unknown, use grep -r "add_shortcode" . in the plugin directory to find the callback, then grep "atts" <file> to identify all supported attributes.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Buy Now Plus – Buy Now buttons for Stripe plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) via the 'buynowplus' shortcode in versions up to 1.0.2. This is caused by insufficient sanitization and output escaping on shortcode attributes such as 'button_id'. Authenticated attackers with Contributor-level access or higher can exploit this by embedding malicious scripts in posts, which execute when the page is viewed by others.

Exploit Outline

To exploit this vulnerability, an attacker with Contributor-level permissions or higher must authenticate and create a new post or page. They insert the [buynowplus] shortcode containing a payload designed to break out of an HTML attribute, such as [buynowplus button_id='"><img src=x onerror=alert(document.domain)>']. Once the post is saved and viewed by any user (including administrators), the script executes in the context of the viewer's browser session. The exploit works because the shortcode handler directly concatenates the attribute values into the rendered HTML without using escaping functions like esc_attr().

Check if your site is affected.

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