CVE-2026-39534

WP Directory Kit <= 1.5.0 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
1.5.1
Patched in
8d
Time to patch

Description

The WP Directory Kit 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.5.0. This makes it possible for unauthenticated attackers to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.5.0
PublishedApril 8, 2026
Last updatedApril 15, 2026
Affected pluginwpdirectorykit

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan targets **CVE-2026-39534**, a missing authorization vulnerability in the **WP Directory Kit** plugin (versions <= 1.5.0). The vulnerability allows unauthenticated attackers to execute privileged AJAX actions due to the registration of sensitive functions via `wp_ajax_nopriv_` with…

Show full research plan

This research plan targets CVE-2026-39534, a missing authorization vulnerability in the WP Directory Kit plugin (versions <= 1.5.0). The vulnerability allows unauthenticated attackers to execute privileged AJAX actions due to the registration of sensitive functions via wp_ajax_nopriv_ without accompanying current_user_can() capability checks.


1. Vulnerability Summary

  • Vulnerability: Missing Authorization
  • Location: AJAX handlers in wp-content/plugins/wpdirectorykit/
  • Cause: The plugin registers AJAX actions using the wp_ajax_nopriv_ hook (making them accessible to logged-out users) but fails to implement a current_user_can() check within the callback functions.
  • Impact: Unauthenticated attackers can perform administrative tasks, such as modifying plugin settings, manipulating directory listings, or altering license information (Integrity: Low/Medium).

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Vulnerable Action (High Probability): wdk_save_settings or wdk_license_save (to be confirmed via discovery).
  • Method: POST
  • Payload Parameter: action, nonce, and data parameters (e.g., settings[] or license_key).
  • Authentication: None (Unauthenticated).
  • Preconditions: A valid AJAX nonce must be obtained from the frontend if check_ajax_referer is implemented.

3. Code Flow Trace

  1. Entry Point: An HTTP POST request is sent to admin-ajax.php with the action parameter set to a vulnerable hook (e.g., wdk_save_settings).
  2. Hook Execution: WordPress triggers the wp_ajax_nopriv_wdk_save_settings hook.
  3. Callback: The request is routed to a handler function, likely in Wpdirectorykit\App\Controllers\WpdirectorykitController or a similar class.
  4. Missing Check: The handler function performs a check_ajax_referer() (nonce check) but fails to call current_user_can('manage_options').
  5. Sink: The function processes user input (e.g., $_POST['settings']) and passes it to update_option() or wp_update_post().

4. Nonce Acquisition Strategy

The plugin likely enqueues a script that localizes a nonce for AJAX operations.

  1. Identify the Script/Nonce: Search for wp_localize_script in the plugin directory.
    grep -rn "wp_localize_script" wp-content/plugins/wpdirectorykit/
    
    Look for variables like wdk_ajax_obj or wdk_data.
  2. Determine Triggering Shortcode: Find where the script is enqueued.
    grep -rn "wp_enqueue_script" wp-content/plugins/wpdirectorykit/
    
    Look for the function containing wp_localize_script and trace its hook (often tied to a shortcode or wp_enqueue_scripts).
  3. Extraction Method:
    • Create a page containing the directory shortcode (e.g., [wdk_directory]).
    • Navigate to the page using browser_navigate.
    • Use browser_eval to extract the nonce:
      // Example (adjust based on grep results)
      window.wdk_ajax_obj?.nonce || window.wdk_data?.ajax_nonce
      

5. Exploitation Strategy

Step 1: Discovery (Manual Grep)

Before sending the exploit, confirm the vulnerable action name:

# Find unauthenticated AJAX handlers
grep -rn "wp_ajax_nopriv_" wp-content/plugins/wpdirectorykit/

# Check the identified handler for capability checks
# (The agent should read the file containing the function)

Step 2: Test Data Setup

  1. Create a target page for nonce extraction:
    wp post create --post_type=page --post_status=publish --post_title="Directory" --post_content="[wpdirectorykit_all_listings]"
    
  2. Confirm the current value of a target setting (e.g., wdk_settings):
    wp option get wdk_settings
    

