CVE-2025-63038

Custom Admin Interface <= 7.40 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
7.41
Patched in
9d
Time to patch

Description

The Custom Admin Interface plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 7.40. 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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=7.40
PublishedDecember 31, 2025
Last updatedJanuary 8, 2026

What Changed in the Fix

Changes introduced in v7.41

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2025-63038 ## 1. Vulnerability Summary The **WP Custom Admin Interface** plugin (<= 7.40) contains a missing authorization vulnerability. While the plugin provides extensive customization of the WordPress admin dashboard (menus, toolbars, CSS, etc.), certain admin…

Show full research plan

Exploitation Research Plan - CVE-2025-63038

1. Vulnerability Summary

The WP Custom Admin Interface plugin (<= 7.40) contains a missing authorization vulnerability. While the plugin provides extensive customization of the WordPress admin dashboard (menus, toolbars, CSS, etc.), certain administrative functions — specifically those triggered via init or admin_init hooks — fail to verify that the requesting user has the manage_options capability. This allows an authenticated user with Subscriber-level permissions to perform unauthorized actions, such as resetting or deleting plugin configurations.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php (which triggers admin_init) or any frontend page (which triggers init).
  • Vulnerable Action: The emergency configuration reset feature (wp-custom-admin-interface=delete-menu) and potentially the settings import/save functionality.
  • Authentication: Subscriber-level (authenticated) access is required.
  • Preconditions: The plugin must be active. For a visible impact, custom menu or toolbar settings should be configured.

3. Code Flow

  1. Entry Point: A Subscriber user sends a request to the site.
  2. Hook Registration: The plugin registers wp_custom_admin_interface_check_for_delete_menu (inferred name) on the init hook, or handles settings in wp_custom_admin_interface_settings_init registered on admin_init.
  3. Vulnerable Function: The handler checks for the presence of a specific GET or POST parameter (e.g., $_GET['wp-custom-admin-interface'] == 'delete-menu').
  4. Authorization Failure: The code likely uses is_user_logged_in() or no check at all, rather than current_user_can('manage_options').
  5. Sink: The function calls delete_option('wp_custom_admin_interface_menu') or overwrites options via update_option().

4. Nonce Acquisition Strategy

Based on the plugin's FAQ and standard "emergency reset" patterns, the delete-menu action may not require a nonce to allow administrators to recover a broken site. However, if other settings actions are targeted:

  1. Identify Script Localization: The plugin likely enqueues scripts for its admin pages. Use grep -r "wp_localize_script" . to find the localization variable.
  2. Shortcode Creation: If nonces are only loaded on specific pages, find a shortcode (e.g., [wp_custom_admin_interface_...]) using grep "add_shortcode" ..
  3. Page Setup:
    wp post create --post_type=page --post_status=publish --post_title="Nonce Page" --post_content='[shortcode_found]'
    
  4. Extraction: Navigate to the page as the Subscriber and use browser_eval to extract the nonce:
    • Example: browser_eval("window.wp_custom_admin_interface_data?.nonce") (inferred variable name).

5. Exploitation Strategy

The primary goal is to demonstrate unauthorized configuration deletion.

Step 1: Configuration Reset (Highest Probability)

  1. Action: Perform a GET request to the homepage with the reset parameter.
  2. Request:
    GET /?wp-custom-admin-interface=delete-menu HTTP/1.1
    Host: localhost:8080
    Cookie: [Subscriber Cookies]
    
  3. Expected Response: A redirect (302) to the admin dashboard, and the deletion of the wp_custom_admin_interface_menu option.

Step 2: Unauthorized Settings Modification (AJAX)

If the above fails, target the AJAX settings handler.

  1. Action: Search for wp_ajax_ handlers in the source.
  2. Target Function: wp_ajax_wp_custom_admin_interface_save_settings (inferred).
  3. Payload: Attempt to change the wp_custom_admin_interface_general_settings (inferred) to modify the footer text.
  4. Request:
    POST /wp-admin/admin-ajax.php HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    Cookie: [Subscriber Cookies]
    
    action=wp_custom_admin_interface_save_settings&footer_text=Hacked+by+Subscriber
    

