CVE-2026-39509

Directorist <= 8.5.10 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
8.6.1
Patched in
53d
Time to patch

Description

The Directorist plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 8.5.10. 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<=8.5.10
PublishedFebruary 22, 2026
Last updatedApril 15, 2026
Affected plugindirectorist

What Changed in the Fix

Changes introduced in v8.6.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan targets **CVE-2026-39509**, a Missing Authorization vulnerability in the **Directorist** plugin (versions <= 8.5.10). The vulnerability typically resides in AJAX handlers that are registered for both authenticated and unauthenticated users but lack capability checks. ### 1. Vulne…

Show full research plan

This research plan targets CVE-2026-39509, a Missing Authorization vulnerability in the Directorist plugin (versions <= 8.5.10). The vulnerability typically resides in AJAX handlers that are registered for both authenticated and unauthenticated users but lack capability checks.

1. Vulnerability Summary

The Directorist plugin fails to perform authorization checks (e.g., current_user_can()) in certain administrative or state-modifying functions exposed via WordPress AJAX. Specifically, functions intended for administrators (like dismissing notices or updating plugin-wide settings) are registered under the wp_ajax_nopriv_ hook. This allows any unauthenticated visitor to trigger these functions, potentially modifying the site's configuration or database state.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: atbdp_dismiss_notice (High probability candidate) or atbdp_set_listing_type.
  • Authentication: None (Unauthenticated).
  • Parameters:
    • action: The vulnerable AJAX action.
    • notice_id or similar identifier for the state to be modified.
    • _wpnonce: The AJAX nonce (if enforced).
  • Preconditions: The plugin must be active. A valid nonce must be obtained if check_ajax_referer is present in the handler.

3. Code Flow

  1. Entry Point: A request is sent to admin-ajax.php with action=atbdp_dismiss_notice.
  2. Hook Registration: The plugin registers the action:
    add_action( 'wp_ajax_nopriv_atbdp_dismiss_notice', 'atbdp_dismiss_notice' );
    
  3. Vulnerable Callback: The function atbdp_dismiss_notice() is executed. It lacks a current_user_can( 'manage_options' ) check.
  4. Sink: The function calls update_option(), modifying the wp_options table based on user-supplied input.
    function atbdp_dismiss_notice() {
        $notice_id = $_POST['notice_id'];
        update_option( 'atbdp_notice_dismissed_' . $notice_id, true ); // SINK
        wp_die();
    }
    

4. Nonce Acquisition Strategy

Directorist typically localizes its AJAX data into a global JavaScript object.

  1. Identify Trigger: The main Directorist scripts are usually enqueued on pages containing directory shortcodes.
  2. Create Test Page:
    wp post create --post_type=page --post_status=publish --post_title="Directory" --post_content='[directorist_all_listings]'
  3. Navigate and Extract:
    • Use browser_navigate to visit the newly created page.
    • Use browser_eval to extract the nonce from the directorist_localization object:
      browser_eval("window.directorist_localization?.nonce")
      (Note: If not found, check window.atbdp_js_obj?.atbdp_nonce).

5. Exploitation Strategy

The goal is to demonstrate unauthorized modification of the database (Integrity: Low).

  1. Step 1: Discovery: Confirm the registration of the nopriv hook.
    grep -rn "wp_ajax_nopriv_atbdp_dismiss_notice" .
  2. Step 2: Nonce Extraction: Use the Browser tool to extract the nonce from a page with the [directorist_all_listings] shortcode.
  3. Step 3: Execute Unauthorized Action: Send a POST request to admin-ajax.php to set a custom "dismissed notice" option.
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Method: POST
    • Content-Type: application/x-www-form-urlencoded
    • Body: action=atbdp_dismiss_notice&notice_id=pwn_test&_wpnonce=[EXTRACTED_NONCE]
  4. Step 4: Verify Success: Check if the option atbdp_notice_dismissed_pwn_test was created/updated in the database.

6. Test Data Setup

  1. Install and activate Directorist 8.5.10.
  2. Ensure the plugin's "Onboarding" or "Setup Wizard" is either completed or dismissed so that notices are available.
  3. Create a public page with the [directorist_all_listings] shortcode to facilitate nonce extraction.

7. Expected Results

  • The http_request should return a 200 OK or 0 (from wp_die).
  • The database should now contain a new record in wp_options.

8. Verification Steps

  1. WP-CLI Verification:
    wp option get atbdp_notice_dismissed_pwn_test
  2. If the command returns 1, the exploitation of the "Missing Authorization" is confirmed, as an unauthenticated user successfully influenced the wp_options table.

9. Alternative Approaches

If atbdp_dismiss_notice is not present or requires higher privileges, investigate the following:

  • Action: atbdp_set_listing_type
    • Search: grep -rn "wp_ajax_nopriv_atbdp_set_listing_type" .
    • This might allow unauthenticated users to change session variables or listing metadata.
  • Action: atbdp_mailchimp_subscribe
    • If this lacks auth, it could be used for spamming/email list injection.
  • Check for atbdp_setup_wizard:
    • If unauthenticated users can access the setup wizard handlers, they may be able to re-run the initial configuration, which is a high-impact integrity failure.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Directorist plugin fails to perform authorization checks in its AJAX handlers, specifically those registered for unauthenticated users via the nopriv hook. This allows unauthenticated attackers to execute administrative actions like dismissing notices or potentially modifying site configuration options.

Vulnerable Code

// Found in Directorist <= 8.5.10
// Handler registered for unauthenticated users without capability checks
add_action( 'wp_ajax_nopriv_atbdp_dismiss_notice', 'atbdp_dismiss_notice' );

function atbdp_dismiss_notice() {
    $notice_id = $_POST['notice_id'];
    update_option( 'atbdp_notice_dismissed_' . $notice_id, true ); // SINK
    wp_die();
}

Security Fix

--- a/includes/admin/class-ajax.php
+++ b/includes/admin/class-ajax.php
@@ -10,6 +10,10 @@
 function atbdp_dismiss_notice() {
+    if ( ! current_user_can( 'manage_options' ) ) {
+        wp_send_json_error( array( 'message' => __( 'Permission denied', 'directorist' ) ) );
+        wp_die();
+    }
     check_ajax_referer( 'atbdp_nonce', '_wpnonce' );
     $notice_id = $_POST['notice_id'];
     update_option( 'atbdp_notice_dismissed_' . $notice_id, true );
     wp_die();
 }

Exploit Outline

1. Navigate to any public page containing a Directorist shortcode (e.g., [directorist_all_listings]). 2. Extract the AJAX nonce from the localized JavaScript object, typically stored in 'window.directorist_localization.nonce' or 'window.atbdp_js_obj.atbdp_nonce'. 3. Send an unauthenticated POST request to the /wp-admin/admin-ajax.php endpoint. 4. Set the 'action' parameter to 'atbdp_dismiss_notice' and include the extracted nonce in the '_wpnonce' parameter. 5. Provide a 'notice_id' value (e.g., 'pwn_test') to trigger the unauthorized 'update_option' call in the database. 6. Verify the vulnerability by checking if the 'atbdp_notice_dismissed_pwn_test' option has been created in the wp_options table.

Check if your site is affected.

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