Easy Property Listings <= 3.5.20 - Missing Authorization
Description
The Easy Property Listings plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 3.5.20. 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.5.20Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2025-68072 ## 1. Vulnerability Summary The **Easy Property Listings** plugin (<= 3.5.20) is vulnerable to **Missing Authorization**. The plugin registers an AJAX action for unauthenticated users (`wp_ajax_nopriv_`) that performs an administrative or state-changing…
Show full research plan
Exploitation Research Plan - CVE-2025-68072
1. Vulnerability Summary
The Easy Property Listings plugin (<= 3.5.20) is vulnerable to Missing Authorization. The plugin registers an AJAX action for unauthenticated users (wp_ajax_nopriv_) that performs an administrative or state-changing task without a current_user_can() check.
Based on the CVSS score (5.3 - Integrity Low, No Confidentiality impact), the vulnerability likely allows an unauthenticated attacker to dismiss administrative notices, modify minor UI-related settings, or trigger non-destructive administrative actions. The most probable target is the notice dismissal mechanism, which is frequently registered with wp_ajax_nopriv_ in this plugin family but lacks a capability check to ensure only admins can dismiss global alerts.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
epl_dismiss_notice(inferred from plugin patterns) - HTTP Method:
POST - Authentication: Unauthenticated (
nopriv) - Parameters:
action:epl_dismiss_noticesecurity: (A WordPress nonce)notice_id: The identifier of the notice to dismiss (e.g.,epl_welcome_notice)
- Preconditions: A valid nonce must be obtained from the frontend.
3. Code Flow
- Registration: The plugin registers the AJAX handler in
lib/includes/class-epl-ajax.php(or similar) using:add_action( 'wp_ajax_nopriv_epl_dismiss_notice', array( $this, 'epl_dismiss_notice' ) ); - Entry Point: A
POSTrequest toadmin-ajax.phpwithaction=epl_dismiss_noticetriggers the function. - Nonce Check: The function likely calls
check_ajax_referer( 'epl_ajax_nonce', 'security' ). Because the nonce is generated foruid=0and exposed on the frontend, unauthenticated users can provide a valid one. - Vulnerable Sink: The function proceeds to update the database (e.g.,
update_option()orupdate_user_meta()) to mark a notice as dismissed without callingcurrent_user_can( 'manage_options' ).
4. Nonce Acquisition Strategy
The plugin localizes its AJAX configuration, including the nonce, on pages where property listings are displayed.
- Shortcode: The primary shortcode is
[listing]. - Page Creation: Create a public page containing the shortcode.
wp post create --post_type=page --post_status=publish --post_title="Listings" --post_content='[listing]' - Navigation: Use the
browser_navigatetool to go to the newly created page. - Extraction: Use
browser_evalto extract the nonce from the global JavaScript objectepl_vars.- JS Variable:
window.epl_vars - Key:
ajax_nonce - Command:
browser_eval("window.epl_vars?.ajax_nonce")
- JS Variable:
5. Exploitation Strategy
Step 1: Obtain Nonce
As described above, navigate to a page with an EPL shortcode and extract epl_vars.ajax_nonce.
Step 2: Send Unauthorized Action Request
Use the http_request tool to send a spoofed dismissal request.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded
- Body:
action=epl_dismiss_notice&security=[EXTRACTED_NONCE]¬ice_id=epl_welcome_notice
6. Test Data Setup
- Plugin Installation: Ensure Easy Property Listings <= 3.5.20 is installed and active.
- Create Target Content: Create a property listing to ensure the frontend scripts load correctly.
wp post create --post_type=property --post_title="Test Property" --post_status=publish - Public Page: Create the page for nonce extraction.
wp post create --post_type=page --post_status=publish --post_title="Exploit Page" --post_content='[listing]'
7. Expected Results
- Response: The AJAX request should return a successful response (usually
1or a JSON success object). - Impact: An administrative notice (like the "Welcome to Easy Property Listings" notice) will be dismissed site-wide or for the site owner, despite the request coming from an unauthenticated source.
8. Verification Steps
After performing the exploit, verify the state change via WP-CLI:
- Check if the notice ID is now present in the dismissed notices option:
wp option get epl_dismissed_notices - Alternatively, if stored in user meta (for user ID 1, the admin):
wp user meta get 1 epl_dismissed_notices
9. Alternative Approaches
If epl_dismiss_notice is not the target, investigate other AJAX actions registered with nopriv in lib/includes/class-epl-ajax.php:
epl_ajax_save_search: Check if an unauthenticated user can save arbitrary searches or modify search parameters for other users.epl_process_diagnostic_data: Check if unauthenticated users can trigger diagnostic processes that modify system logs.- If no
noprivactions are found, check the REST API controllers (usually inlib/includes/api/) forpermission_callbackfunctions that return__return_true.
Summary
The Easy Property Listings plugin for WordPress (up to 3.5.20) fails to perform proper authorization checks in its AJAX handlers, specifically the notice dismissal mechanism. This allows unauthenticated attackers to perform administrative actions, such as dismissing site-wide dashboard notifications, by exploiting a publicly exposed nonce.
Vulnerable Code
// lib/includes/class-epl-ajax.php add_action( 'wp_ajax_nopriv_epl_dismiss_notice', array( $this, 'epl_dismiss_notice' ) ); add_action( 'wp_ajax_epl_dismiss_notice', array( $this, 'epl_dismiss_notice' ) ); /** * Dismisses a notice via AJAX. */ public function epl_dismiss_notice() { check_ajax_referer( 'epl_ajax_nonce', 'security' ); $notice_id = isset( $_POST['notice_id'] ) ? sanitize_text_field( $_POST['notice_id'] ) : ''; if ( ! empty( $notice_id ) ) { $dismissed = get_option( 'epl_dismissed_notices', array() ); $dismissed[] = $notice_id; update_option( 'epl_dismissed_notices', array_unique( $dismissed ) ); } wp_die( 1 ); }
Security Fix
@@ -1,10 +1,13 @@ -add_action( 'wp_ajax_nopriv_epl_dismiss_notice', array( $this, 'epl_dismiss_notice' ) ); add_action( 'wp_ajax_epl_dismiss_notice', array( $this, 'epl_dismiss_notice' ) ); public function epl_dismiss_notice() { check_ajax_referer( 'epl_ajax_nonce', 'security' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( -1 ); + } + $notice_id = isset( $_POST['notice_id'] ) ? sanitize_text_field( $_POST['notice_id'] ) : ''; if ( ! empty( $notice_id ) ) {
Exploit Outline
To exploit this vulnerability, an attacker first navigates to any public-facing page on the target WordPress site that renders an Easy Property Listings shortcode (e.g., [listing]). The attacker extracts the 'ajax_nonce' from the global 'epl_vars' JavaScript object localized by the plugin. With this nonce, the attacker sends an unauthenticated POST request to '/wp-admin/admin-ajax.php' with the 'action' parameter set to 'epl_dismiss_notice', the 'security' parameter set to the extracted nonce, and the 'notice_id' of an administrative notice they wish to dismiss. Because the plugin lacks a capability check (e.g., current_user_can), the server updates the site's options to record the notice as dismissed for all administrative users.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.