CVE-2026-3896

Livemesh SiteOrigin Widgets <= 3.9.2 - Missing Authorization to Authenticated (Subscriber+) Stored Cross-Site Scripting

mediumMissing Authorization
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Livemesh SiteOrigin Widgets plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the `lsow_admin_ajax` AJAX action in all versions up to, and including, 3.9.2 due to missing authorization checks and insufficient input sanitization. The AJAX handler verifies a nonce but does not check user capabilities. This makes it possible for authenticated attackers with Subscriber-level access and above to modify plugin settings and inject malicious scripts that execute when administrators access the plugin settings page or when any user visits the frontend.

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<=3.9.2
PublishedMay 26, 2026
Last updatedMay 27, 2026
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-3896 ## 1. Vulnerability Summary **ID:** CVE-2026-3896 **Plugin:** Livemesh SiteOrigin Widgets (slug: `livemesh-siteorigin-widgets`) **Vulnerability Type:** Missing Authorization to Stored Cross-Site Scripting (XSS) **Affected Versions:** <= 3.9.2 **Vulnerable…

Show full research plan

Exploitation Research Plan: CVE-2026-3896

1. Vulnerability Summary

ID: CVE-2026-3896
Plugin: Livemesh SiteOrigin Widgets (slug: livemesh-siteorigin-widgets)
Vulnerability Type: Missing Authorization to Stored Cross-Site Scripting (XSS)
Affected Versions: <= 3.9.2
Vulnerable Endpoint: wp_ajax_lsow_admin_ajax

The vulnerability exists in the AJAX handler lsow_admin_ajax. While the function verifies a WordPress nonce to prevent CSRF, it fails to perform a capability check (e.g., current_user_can('manage_options')). This oversight allows any authenticated user, including those with Subscriber-level permissions, to invoke the function. The function is designed to update plugin settings or widget configurations. Because the input is not properly sanitized before being stored in the database (typically via update_option), an attacker can inject malicious JavaScript. This script will execute in the context of an administrator when they visit the plugin's settings page or on the frontend for all visitors.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: lsow_admin_ajax
  • Authentication: Required (Subscriber or higher)
  • Parameters:
    • action: lsow_admin_ajax
    • lsow_nonce: The nonce value (required)
    • lsow_settings: A serialized or array-based parameter containing plugin settings (inferred).
    • lsow_active_widgets: Potentially an array of active widgets to modify (inferred).
  • Preconditions:
    • Attacker must have a valid Subscriber account.
    • The plugin must be active.

3. Code Flow (Inferred)

  1. Entry Point: A POST request is sent to admin-ajax.php with action=lsow_admin_ajax.
  2. Hook Registration: The plugin registers the action via:
    add_action('wp_ajax_lsow_admin_ajax', 'lsow_admin_ajax_callback'); (or a class-based equivalent).
  3. Nonce Verification: The handler calls check_ajax_referer('lsow-admin-ajax-nonce', 'lsow_nonce') or wp_verify_nonce().
  4. Missing Authorization: The code proceeds directly to logic without calling current_user_can().
  5. Data Processing: The handler takes input from $_POST['lsow_settings'] or similar.
  6. Persistence: The handler calls update_option('lsow_settings', $_POST['lsow_settings']) (or similar) without sanitizing the input for script tags.
  7. Sink: The settings are retrieved and echoed in the admin panel or on the frontend, triggering the XSS.

4. Nonce Acquisition Strategy

The nonce is likely localized for the admin dashboard using wp_localize_script. Since Subscribers have access to the /wp-admin/index.php (the Dashboard) and their Profile page, they can retrieve the nonce from the HTML source.

  1. Identify Localization: Look for wp_localize_script calls in the plugin code (e.g., in includes/admin/class-lsow-admin.php).
  2. JS Variable: The localization key is likely lsow_admin_vars or lsow_settings.
  3. Procedure:
    • Log in as a Subscriber.
    • Navigate to /wp-admin/index.php.
    • Use browser_eval to extract the nonce:
      browser_eval("window.lsow_admin_vars?.nonce") or browser_eval("window.lsow_admin_vars?.lsow_nonce").

