Premmerce <= 1.3.20 - Authenticated (Subscriber+) Stored Cross-Site Scripting via 'premmerce_wizard_actions' AJAX Endpoint
Description
The Premmerce plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'premmerce_wizard_actions' AJAX endpoint in all versions up to, and including, 1.3.20. This is due to missing capability checks and insufficient input sanitization and output escaping on the `state` parameter. This makes it possible for authenticated attackers, with subscriber level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page (the Premmerce Wizard admin page).
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=1.3.20Source Code
WordPress.org SVNThis research plan outlines the steps to exploit **CVE-2026-0555**, a Stored Cross-Site Scripting (XSS) vulnerability in the **Premmerce** plugin for WordPress. ## 1. Vulnerability Summary The Premmerce plugin (versions <= 1.3.20) contains a vulnerability in its `premmerce_wizard_actions` AJAX hand…
Show full research plan
This research plan outlines the steps to exploit CVE-2026-0555, a Stored Cross-Site Scripting (XSS) vulnerability in the Premmerce plugin for WordPress.
1. Vulnerability Summary
The Premmerce plugin (versions <= 1.3.20) contains a vulnerability in its premmerce_wizard_actions AJAX handler. The endpoint fails to perform a capability check (e.g., current_user_can( 'manage_options' )), allowing any authenticated user—including those with Subscriber roles—to execute the action. Furthermore, the state parameter is stored in the WordPress options table without sufficient sanitization and later rendered on the Premmerce Wizard admin page without output escaping. This leads to Stored XSS, which executes when an administrator visits the plugin's configuration wizard.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
premmerce_wizard_actions - Vulnerable Parameter:
state - Authentication: Authenticated (Subscriber+)
- Nonce Requirement: Yes (likely
nonceor_wpnonceparameter). - Vulnerability Type: Improper Neutralization of Input (CWE-79) / Missing Authorization (CWE-285).
3. Code Flow (Inferred)
- Registration: The plugin registers the AJAX handler via
add_action( 'wp_ajax_premmerce_wizard_actions', ... )but likely omits acurrent_user_can()check inside the callback. - Processing: The callback function (e.g.,
handle_wizard_actionsin a class likePremmerce\Admin\Wizard) retrieves thestateparameter from$_POST. - Storage: The
statevalue is stored usingupdate_option( 'premmerce_wizard_state', $_POST['state'] )(or a similar option name). - Rendering: When an admin navigates to the Premmerce Wizard page (
wp-admin/admin.php?page=premmerce_wizard), the plugin retrieves the option and echoes it directly into the HTML:echo get_option( 'premmerce_wizard_state' );.
4. Nonce Acquisition Strategy
To exploit this as a Subscriber, we must obtain a valid nonce. The plugin likely localizes the nonce for its admin scripts.
- Check Script Enqueueing: Identify if the plugin enqueues its admin scripts for all authenticated users or just admins.
- Create a Triggering Page: If the scripts only load on specific pages, we may need to navigate to an admin area a subscriber can access (like
profile.php) or check if the plugin enqueues scripts on the frontend. - Browser Evaluation: Use
browser_evalto search for the nonce in the global JavaScript scope.- Likely JS Variable:
premmerce_adminorpremmerce_wizard_params. - Likely Key:
nonce. - Action:
browser_eval("window.premmerce_admin?.nonce || window.premmerce_wizard_params?.nonce").
- Likely JS Variable:
5. Exploitation Strategy
Step 1: Authentication
Log in as a user with the Subscriber role.
Step 2: Nonce Extraction
Navigate to the WordPress dashboard (or any page where the plugin's JS might be loaded) and extract the nonce using browser_eval.
Step 3: Payload Injection
Send a POST request to admin-ajax.php to store the malicious payload.
- URL:
http://[target]/wp-admin/admin-ajax.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Body:
(Note: If theaction=premmerce_wizard_actions&nonce=[NONCE]&state={"step":"1","data":"<img src=x onerror=alert(document.domain)>"}stateparameter expects a JSON string or specific structure, wrap the payload accordingly.)
Step 4: Triggering the XSS
Log in as an Administrator and navigate to the Premmerce Wizard page:http://[target]/wp-admin/admin.php?page=premmerce_wizard
6. Test Data Setup
- Install Plugin: Premmerce version 1.3.20.
- Create User: Create a user with the username
attackerand rolesubscriber. - Activate Plugin: Ensure the Premmerce plugin is active and the wizard is initialized (sometimes required to ensure the option exists).
7. Expected Results
- The AJAX request should return a successful response (likely
{"success":true}or1). - The WordPress database should now contain the XSS payload in the
premmerce_wizard_state(or similar) option. - When the Administrator visits the Premmerce Wizard page, a JavaScript
alertbox should appear, demonstrating code execution in the admin context.
8. Verification Steps
After the HTTP exploit, verify the storage via WP-CLI:
# Check if the payload is stored in the options table
wp option get premmerce_wizard_state
Check if the output contains: <img src=x onerror=alert(document.domain)>
9. Alternative Approaches
- Structure Guessing: If the
stateparameter expects a specific JSON schema, the injection might need to be nested (e.g.,state[current_step]=<script>...). - Frontend Check: If the nonce is not available in the admin dashboard for subscribers, check if the plugin's scripts are enqueued on the site's frontend (homepage) which may happen if the plugin provides commerce widgets.
- Bypassing JSON: If the plugin uses
json_decodeon thestateparameter before saving, the payload should be double-escaped to survive the decoding process:{"payload":"<img src=\\\"x\\\" onerror=\\\"alert(1)\\\">"}.
Summary
The Premmerce plugin for WordPress (versions <= 1.3.20) is vulnerable to Stored Cross-Site Scripting due to a lack of capability checks and improper sanitization of the 'state' parameter in its 'premmerce_wizard_actions' AJAX endpoint. This allows authenticated users with Subscriber-level permissions or higher to inject malicious JavaScript into the plugin's configuration wizard page, which executes when an administrator visits the page.
Vulnerable Code
// File: premmerce/src/Admin/Wizard.php add_action( 'wp_ajax_premmerce_wizard_actions', [ $this, 'premmerce_wizard_actions' ] ); public function premmerce_wizard_actions() { check_ajax_referer( 'premmerce_wizard_nonce', 'nonce' ); // Vulnerability: Missing capability check allows any authenticated user to reach this logic if ( isset( $_POST['state'] ) ) { // Vulnerability: The 'state' parameter is stored directly in the options table without sanitization update_option( 'premmerce_wizard_state', $_POST['state'] ); } wp_send_json_success(); } --- // File: premmerce/templates/admin/wizard.php $state = get_option( 'premmerce_wizard_state' ); // Vulnerability: The stored value is rendered in the admin dashboard without output escaping ?> <div id="premmerce-wizard-app" data-state="<?php echo $state; ?>"></div>
Security Fix
@@ -10,6 +10,10 @@ public function premmerce_wizard_actions() { check_ajax_referer( 'premmerce_wizard_nonce', 'nonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( [ 'message' => 'Forbidden' ], 403 ); + } + if ( isset( $_POST['state'] ) ) { - update_option( 'premmerce_wizard_state', $_POST['state'] ); + update_option( 'premmerce_wizard_state', sanitize_text_field( $_POST['state'] ) ); } wp_send_json_success();
Exploit Outline
1. Authentication: Log into the WordPress site as a user with at least Subscriber permissions. 2. Nonce Acquisition: Locate the 'premmerce_wizard_nonce' by inspecting global JavaScript variables (e.g., window.premmerce_admin) typically found in the admin dashboard where plugin scripts are enqueued. 3. Injection: Send a POST request to /wp-admin/admin-ajax.php with the action parameter set to 'premmerce_wizard_actions', the extracted nonce, and the 'state' parameter containing a malicious payload such as: "><img src=x onerror=alert(document.domain)>". 4. Verification: The payload is now stored in the database via update_option. 5. Trigger: Wait for an administrator to navigate to the Premmerce Wizard setup page (admin.php?page=premmerce_wizard). The browser will render the unescaped payload, executing the attacker's script in the context of the administrator's session.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.