CVE-2026-0555

Premmerce <= 1.3.20 - Authenticated (Subscriber+) Stored Cross-Site Scripting via 'premmerce_wizard_actions' AJAX Endpoint

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

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: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.3.20
PublishedFebruary 6, 2026
Last updatedFebruary 25, 2026
Affected pluginpremmerce

Source Code

WordPress.org SVN
Research Plan
Unverified

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 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 nonce or _wpnonce parameter).
  • Vulnerability Type: Improper Neutralization of Input (CWE-79) / Missing Authorization (CWE-285).

3. Code Flow (Inferred)

  1. Registration: The plugin registers the AJAX handler via add_action( 'wp_ajax_premmerce_wizard_actions', ... ) but likely omits a current_user_can() check inside the callback.
  2. Processing: The callback function (e.g., handle_wizard_actions in a class like Premmerce\Admin\Wizard) retrieves the state parameter from $_POST.
  3. Storage: The state value is stored using update_option( 'premmerce_wizard_state', $_POST['state'] ) (or a similar option name).
  4. 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.

  1. Check Script Enqueueing: Identify if the plugin enqueues its admin scripts for all authenticated users or just admins.
  2. 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.
  3. Browser Evaluation: Use browser_eval to search for the nonce in the global JavaScript scope.
    • Likely JS Variable: premmerce_admin or premmerce_wizard_params.
    • Likely Key: nonce.
    • Action: browser_eval("window.premmerce_admin?.nonce || window.premmerce_wizard_params?.nonce").

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:
    action=premmerce_wizard_actions&nonce=[NONCE]&state={"step":"1","data":"<img src=x onerror=alert(document.domain)>"}
    
    (Note: If the state parameter 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

  1. Install Plugin: Premmerce version 1.3.20.
  2. Create User: Create a user with the username attacker and role subscriber.
  3. 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} or 1).
  • 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 alert box 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 state parameter 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_decode on the state parameter before saving, the payload should be double-escaped to survive the decoding process: {"payload":"<img src=\\\"x\\\" onerror=\\\"alert(1)\\\">"}.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/premmerce/src/Admin/Wizard.php
+++ b/premmerce/src/Admin/Wizard.php
@@ -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.