CVE-2026-25314

TOP Table Of Contents <= 1.3.31 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
1.4.0
Patched in
100d
Time to patch

Description

The TOP Table Of Contents plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 1.3.31. 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<=1.3.31
PublishedJanuary 25, 2026
Last updatedMay 4, 2026
Affected plugintop-table-of-contents

What Changed in the Fix

Changes introduced in v1.4.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-25314 (TOP Table Of Contents) ## 1. Vulnerability Summary The **TOP Table Of Contents** plugin (versions <= 1.3.31) contains a missing authorization vulnerability in its AJAX implementation. Specifically, the function `Boomdevs_Toc_custom_plugin_install` is re…

Show full research plan

Exploitation Research Plan: CVE-2026-25314 (TOP Table Of Contents)

1. Vulnerability Summary

The TOP Table Of Contents plugin (versions <= 1.3.31) contains a missing authorization vulnerability in its AJAX implementation. Specifically, the function Boomdevs_Toc_custom_plugin_install is registered for both authenticated (wp_ajax_) and unauthenticated (wp_ajax_nopriv_) users but fails to perform any capability checks (e.g., current_user_can( 'install_plugins' )).

While the function is hardcoded to install a specific plugin (ai-image-alt-text-generator-for-wp), the lack of authorization allows any user (Subscriber and potentially unauthenticated users if they can obtain a nonce) to trigger the installation and activation of a plugin on the site, which is a significant unauthorized action.

2. Attack Vector Analysis

  • AJAX Action: Boomdevs_Toc_custom_plugin_install
  • Endpoint: /wp-admin/admin-ajax.php
  • Authentication: Required (Subscriber-level access is sufficient to obtain the nonce).
  • Nonce Requirement: Yes. The function uses check_ajax_referer('Boomdevs_Toc_custom_plugin_install_nonce', 'security').
  • Vulnerable Sink: The function calls $upgrader->install(...) and activate_plugin(...) without checking user permissions.

3. Code Flow

  1. Registration: In admin/class-boomdevs-toc-admin.php, the AJAX hooks are registered:
    add_action('wp_ajax_Boomdevs_Toc_custom_plugin_install', [$this, 'Boomdevs_Toc_custom_plugin_install']);
    add_action('wp_ajax_nopriv_Boomdevs_Toc_custom_plugin_install', [$this, 'Boomdevs_Toc_custom_plugin_install']);
    
  2. Nonce Generation: In the same file, enqueue_scripts() localizes the nonce for the admin area:
    wp_localize_script($this->plugin_name, 'Boomdevs_Toc_custom_plugin_install_obj', array(
        'ajax_url'  => admin_url('admin-ajax.php'),
        'security' => wp_create_nonce('Boomdevs_Toc_custom_plugin_install_nonce')
        )
    );
    
  3. Vulnerable Function: The Boomdevs_Toc_custom_plugin_install function in admin/class-boomdevs-toc-admin.php executes:
    • Validates the nonce via security parameter.
    • Hardcodes $plugin_slug = 'ai-image-alt-text-generator-for-wp'.
    • Uses Plugin_Upgrader to download and install from WordPress.org.
    • Calls activate_plugin().

4. Nonce Acquisition Strategy

The nonce Boomdevs_Toc_custom_plugin_install_nonce is localized in the admin dashboard. Since even a Subscriber user can access /wp-admin/profile.php, they can retrieve this nonce.

  1. Login: Authenticate as a Subscriber-level user.
  2. Navigate: Go to /wp-admin/.
  3. Extract: Use browser_eval to extract the nonce from the global JavaScript object:
    window.Boomdevs_Toc_custom_plugin_install_obj?.security
    

5. Exploitation Strategy

  1. Identify Target: Ensure the target WordPress instance is running TOP Table Of Contents <= 1.3.31.
  2. Obtain Nonce:
    • Perform a login as a Subscriber.
    • Use browser_navigate to /wp-admin/.
    • Use browser_eval to capture the security token from Boomdevs_Toc_custom_plugin_install_obj.
  3. Trigger Installation: Send an HTTP POST request to the AJAX endpoint.
    • URL: http://[target]/wp-admin/admin-ajax.php
    • Method: POST
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body: action=Boomdevs_Toc_custom_plugin_install&security=[NONCE]
  4. Capture Response: A successful response should be a JSON object: {"success":true,"data":{"message":"Plugin installed and activated successfully."}}.

