Livemesh SiteOrigin Widgets <= 3.9.2 - Missing Authorization to Authenticated (Subscriber+) Stored Cross-Site Scripting
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:NTechnical Details
<=3.9.2# 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_ajaxlsow_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)
- Entry Point: A POST request is sent to
admin-ajax.phpwithaction=lsow_admin_ajax. - Hook Registration: The plugin registers the action via:
add_action('wp_ajax_lsow_admin_ajax', 'lsow_admin_ajax_callback');(or a class-based equivalent). - Nonce Verification: The handler calls
check_ajax_referer('lsow-admin-ajax-nonce', 'lsow_nonce')orwp_verify_nonce(). - Missing Authorization: The code proceeds directly to logic without calling
current_user_can(). - Data Processing: The handler takes input from
$_POST['lsow_settings']or similar. - Persistence: The handler calls
update_option('lsow_settings', $_POST['lsow_settings'])(or similar) without sanitizing the input for script tags. - 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.
- Identify Localization: Look for
wp_localize_scriptcalls in the plugin code (e.g., inincludes/admin/class-lsow-admin.php). - JS Variable: The localization key is likely
lsow_admin_varsorlsow_settings. - Procedure:
- Log in as a Subscriber.
- Navigate to
/wp-admin/index.php. - Use
browser_evalto extract the nonce:browser_eval("window.lsow_admin_vars?.nonce")orbrowser_eval("window.lsow_admin_vars?.lsow_nonce").
5. Exploitation Strategy
- Setup: Create a Subscriber user and ensure the plugin is installed.
- Nonce Retrieval: Log in as the Subscriber and extract the
lsow_noncefrom the admin dashboard usingbrowser_eval. - Craft Payload: Create a POST body that updates a settings field with a script.
- Example payload:
<script>alert(document.domain)</script>
- Example payload:
- Send Request: Use
http_requestto send the payload toadmin-ajax.php.- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method: POST
- Content-Type:
application/x-www-form-urlencoded - Body Parameters:
(Note: The exact parameter structureaction=lsow_admin_ajax lsow_nonce=[EXTRACTED_NONCE] lsow_settings[some_text_field]=<script>alert('XSS')</script>lsow_settings[...]must be confirmed by inspecting the plugin's settings form or source code).
- URL:
- Trigger: Log in as an Administrator and navigate to the "Livemesh Widgets" settings page (usually under
Settingsor its own menu).
6. Test Data Setup
- Plugin Installation:
wp plugin install livemesh-siteorigin-widgets --version=3.9.2 --activate - User Creation:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password123 - Identify Settings Structure: Run
wp option get lsow_settingsto see the current structure and identify which keys to target in thePOSTrequest.
7. Expected Results
- The AJAX request should return a
200 OKor 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
- Database Check: Use WP-CLI to verify the option was overwritten:
wp option get lsow_settings
Check if the output contains the<script>tag. - Frontend/Admin Check: Use
browser_navigateto 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 thewidget_idis 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 aslsow_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.
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
@@ -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.