WP Directory Kit <= 1.5.0 - Missing Authorization
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:NTechnical Details
<=1.5.0Source Code
WordPress.org SVNThis 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 acurrent_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_settingsorwdk_license_save(to be confirmed via discovery). - Method: POST
- Payload Parameter:
action,nonce, and data parameters (e.g.,settings[]orlicense_key). - Authentication: None (Unauthenticated).
- Preconditions: A valid AJAX nonce must be obtained from the frontend if
check_ajax_refereris implemented.
3. Code Flow Trace
- Entry Point: An HTTP POST request is sent to
admin-ajax.phpwith theactionparameter set to a vulnerable hook (e.g.,wdk_save_settings). - Hook Execution: WordPress triggers the
wp_ajax_nopriv_wdk_save_settingshook. - Callback: The request is routed to a handler function, likely in
Wpdirectorykit\App\Controllers\WpdirectorykitControlleror a similar class. - Missing Check: The handler function performs a
check_ajax_referer()(nonce check) but fails to callcurrent_user_can('manage_options'). - Sink: The function processes user input (e.g.,
$_POST['settings']) and passes it toupdate_option()orwp_update_post().
4. Nonce Acquisition Strategy
The plugin likely enqueues a script that localizes a nonce for AJAX operations.
- Identify the Script/Nonce: Search for
wp_localize_scriptin the plugin directory.
Look for variables likegrep -rn "wp_localize_script" wp-content/plugins/wpdirectorykit/wdk_ajax_objorwdk_data. - Determine Triggering Shortcode: Find where the script is enqueued.
Look for the function containinggrep -rn "wp_enqueue_script" wp-content/plugins/wpdirectorykit/wp_localize_scriptand trace its hook (often tied to a shortcode orwp_enqueue_scripts). - Extraction Method:
- Create a page containing the directory shortcode (e.g.,
[wdk_directory]). - Navigate to the page using
browser_navigate. - Use
browser_evalto extract the nonce:// Example (adjust based on grep results) window.wdk_ajax_obj?.nonce || window.wdk_data?.ajax_nonce
- Create a page containing the directory shortcode (e.g.,
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
- Create a target page for nonce extraction:
wp post create --post_type=page --post_status=publish --post_title="Directory" --post_content="[wpdirectorykit_all_listings]" - 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 OKand 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
- Check Option Value:
wp option get wdk_settings - 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_licenseis the vulnerable action, attempt to inject a fake license key to unlock Pro features. - Metadata Modification: Search for
wp_ajax_nopriv_wdk_update_listingto see if unauthenticated users can modify directory entries directly. - Nonce Bypass: Check if
check_ajax_refereris called with the third parameter set tofalse(e.g.,check_ajax_referer('wdk_nonce', 'nonce', false)). if the return value is not checked with anifstatement, 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).
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
@@ -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.