5. Exploitation Strategy

  1. Setup: Create a Subscriber user and ensure the plugin is installed.
  2. Nonce Retrieval: Log in as the Subscriber and extract the lsow_nonce from the admin dashboard using browser_eval.
  3. Craft Payload: Create a POST body that updates a settings field with a script.
    • Example payload: <script>alert(document.domain)</script>
  4. Send Request: Use http_request to send the payload to admin-ajax.php.
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Method: POST
    • Content-Type: application/x-www-form-urlencoded
    • Body Parameters:
      action=lsow_admin_ajax
      lsow_nonce=[EXTRACTED_NONCE]
      lsow_settings[some_text_field]=<script>alert('XSS')</script>
      
      (Note: The exact parameter structure lsow_settings[...] must be confirmed by inspecting the plugin's settings form or source code).
  5. Trigger: Log in as an Administrator and navigate to the "Livemesh Widgets" settings page (usually under Settings or its own menu).

6. Test Data Setup

  1. Plugin Installation: wp plugin install livemesh-siteorigin-widgets --version=3.9.2 --activate
  2. User Creation: wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
  3. Identify Settings Structure: Run wp option get lsow_settings to see the current structure and identify which keys to target in the POST request.

7. Expected Results

  • The AJAX request should return a 200 OK or a JSON success response (e.g., {"success":true}).
  • The malicious script should be present in the database.
  • When an administrator views the settings page, the browser should execute the alert('XSS').

8. Verification Steps

  1. Database Check: Use WP-CLI to verify the option was overwritten:
    wp option get lsow_settings
    Check if the output contains the <script> tag.
  2. Frontend/Admin Check: Use browser_navigate to the plugin settings page as an admin and check for the presence of the script in the DOM or an alert trigger.

9. Alternative Approaches

If lsow_settings is not the correct parameter:

  • Widget Activation: Check if the AJAX action allows toggling widgets: lsow_active_widgets[widget_id]=1. If the widget_id is processed without sanitization, it might be the sink.
  • Form Data: If the handler expects a raw serialize() string from a form, the payload may need to be formatted as lsow_settings=key1=val1&key2=<script>....
  • Global Settings: Check if the plugin allows setting a custom "Custom CSS" or "Custom JS" block through this AJAX action, which is a common feature in widget bundles.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Livemesh SiteOrigin Widgets plugin for WordPress (up to version 3.9.2) is vulnerable to Stored Cross-Site Scripting (XSS) due to a lack of capability checks and input sanitization in its `lsow_admin_ajax` handler. This allows authenticated users with Subscriber-level access to modify plugin settings and inject malicious JavaScript that executes in the context of other users, including administrators.

Security Fix

--- a/includes/admin/class-lsow-admin.php
+++ b/includes/admin/class-lsow-admin.php
@@ -1,4 +1,8 @@
 	public function lsow_admin_ajax() {
 		check_ajax_referer('lsow-admin-ajax-nonce', 'lsow_nonce');
 
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( array( 'message' => 'Unauthorized' ) );
+		}
+
 		if ( isset( $_POST['lsow_settings'] ) ) {
-			update_option( 'lsow_settings', $_POST['lsow_settings'] );
+			$settings = map_deep( $_POST['lsow_settings'], 'sanitize_text_field' );
+			update_option( 'lsow_settings', $settings );
 		}
 
 		wp_send_json_success();

Exploit Outline

An attacker with Subscriber-level access logs into the WordPress dashboard and retrieves the 'lsow_nonce' from the localized 'lsow_admin_vars' JavaScript object. They then send a POST request to '/wp-admin/admin-ajax.php' using the action 'lsow_admin_ajax' and the valid nonce. The payload targets the 'lsow_settings' parameter, injecting a malicious script (e.g., '<script>alert(1)</script>') into a setting field. Since the plugin fails to check user capabilities or sanitize the input before saving it via 'update_option', the script is stored in the database and executes when an administrator views the plugin settings page or a visitor views a page where the setting is rendered.

Check if your site is affected.

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