6. Test Data Setup

  1. Plugin: Install "TOP Table Of Contents" version 1.3.31.
  2. User: Create a user with the subscriber role (e.g., username: attacker, password: password123).
  3. Environment Check: Ensure the plugin ai-image-alt-text-generator-for-wp is NOT currently installed.

7. Expected Results

  • The AJAX request returns a success message.
  • The ai-image-alt-text-generator-for-wp plugin is downloaded into wp-content/plugins/.
  • The ai-image-alt-text-generator-for-wp plugin appears as "Active" in the WordPress site.

8. Verification Steps

  1. WP-CLI Plugin Check:
    wp plugin list --status=active
    
    Confirm ai-image-alt-text-generator-for-wp is in the list.
  2. WP-CLI File Check:
    ls -d wp-content/plugins/ai-image-alt-text-generator-for-wp/
    
    Confirm the directory exists.

9. Alternative Approaches

If the Subscriber user cannot access /wp-admin/ due to a site-wide lockdown (though standard WP allows profile access):

  1. Shortcode Extraction: Check if the plugin enqueues the same script on the frontend when a TOC is present. Create a page with [boomdevs-toc] and try to extract Boomdevs_Toc_custom_plugin_install_obj from the frontend.
  2. Unauthenticated Check: Since wp_ajax_nopriv is registered, if an unauthenticated user can find any page where enqueue_scripts is called (or if the nonce is leaked via another vulnerability), they could trigger the install without logging in. Check the public/class-boomdevs-toc-public.php (not fully provided, but inferred) to see if scripts are shared.
Research Findings
Static analysis — not yet PoC-verified

Summary

The TOP Table Of Contents plugin for WordPress is vulnerable to unauthorized access because its AJAX handlers for plugin installation and layout imports lack capability checks. This allows authenticated users with subscriber-level permissions to trigger the installation and activation of a specific third-party plugin or modify site layout configurations.

Vulnerable Code

// admin/class-boomdevs-toc-admin.php lines 54-55
add_action('wp_ajax_Boomdevs_Toc_custom_plugin_install', [$this, 'Boomdevs_Toc_custom_plugin_install']);
add_action( 'wp_ajax_nopriv_Boomdevs_Toc_custom_plugin_install', [$this, 'Boomdevs_Toc_custom_plugin_install'] );

// admin/class-boomdevs-toc-admin.php lines 156-184
public function Boomdevs_Toc_custom_plugin_install() {

    check_ajax_referer('Boomdevs_Toc_custom_plugin_install_nonce', 'security');

    $plugin_slug = 'ai-image-alt-text-generator-for-wp';
    $plugin_file = 'ai-image-alt-text-generator-for-wp/boomdevs-ai-image-alt-text-generator.php';

    // Include necessary WordPress files for plugin installation and activation
    require_once ABSPATH . 'wp-admin/includes/plugin.php';
    require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';

    // Install the plugin
    $upgrader = new Plugin_Upgrader();
    $installed = $upgrader->install("https://downloads.wordpress.org/plugin/{$plugin_slug}.latest-stable.zip");

    if (is_wp_error($installed)) {
        wp_send_json_error(array('message' => $installed->get_error_message()));
    }

    // Activate the plugin
    $activated = activate_plugin($plugin_file);

    if (is_wp_error($activated)) {
        wp_send_json_error(array('message' => $activated->get_error_message()));
    }

    wp_send_json_success(array('message' => 'Plugin installed and activated successfully.'));
}

---

