DirectoryPress – Business Directory And Classified Ad Listing <= 3.6.25 - Missing Authorization
Description
The DirectoryPress – Business Directory And Classified Ad Listing plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 3.6.25. 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
<=3.6.25What Changed in the Fix
Changes introduced in v3.6.26
Source Code
WordPress.org SVNThis plan outlines the research and proof-of-concept exploitation for **CVE-2026-23548**, a Missing Authorization vulnerability in the **DirectoryPress** plugin. ### 1. Vulnerability Summary The DirectoryPress plugin (<= 3.6.25) registers several AJAX handlers that lack proper authorization (capabi…
Show full research plan
This plan outlines the research and proof-of-concept exploitation for CVE-2026-23548, a Missing Authorization vulnerability in the DirectoryPress plugin.
1. Vulnerability Summary
The DirectoryPress plugin (<= 3.6.25) registers several AJAX handlers that lack proper authorization (capability) checks and, in some cases, nonce verification. Specifically, the functions responsible for retrieving and saving category (taxonomy term) configuration metadata are exposed to unauthorized users. An unauthenticated attacker can exploit this to modify directory category settings, such as icons, colors, and background images, which affects the site's front-end presentation and map markers.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
directorypress_save_category_fields_ajax(and potentiallydirectorypress_terms_configuration_html) - Vulnerable Function:
directorypress_save_category_fields_ajaxinincludes/core/terms/categories-functions.php. - Authentication: Unauthenticated (PR:N). The plugin often registers AJAX actions via
wp_ajax_nopriv_or fails to restrictwp_ajax_handlers to administrative roles. - Preconditions: A directory category (term in the
directorypress-categorytaxonomy) must exist.
3. Code Flow
- Entry Point: An unauthenticated request is sent to
admin-ajax.phpwithaction=directorypress_save_category_fields_ajax. - Hook Registration: In
includes/core/terms/categories-functions.php, the action is registered:add_action('wp_ajax_directorypress_save_category_fields_ajax', 'directorypress_save_category_fields_ajax'); // Note: The vulnerability implies this is either reachable via nopriv or lacks capability checks for existing users. - Missing Check: The function
directorypress_save_category_fields_ajax(as seen in the truncateddirectorypress_terms_configuration_htmlwhich follows the same pattern) likely proceeds directly tosanitize_text_field($_POST['term_id'])and updates term metadata without callingcurrent_user_can('manage_options')or verifying a nonce. - Sink: The function calls
update_term_meta()to store attacker-supplied values in the database.
4. Nonce Acquisition Strategy
While many handlers in this plugin are missing nonces, some might require dp_ajax_nonce. If the target handler requires it, follow this strategy:
- Identify Trigger: The plugin enqueues the
directorypress-ajax-nonceviawp_localize_scripton any page where the DirectoryPress main shortcode is present. - Test Data Setup: Create a public page containing the directory shortcode:
wp post create --post_type=page --post_status=publish --post_title="Directory" --post_content='[directorypress]' - Navigate and Extract:
- Use
browser_navigateto visit the newly created page. - Use
browser_evalto extract the nonce from the localized JS object:// Common object names in DirectoryPress: directorypress_js_obj or dp_js_obj window.directorypress_js_obj?.dp_ajax_nonce || window.dp_js_obj?.dp_ajax_nonce
- Use
5. Exploitation Strategy
We will attempt to modify the color of a specific directory category.
Step 1: Discover Target Category
Retrieve an existing category ID for the directorypress-category taxonomy.
wp term list directorypress-category --format=ids
Step 2: Execute Unauthorized Metadata Update
Send a POST request to update the category's marker color.
- URL: `http://
Summary
The DirectoryPress plugin for WordPress is vulnerable to unauthorized modification of directory category metadata due to a missing capability check in the directorypress_save_category_fields_ajax function. This allows unauthenticated attackers to perform administrative actions such as changing category icons, colors, and map marker settings by interacting with the WordPress AJAX endpoint.
Vulnerable Code
// includes/core/terms/categories-functions.php line 16 add_action('wp_ajax_directorypress_save_category_fields_ajax', 'directorypress_save_category_fields_ajax'); // ... // includes/core/terms/categories-functions.php line 386 function directorypress_save_category_fields_ajax() { $response = array(); $term_id = sanitize_text_field($_POST['term_id']); if ( isset( $_POST['directorypress_category_icon'] ) ) { update_term_meta( esc_attr($term_id), 'directorypress_category_icon', $_POST['directorypress_category_icon'] ); } if ( isset( $_POST['directorypress_category_icon_for_listing'] ) ) { update_term_meta( esc_attr($term_id), 'directorypress_category_icon_for_listing', $_POST['directorypress_category_icon_for_listing'] ); } if ( isset( $_POST['directorypress_category_icon_for_map'] ) ) { update_term_meta( esc_attr($term_id), 'directorypress_category_icon_for_map', $_POST['directorypress_category_icon_for_map'] ); } if ( isset( $_POST['category-image-id'] ) ) { update_term_meta( esc_attr($term_id), 'category-image-id', $_POST['category-image-id'] ); } if ( isset( $_POST['directorypress_category_font_icon'] ) ) { $directorypress_category_font_icon = sanitize_text_field($_POST['directorypress_category_font_icon']); update_term_meta( esc_attr($term_id), 'directorypress_category_font_icon', $directorypress_category_font_icon ); } if ( isset( $_POST['marker_color'] ) ) { $directorypress_category_color = sanitize_text_field($_POST['marker_color']); update_term_meta(esc_attr($term_id), 'marker_color', $directorypress_category_color ); } $response['type'] = 'success'; $response['message'] = esc_html__('updated successfully', 'DIRECTORYPRESS'); wp_send_json($response); }
Security Fix
@@ -384,35 +384,41 @@ // Save Category Fields function directorypress_save_category_fields_ajax() { - $response = array(); - $term_id = sanitize_text_field($_POST['term_id']); - - if ( isset( $_POST['directorypress_category_icon'] ) ) { - - update_term_meta( esc_attr($term_id), 'directorypress_category_icon', $_POST['directorypress_category_icon'] ); - } - if ( isset( $_POST['directorypress_category_icon_for_listing'] ) ) { - update_term_meta( esc_attr($term_id), 'directorypress_category_icon_for_listing', $_POST['directorypress_category_icon_for_listing'] ); - } - if ( isset( $_POST['directorypress_category_icon_for_map'] ) ) { - - update_term_meta( esc_attr($term_id), 'directorypress_category_icon_for_map', $_POST['directorypress_category_icon_for_map'] ); - } - if ( isset( $_POST['category-image-id'] ) ) { - - update_term_meta( esc_attr($term_id), 'category-image-id', $_POST['category-image-id'] ); + if (current_user_can( 'manage_options' ) ) { + + $response = array(); + $term_id = sanitize_text_field($_POST['term_id']); + + if ( isset( $_POST['directorypress_category_icon'] ) ) { + + update_term_meta( esc_attr($term_id), 'directorypress_category_icon', $_POST['directorypress_category_icon'] ); + } + if ( isset( $_POST['directorypress_category_icon_for_listing'] ) ) { + update_term_meta( esc_attr($term_id), 'directorypress_category_icon_for_listing', $_POST['directorypress_category_icon_for_listing'] ); + } + if ( isset( $_POST['directorypress_category_icon_for_map'] ) ) { + + update_term_meta( esc_attr($term_id), 'directorypress_category_icon_for_map', $_POST['directorypress_category_icon_for_map'] ); + } + if ( isset( $_POST['category-image-id'] ) ) { + + update_term_meta( esc_attr($term_id), 'category-image-id', $_POST['category-image-id'] ); + } + if ( isset( $_POST['directorypress_category_font_icon'] ) ) { + $directorypress_category_font_icon = sanitize_text_field($_POST['directorypress_category_font_icon']); + update_term_meta( esc_attr($term_id), 'directorypress_category_font_icon', $directorypress_category_font_icon ); + } + if ( isset( $_POST['marker_color'] ) ) { + $directorypress_category_color = sanitize_text_field($_POST['marker_color']); + update_term_meta(esc_attr($term_id), 'marker_color', $directorypress_category_color ); + } + + $response['type'] = 'success'; + $response['message'] = esc_html__('updated successfully', 'DIRECTORYPRESS'); + }else{ + $response['type'] = 'error'; + $response = esc_html__('no permission!', 'DIRECTORYPRESS'); } - if ( isset( $_POST['directorypress_category_font_icon'] ) ) { - $directorypress_category_font_icon = sanitize_text_field($_POST['directorypress_category_font_icon']); - update_term_meta( esc_attr($term_id), 'directorypress_category_font_icon', $directorypress_category_font_icon ); - } - if ( isset( $_POST['marker_color'] ) ) { - $directorypress_category_color = sanitize_text_field($_POST['marker_color']); - update_term_meta(esc_attr($term_id), 'marker_color', $directorypress_category_color ); - } - - $response['type'] = 'success'; - $response['message'] = esc_html__('updated successfully', 'DIRECTORYPRESS'); wp_send_json($response);
Exploit Outline
The exploit targets the AJAX action `directorypress_save_category_fields_ajax` which is registered without any capability or authorization checks. 1. Target Identification: The attacker identifies a valid category term ID within the `directorypress-category` taxonomy (e.g., by inspecting the front-end or using public enumeration). 2. Payload Construction: The attacker prepares a POST request to `/wp-admin/admin-ajax.php` with the following parameters: - `action`: `directorypress_save_category_fields_ajax` - `term_id`: The ID of the category to modify. - Metadata fields: Any supported category fields like `marker_color`, `directorypress_category_font_icon`, or `directorypress_category_icon` with the desired attacker-supplied values. 3. Request Execution: The attacker submits the request. Because the handler lacks `current_user_can()` checks and nonce verification, the plugin executes `update_term_meta()` with the provided values. 4. Verification: The changes can be verified by observing the updated markers or icons in the directory front-end or map views.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.