CVE-2026-24990

WP Docs <= 2.2.8 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.2.9
Patched in
21d
Time to patch

Description

The WP Docs 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.2.8. 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<=2.2.8
PublishedJanuary 21, 2026
Last updatedFebruary 10, 2026
Affected pluginwp-docs

What Changed in the Fix

Changes introduced in v2.2.9

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-24990 ## 1. Vulnerability Summary The **WP Docs** plugin for WordPress (versions <= 2.2.8) contains a **Missing Authorization** vulnerability in its AJAX handlers. Specifically, the function responsible for updating plugin settings (likely `wp_ajax_wpdocs_sav…

Show full research plan

Exploitation Research Plan - CVE-2026-24990

1. Vulnerability Summary

The WP Docs plugin for WordPress (versions <= 2.2.8) contains a Missing Authorization vulnerability in its AJAX handlers. Specifically, the function responsible for updating plugin settings (likely wp_ajax_wpdocs_save_settings) performs a nonce check but fails to verify if the requesting user has the necessary administrative capabilities (e.g., manage_options). This allows an authenticated attacker with Subscriber-level permissions to modify plugin configurations, such as allowed file extensions or user roles permitted to upload documents.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: wpdocs_save_settings (inferred from the nonce action wpdocs_update_options_nonce localized in inc/functions.php).
  • HTTP Method: POST
  • Payload Parameter: wpdocs_options (array)
  • Authentication: Required (Subscriber level or higher).
  • Preconditions: The attacker must be logged in. To pass the nonce check, the attacker must either obtain the wpdocs_update_options_nonce or exploit a failure in the nonce verification (e.g., missing check or default action -1).

3. Code Flow

  1. Entry Point: The AJAX action wp_ajax_wpdocs_save_settings is triggered by a POST request to admin-ajax.php.
  2. Nonce Verification: The handler calls check_ajax_referer('wpdocs_update_options_nonce', 'nonce').
  3. Missing Check: The handler omits a call to current_user_can('manage_options').
  4. Data Processing: User input from $_POST['wpdocs_options'] is passed to sanitize_wpdocs_data() (defined in inc/functions.php).
  5. Sink: The sanitized data is saved to the database using update_option('wpdocs_options', ... ).

4. Nonce Acquisition Strategy

The nonce is generated in inc/functions.php within the wpdocs_admin_enqueue_script() function and localized to wpdocs_ajax_object.nonce.

// From inc/functions.php
wp_localize_script(
    'wpdocs_admin_scripts',
    'wpdocs_ajax_object',
    array(
        ...
        'nonce' => wp_create_nonce('wpdocs_update_options_nonce'),
        ...
    )
);

Strategy:

  1. Accessing the Script: The script is enqueued if $_GET['page'] == 'wpdocs'. While this page is typically restricted to admins, the vulnerability may stem from the fact that the plugin registers this page with a low capability requirement (e.g., read) or that the nonce is leaked on other accessible admin pages.
  2. Extraction:
    • Log in as a Subscriber.
    • Navigate to wp-admin/admin.php?page=wpdocs.
    • Use browser_eval to extract the nonce: window.wpdocs_ajax_object?.nonce.
    • If the page is inaccessible, try the exploit with a blank or invalid nonce to confirm if check_ajax_referer is strictly enforced.

5. Exploitation Strategy

We will attempt to modify the plugin settings to allow Subscribers to upload files by adding the subscriber role to the upload_roles (or similar) setting.

Step-by-Step:

  1. Login: Authenticate as a Subscriber user.
  2. Nonce Retrieval: Navigate to the dashboard and check if wpdocs_ajax_object is available in the global JS scope.
  3. Formulate Payload:
    • Action: wpdocs_save_settings
    • Nonce: [extracted_nonce]
    • Data: wpdocs_options[allowed_roles][]=subscriber&wpdocs_options[allowed_roles][]=administrator
  4. Execute Request:
    • Send a POST request using the http_request tool.
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body: action=wpdocs_save_settings&nonce=[NONCE]&wpdocs_options[allowed_roles][]=subscriber&wpdocs_options[allowed_roles][]=administrator

6. Test Data Setup

  1. Plugin: Install and activate WP Docs version 2.2.8.
  2. User: Create a user with the subscriber role (e.g., attacker/attacker).
  3. Initial State: Ensure the wpdocs_options does not currently include the subscriber role in its allowed list.

7. Expected Results

  • The server should return a 200 OK or a success JSON response (e.g., {"success":true}).
  • The wpdocs_options record in the wp_options table should be updated to include 'subscriber' in the roles list.

8. Verification Steps

  1. WP-CLI Check: Verify the option change directly in the database.
    wp option get wp
    
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Docs plugin for WordPress is vulnerable to unauthorized document management due to missing capability checks in the AJAX handlers for folder creation and deletion. Authenticated attackers with Subscriber-level access or higher can bypass intended administrative restrictions to create or delete document directories by providing a valid security nonce.

Vulnerable Code

// inc/functions.php line 627 (approx)
function wpdocs_create_folder()
{
    $nonce = sanitize_wpdocs_data(wp_unslash($_POST['nonce']));
    
    if (!empty($_POST) && isset($_POST['nonce']) && ! wp_verify_nonce( $nonce, 'wpdocs_update_options_nonce' ) )

---

// inc/functions.php line 2020 (approx)
function wpdocs_delete_folder()
{
    $nonce = sanitize_wpdocs_data(wp_unslash($_POST['nonce']));
    
     if (!empty($_POST) && isset($_POST['nonce']) && ! wp_verify_nonce( $nonce, 'wpdocs_update_options_nonce' ) )

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-docs/2.2.8/inc/functions.php /home/deploy/wp-safety.org/data/plugin-versions/wp-docs/2.2.9/inc/functions.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-docs/2.2.8/inc/functions.php	2025-03-27 01:58:58.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-docs/2.2.9/inc/functions.php	2026-01-17 14:15:20.000000000 +0000
@@ -626,6 +626,11 @@
 	
 	function wpdocs_create_folder()
 	{
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( __("Unauthorized user", 'wp-docs') );
+			wp_die();
+		}
+		
 		$nonce = sanitize_wpdocs_data(wp_unslash($_POST['nonce']));
 		
 		if (!empty($_POST) && isset($_POST['nonce']) && ! wp_verify_nonce( $nonce, 'wpdocs_update_options_nonce' ) )
@@ -2019,6 +2024,12 @@
 
 	function wpdocs_delete_folder()
 	{
+		
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( __( 'Unauthorized user', 'wp-docs' ) );
+			wp_die();
+		}
+				
 		$nonce = sanitize_wpdocs_data(wp_unslash($_POST['nonce']));
 		
 		 if (!empty($_POST) && isset($_POST['nonce']) && ! wp_verify_nonce( $nonce, 'wpdocs_update_options_nonce' ) )

Exploit Outline

To exploit this vulnerability, an attacker must first authenticate as a Subscriber. The attacker then retrieves the 'wpdocs_update_options_nonce' security token, which is localized in the JavaScript variable 'wpdocs_ajax_object.nonce' (often accessible via the admin dashboard or by navigating to plugin pages where scripts are enqueued). Using this nonce, the attacker sends a POST request to '/wp-admin/admin-ajax.php' with the 'action' parameter set to 'wpdocs_create_folder' (to create new directories) or 'wpdocs_delete_folder' (to delete existing ones). Because the server-side handlers only verify the nonce and not the user's capabilities, the requested operation is performed despite the attacker lacking administrative privileges.

Check if your site is affected.

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