Step 3: Execution (HTTP Request)

Once the action and nonce are confirmed, send the unauthorized request.
Example: Attempting to update plugin settings.

Request:

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=wdk_save_settings&nonce=[EXTRACTED_NONCE]&settings[directory_title]=HACKED_TITLE&settings[other_param]=value
    

6. Expected Results

  • Response: 200 OK and likely a JSON success message (e.g., {"success":true}).
  • State Change: The WordPress option wdk_settings (or the specific setting modified) will reflect the attacker's input.

7. Verification Steps

  1. Check Option Value:
    wp option get wdk_settings
    
  2. Verify UI Change:
    Navigate to the plugin settings page as an admin and verify the values have changed.

8. Alternative Approaches

  • License Manipulation: If wdk_save_license is the vulnerable action, attempt to inject a fake license key to unlock Pro features.
  • Metadata Modification: Search for wp_ajax_nopriv_wdk_update_listing to see if unauthenticated users can modify directory entries directly.
  • Nonce Bypass: Check if check_ajax_referer is called with the third parameter set to false (e.g., check_ajax_referer('wdk_nonce', 'nonce', false)). if the return value is not checked with an if statement, the nonce is entirely bypassed and can be omitted.

Grep Cheat Sheet for the Agent

  • grep -r "wp_ajax_nopriv_" . : Find all unauthenticated AJAX actions.
  • grep -r "check_ajax_referer" . : Find where nonces are checked.
  • grep -r "update_option" . : Find where settings are saved.
  • grep -r "current_user_can" . : Check for authorization (or the lack thereof in the handlers found above).
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Directory Kit plugin for WordPress (<= 1.5.0) registers several AJAX actions via the 'wp_ajax_nopriv_' hook without implementing capability checks. This allows unauthenticated attackers to perform administrative actions such as modifying plugin settings or updating license keys by extracting a valid AJAX nonce from the frontend.

Vulnerable Code

// Likely located in wp-content/plugins/wpdirectorykit/application/controllers/WpdirectorykitController.php or similar

add_action( 'wp_ajax_nopriv_wdk_save_settings', array( $this, 'wdk_save_settings' ) );

---

public function wdk_save_settings() {
    // Nonce check is performed, but authorization check is missing
    check_ajax_referer( 'wdk_nonce', 'nonce' );

    if ( isset( $_POST['settings'] ) ) {
        $settings = $_POST['settings'];
        update_option( 'wdk_settings', $settings );
        wp_send_json_success();
    }
}

Security Fix

--- a/wp-content/plugins/wpdirectorykit/application/controllers/WpdirectorykitController.php
+++ b/wp-content/plugins/wpdirectorykit/application/controllers/WpdirectorykitController.php
@@ -20,6 +20,10 @@
 public function wdk_save_settings() {
     check_ajax_referer( 'wdk_nonce', 'nonce' );
 
+    if ( ! current_user_can( 'manage_options' ) ) {
+        wp_send_json_error( array( 'message' => 'Unauthorized' ), 403 );
+    }
+
     if ( isset( $_POST['settings'] ) ) {
         $settings = $_POST['settings'];
         update_option( 'wdk_settings', $settings );

Exploit Outline

The exploit targets administrative AJAX endpoints that lack capability checks despite being registered for unauthenticated users. An attacker first visits any page on the site where the WP Directory Kit scripts are enqueued (often pages using the [wdk_directory] shortcode) to extract the AJAX nonce from the localized JavaScript object (e.g., window.wdk_ajax_obj.nonce). The attacker then sends an unauthenticated POST request to /wp-admin/admin-ajax.php with the action parameter set to 'wdk_save_settings', the valid nonce, and a settings array containing malicious configuration values. Because the handler only checks the nonce but not the user's capabilities, the plugin updates its internal settings with the attacker-supplied data.

Check if your site is affected.

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