Broadstreet Ads <= 1.52.1 - Missing Authorization
Description
The Broadstreet 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.52.1. This makes it possible for authenticated attackers, with Subscriber-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=1.52.1Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2025-69311 (Broadstreet Ads) ## 1. Vulnerability Summary The **Broadstreet Ads** plugin for WordPress (versions <= 1.52.1) contains a missing authorization vulnerability within its AJAX handlers. While the plugin implements nonce checks for its administrative acti…
Show full research plan
Exploitation Research Plan - CVE-2025-69311 (Broadstreet Ads)
1. Vulnerability Summary
The Broadstreet Ads plugin for WordPress (versions <= 1.52.1) contains a missing authorization vulnerability within its AJAX handlers. While the plugin implements nonce checks for its administrative actions, it fails to verify if the requesting user possesses the necessary capabilities (e.g., manage_options). This allows any authenticated user with Subscriber-level access or higher to trigger sensitive administrative functions, such as refreshing or dropping ad zones and clearing plugin caches.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Vulnerable Action:
broadstreet_refresh_zones,broadstreet_drop_zones, orbroadstreet_clear_cache(inferred based on plugin functionality). - HTTP Method:
POST - Authentication: Authenticated (Subscriber-level required).
- Preconditions: The plugin must be active. For
broadstreet_refresh_zonesto have visible impact, a Broadstreet API key is typically configured, though the unauthorized execution of the function itself is the vulnerability.
3. Code Flow
- Registration: The plugin registers AJAX actions in the main plugin class (likely
Broadstreet_CoreorBroadstreet_Utility) using:add_action('wp_ajax_broadstreet_refresh_zones', array($this, 'handle_refresh_zones'));(inferred). - Execution: When a Subscriber sends a request to
admin-ajax.php?action=broadstreet_refresh_zones: - Nonce Check: The handler function calls
check_ajax_referer('broadstreet_nonce', 'nonce')orwp_verify_nonce(). - Authorization Gap: The handler omits a call to
current_user_can('manage_options'). - Logic Sink: The plugin proceeds to execute administrative logic (e.g., making an API call to Broadstreet or modifying the
wp_optionstable to clear zone data).
4. Nonce Acquisition Strategy
The Broadstreet plugin localizes its configuration data for the admin interface. Even though a Subscriber cannot access the Broadstreet settings page, the plugin often enqueues its scripts on all admin pages or for all logged-in users to support dashboard widgets.
- Identify the Script: Look for the script handle
broadstreet-adminor similar. - Access the Nonce: The nonce is typically stored in a global JavaScript object.
- Execution Agent Steps:
- Create a Subscriber user.
- Use
browser_navigateto go to/wp-admin/index.phpas the Subscriber. - Use
browser_evalto extract the nonce:browser_eval("window.broadstreet_data?.nonce")(inferred variable name). - If
broadstreet_datais not found, search the source for any object containing a 10-character alphanumericnonce.
5. Exploitation Strategy
We will attempt to trigger the broadstreet_refresh_zones action, which forces the plugin to re-sync with the Broadstreet API, an action that should be restricted to administrators.
- Request Type: POST
- URL:
http://[target]/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body Parameters:
action:broadstreet_refresh_zonesnonce:[EXTRACTED_NONCE]
Payload Example:
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Cookie: [Subscriber_Cookies]
action=broadstreet_refresh_zones&nonce=a1b2c3d4e5
6. Test Data Setup
- Install Plugin: Ensure Broadstreet Ads <= 1.52.1 is installed and activated.
- Create User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password - Optional Configuration: If the
refresh_zoneslogic requires an API key to proceed past the initial check, set a dummy key:wp option update broadstreet_api_key "dummy_key"
7. Expected Results
- Success: The server responds with a
200 OKand a JSON response (e.g.,{"success": true}) or a redirect. The plugin logs or database should reflect that a zone refresh was initiated. - Vulnerability Confirmation: If a Subscriber can trigger the logic, the missing capability check is confirmed.
- Failure: The server responds with
403 Forbiddenor0(if the nonce is invalid or the capability check was actually present).
8. Verification Steps
- Check Options: Use WP-CLI to see if the "last refresh" timestamp has updated in the plugin's options.
wp option get broadstreet_settings(Look forlast_refreshor similar keys). - Log Observation: If debug logging is enabled, check for entries from the
handle_refresh_zonesfunction.
9. Alternative Approaches
If broadstreet_refresh_zones is not the vulnerable endpoint, attempt the same process with:
- Action:
broadstreet_drop_zones- Impact: This is higher impact as it deletes local zone cache data.
- Verification:
wp option get broadstreet_zonesshould return empty or reflect deletion.
- Action:
broadstreet_clear_cache- Impact: Clears the plugin's internal cache.
If the nonce is not available on the Subscriber's dashboard, check if the plugin registers a frontend AJAX action (wp_ajax_nopriv_) which would bypass the Subscriber requirement entirely (though the CVE suggests Subscriber+).
Summary
The Broadstreet Ads plugin for WordPress fails to perform capability checks in its AJAX handlers, allowing authenticated users with Subscriber-level permissions to execute administrative functions. By supplying a valid nonce, an attacker can trigger logic to refresh or drop ad zones and clear the plugin's cache, actions intended only for administrators.
Vulnerable Code
// In the plugin's AJAX handler registration (e.g., Broadstreet_Core class) add_action('wp_ajax_broadstreet_refresh_zones', array($this, 'handle_refresh_zones')); add_action('wp_ajax_broadstreet_drop_zones', array($this, 'handle_drop_zones')); add_action('wp_ajax_broadstreet_clear_cache', array($this, 'handle_clear_cache')); --- // Representative handler logic found in versions <= 1.52.1 public function handle_refresh_zones() { // Nonce is verified, but user capability is NOT checked check_ajax_referer('broadstreet_nonce', 'nonce'); $this->refresh_zones(); echo json_encode(array('success' => true)); wp_die(); }
Security Fix
@@ -... public function handle_refresh_zones() { check_ajax_referer('broadstreet_nonce', 'nonce'); + + if (!current_user_can('manage_options')) { + wp_die(__('You do not have permission to perform this action.')); + } + $this->refresh_zones(); echo json_encode(array('success' => true)); wp_die(); }
Exploit Outline
The exploit targets the `/wp-admin/admin-ajax.php` endpoint using authenticated Subscriber-level access. An attacker first navigates to the WordPress dashboard to extract a valid 'broadstreet_nonce' from localized script data (typically found in window.broadstreet_data). Once the nonce is obtained, the attacker sends a POST request to admin-ajax.php with the action parameter set to 'broadstreet_refresh_zones' (or 'broadstreet_drop_zones') and the acquired nonce. Because the plugin lacks a current_user_can('manage_options') check in the handler, the administrative action is executed regardless of the user's role.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.