6. Test Data Setup

  1. Install & Activate: Ensure version 7.40 is installed.
  2. Configure Admin Interface:
    # Create a dummy menu configuration to be deleted
    wp option update wp_custom_admin_interface_menu '{"test_menu": "modified"}'
    
  3. Create User:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password
    

7. Expected Results

  • Success (Reset): The wp_custom_admin_interface_menu option is deleted or returned to default values in the database.
  • Success (Modification): The plugin settings are updated despite the user lacking administrative privileges.
  • Response: The server returns a success code (200) or a redirect (302) instead of a forbidden (403).

8. Verification Steps

  1. Check Option Deletion:
    wp option get wp_custom_admin_interface_menu
    # If the exploit worked, this will return 'false' or the default value.
    
  2. Check Settings Modification:
    wp option get wp_custom_admin_interface_general_settings
    # Verify if the injected footer_text or other parameter is present.
    

9. Alternative Approaches

If the delete-menu parameter is not processed via init, check for a direct POST handler in wp_custom_admin_interface_settings_init which is hooked to admin_init. Subscribers can trigger admin_init by visiting /wp-admin/admin-ajax.php. Send a POST request to that endpoint with parameters like wp_custom_admin_interface_export or wp_custom_admin_interface_import to test for other authorization gaps.

Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Custom Admin Interface plugin for WordPress is vulnerable to unauthorized access in versions up to 7.40 due to missing capability checks on administrative functions. Authenticated attackers, including those with subscriber-level permissions, can dismiss admin notices for other users via an AJAX action or potentially trigger an emergency configuration reset that deletes custom menu settings.

Vulnerable Code

// wp-custom-admin-interface.php line 2450
    if( wp_verify_nonce( $_POST['nonce'], 'wp_custom_admin_interface_notice' ) ){

        //get user input
        $userId = intval($_POST['userId']); 
        
        //create transient name - an stands for admin notice    
        $transientName = 'an_dismiss_'.$userId;
        
        //create actual transient
        set_transient($transientName,true,0);
    }

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/wp-custom-admin-interface/7.40/wp-custom-admin-interface.php	2025-11-05 21:42:08.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-custom-admin-interface/7.41/wp-custom-admin-interface.php	2026-01-06 23:32:10.000000000 +0000
@@ -2450,13 +2450,18 @@
     if( wp_verify_nonce( $_POST['nonce'], 'wp_custom_admin_interface_notice' ) ){
 
         //get user input
-        $userId = intval($_POST['userId']); 
+        $userId = intval($_POST['userId']);
         
-        //create transient name - an stands for admin notice    
-        $transientName = 'an_dismiss_'.$userId;
+        $current_user_id = intval(get_current_user_id());
+
+        if($userId == $current_user_id){
+            //create transient name - an stands for admin notice    
+            $transientName = 'an_dismiss_'.$userId;
+            
+            //create actual transient
+            set_transient($transientName,true,0);
+        }
         
-        //create actual transient
-        set_transient($transientName,true,0);
     }

Exploit Outline

1. Authenticate as a Subscriber-level user. 2. Obtain a valid nonce for the 'wp_custom_admin_interface_notice' action by inspecting the localized script data or HTML source of the WordPress admin profile page. 3. Send a POST request to `/wp-admin/admin-ajax.php` with the action (likely `wp_custom_admin_interface_dismiss_notice`), the stolen nonce, and a target `userId` (e.g., an Administrator's ID). This will dismiss admin notices for the target user. 4. Alternatively, attempt to reset the plugin's configuration by sending a GET request to the site root with the parameter `/?wp-custom-admin-interface=delete-menu`. This exploit works if the plugin fails to verify the `manage_options` capability before deleting the `wp_custom_admin_interface_menu` option.

Check if your site is affected.

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