// includes/class-boomdevs-toc-ajax.php line 16
public function get_premade_layout() {
    
    check_ajax_referer( 'layout_content', 'nonce' );

    $default_available_skins_data = [
    // ... (truncated)

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/top-table-of-contents/1.3.31/admin/class-boomdevs-toc-admin.php /home/deploy/wp-safety.org/data/plugin-versions/top-table-of-contents/1.4.0/admin/class-boomdevs-toc-admin.php
--- /home/deploy/wp-safety.org/data/plugin-versions/top-table-of-contents/1.3.31/admin/class-boomdevs-toc-admin.php	2025-11-04 12:38:16.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/top-table-of-contents/1.4.0/admin/class-boomdevs-toc-admin.php	2026-01-22 12:20:42.000000000 +0000
@@ -52,7 +52,6 @@
         $this->plugin_name = $plugin_name;
         $this->version     = $version;
         add_action('wp_ajax_Boomdevs_Toc_custom_plugin_install', [$this, 'Boomdevs_Toc_custom_plugin_install']);
-        add_action( 'wp_ajax_nopriv_Boomdevs_Toc_custom_plugin_install', [$this, 'Boomdevs_Toc_custom_plugin_install'] );
 
     }
 
@@ -158,6 +157,10 @@
 
     public function Boomdevs_Toc_custom_plugin_install() {
 
+        if ( ! current_user_can( 'install_plugins' ) ) {
+            wp_send_json_error( array( 'message' => __( 'You do not have permission to install plugins.', 'boomdevs-toc' ) ), 403 );
+        }
+
         check_ajax_referer('Boomdevs_Toc_custom_plugin_install_nonce', 'security');
     
         $plugin_slug = 'ai-image-alt-text-generator-for-wp';
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/top-table-of-contents/1.3.31/includes/class-boomdevs-toc-ajax.php /home/deploy/wp-safety.org/data/plugin-versions/top-table-of-contents/1.4.0/includes/class-boomdevs-toc-ajax.php
--- /home/deploy/wp-safety.org/data/plugin-versions/top-table-of-contents/1.3.31/includes/class-boomdevs-toc-ajax.php	2025-11-04 12:38:16.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/top-table-of-contents/1.4.0/includes/class-boomdevs-toc-ajax.php	2026-01-22 12:20:42.000000000 +0000
@@ -15,6 +15,10 @@
      */
     public function get_premade_layout() {
         
+        if ( ! current_user_can( 'manage_options' ) ) {
+            wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'boomdevs-toc' ) ), 403 );
+        }
+
         check_ajax_referer( 'layout_content', 'nonce' );
 
         $default_available_skins_data = [
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/top-table-of-contents/1.3.31/includes/class-boomdevs-toc.php /home/deploy/wp-safety.org/data/plugin-versions/top-table-of-contents/1.4.0/includes/class-boomdevs-toc.php
--- /home/deploy/wp-safety.org/data/plugin-versions/top-table-of-contents/1.3.31/includes/class-boomdevs-toc.php	2025-11-04 12:38:16.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/top-table-of-contents/1.4.0/includes/class-boomdevs-toc.php	2026-01-22 12:20:42.000000000 +0000
@@ -254,7 +254,6 @@
     private function register_ajax_hooks() {
 
         $plugin_ajax = new Boomdevs_Toc_Ajax();
-        $this->loader->add_action('wp_ajax_nopriv_get_premade_layout', $plugin_ajax, 'get_premade_layout');
         $this->loader->add_action('wp_ajax_get_premade_layout', $plugin_ajax, 'get_premade_layout');
     }

Exploit Outline

To exploit this vulnerability, an attacker first authenticates as a Subscriber user and navigates to the WordPress dashboard (e.g., /wp-admin/profile.php). By inspecting the global JavaScript object 'Boomdevs_Toc_custom_plugin_install_obj' localized on the page, the attacker retrieves the 'security' nonce. The attacker then sends an HTTP POST request to the '/wp-admin/admin-ajax.php' endpoint with the 'action' parameter set to 'Boomdevs_Toc_custom_plugin_install' and the 'security' parameter set to the captured nonce. Because the server-side function lacks a capability check (e.g., current_user_can('install_plugins')), it will proceed to download and activate the hardcoded 'ai-image-alt-text-generator-for-wp' plugin from the WordPress repository.

Check if your site is affected.

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