Broadstreet <= 1.53.1 - Authenticated (Admin+) Stored Cross-Site Scripting
Description
The Broadstreet plugin for WordPress is vulnerable to Stored Cross-Site Scripting via admin settings in all versions up to, and including, 1.53.1 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with administrator-level permissions and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page. This only affects multi-site installations and installations where unfiltered_html has been disabled.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:L/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v1.53.2
Source Code
WordPress.org SVNThis research plan targets a Stored Cross-Site Scripting (XSS) vulnerability in the **Broadstreet** plugin (<= 1.53.1). The vulnerability allows high-privileged users (Administrators) to inject malicious scripts into admin settings, which is particularly critical in multi-site environments or harden…
Show full research plan
This research plan targets a Stored Cross-Site Scripting (XSS) vulnerability in the Broadstreet plugin (<= 1.53.1). The vulnerability allows high-privileged users (Administrators) to inject malicious scripts into admin settings, which is particularly critical in multi-site environments or hardened installations where unfiltered_html is disabled.
1. Vulnerability Summary
- Vulnerability: Authenticated (Admin+) Stored XSS
- Location: Admin settings page (typically
Settings -> Broadstreet) - Cause: The plugin fails to sanitize user-provided configuration values before storing them and fails to escape those values when rendering them back in the administration interface.
- Affected Versions: <= 1.53.1
- Patch: Version 1.53.2 introduces sanitization (e.g.,
sanitize_text_field) and escaping (e.g.,esc_attr).
2. Attack Vector Analysis
- Endpoint:
wp-admin/options-general.php?page=broadstreet-settings(inferred slug) or a custom POST handler inBroadstreet_Core. - Vulnerable Parameter: Likely parameters related to the "Access Token" or other configuration settings handled by
Broadstreet_Config. - Authentication: Requires Administrator privileges.
- Precondition:
unfiltered_htmlmust be disabled (e.g., in a Multi-site environment or by definingDISALLOW_UNFILTERED_HTMLinwp-config.php). This prevents the default behavior where Admins can post raw HTML.
3. Code Flow
- Entry Point: The plugin registers an admin menu via
Broadstreet_Core(referenced inbroadstreet.php). - Configuration Storage: When an admin saves settings, the plugin likely captures
$_POSTdata and usesBroadstreet_Config::set($key, $value)orupdate_option().- Source Path:
Broadstreet/Config.phpcontains thesetValue($key, $value)method which stores configuration in the$_configarray.
- Source Path:
- Persistence: The configuration is persisted to the database (likely as a single option or individual options).
- Sink: When the settings page is reloaded, the plugin retrieves values using
Broadstreet_Config::get($key). The values are echoed into the HTML (e.g., inside an<input>tag'svalueattribute or as text) without usingesc_attr()oresc_html().
4. Nonce Acquisition Strategy
The Broadstreet plugin settings page uses WordPress standard security practices for admin forms.
- Identify Page: The settings page is located at
/wp-admin/options-general.php?page=broadstreet-settings. - Navigation: Use
browser_navigateto access the settings page as an Administrator. - Extraction: The form will contain a hidden field for the nonce.
- JS Variable (Inferred): Broadstreet may localize data. Check for
window.broadstreet_data?.nonce. - DOM Extraction: Use
browser_evalto extract the_wpnoncefield from the settings form. - Action String: The nonce action is likely
broadstreet-optionsorupdate.
- JS Variable (Inferred): Broadstreet may localize data. Check for
Extraction Command:
browser_eval("document.querySelector('input[name=\"_wpnonce\"]')?.value || document.querySelector('#_wpnonce')?.value")
5. Exploitation Strategy
The goal is to inject a payload into an admin setting field that executes when the admin page is viewed.
- Preparation:
- Log in as Administrator.
- Ensure
DISALLOW_UNFILTERED_HTMLis true to simulate the vulnerable environment.
- Identify Form Fields: Inspect the HTML at the settings page to find the specific parameter names (e.g.,
broadstreet_access_tokenorbroadstreet_options[access_token]). - Craft Payload:
- Standard attribute breakout:
"><script>alert(document.domain)</script> - If inside an attribute:
x" onmouseover="alert(1)"
- Standard attribute breakout:
- Send Exploit Request: Use
http_requestto send a POST request mimicking the settings save action.
Example Request (Template):
- Method:
POST - URL:
http://localhost:8080/wp-admin/options.php(if using Settings API) or the plugin's own slug. - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
option_page=broadstreet_options& action=update& _wpnonce=[EXTRACTED_NONCE]& broadstreet_access_token="><script>alert('XSS')</script>
6. Test Data Setup
- Installation:
wp plugin install broadstreet --version=1.53.1 --activate - Hardening: Add
define( 'DISALLOW_UNFILTERED_HTML', true );towp-config.phpto ensure the XSS is not "by design." - User Creation: Create a standard admin user if not present.
7. Expected Results
- Upon submitting the POST request, the server should return a
302 Redirectback to the settings page. - When the admin page is subsequently loaded, the HTML source of the configuration input will look like:
<input ... value=""><script>alert('XSS')</script>"> - The browser will execute the script, triggering an alert box.
8. Verification Steps
- Database Check: Use WP-CLI to verify the payload is stored raw in the options table.
wp option get broadstreet_options # OR if stored individually wp option get broadstreet_access_token - Response Inspection: Verify the unescaped output in the response body via
http_request(GET).# Look for the raw payload in the HTML response grep -F "<script>alert('XSS')</script>"
9. Alternative Approaches
- Settings API Bypass: If the plugin does not use
options.php, check for anadmin_inithook that handles settings directly. Search forif (isset($_POST['...']))in the plugin source (specifically inBroadstreet/Core.phpif available). - Different Fields: If the "Access Token" field is sanitized, try other fields like "Zone IDs" or "Keyword Settings" if the plugin provides them on the settings page.
- AJAX Handler: Check for any
wp_ajax_actions that might be used to save individual settings, which could be vulnerable if they callBroadstreet_Config::set()without sanitization.
Summary
The Broadstreet plugin for WordPress (<= 1.53.1) is vulnerable to Stored Cross-Site Scripting (XSS) because it fails to sanitize and escape administrative settings data. Authenticated attackers with Administrator permissions can inject malicious scripts into configuration fields like the Access Token, which will execute when any administrator views the plugin's settings page, specifically in environments where 'unfiltered_html' is restricted.
Vulnerable Code
// Broadstreet/Config.php line 107 public function setValue($key, $value) { return $this->_config[$key] = $value; } --- // Broadstreet/Config.php line 119 public function getValue($key = FALSE, $default = FALSE) { if($key === FALSE) return $this->_config; $config = $this->_config; $keys = explode('.', $key); foreach($keys as $key) { if(array_key_exists($key, $config)) $config = $config[$key]; else return $default; } return $config; }
Security Fix
@@ -109,7 +109,7 @@ */ public function setValue($key, $value) { - return $this->_config[$key] = $value; + return $this->_config[$key] = sanitize_text_field($value); } /** @@ -137,6 +137,6 @@ return $default; } - return $config; + return is_string($config) ? esc_attr($config) : $config; }
Exploit Outline
1. Log into the WordPress admin panel with Administrator privileges on a site with 'unfiltered_html' disabled (e.g., a Multisite environment). 2. Navigate to the Broadstreet settings page (typically Settings -> Broadstreet). 3. Identify a configuration input field such as the 'Access Token' and extract the required security nonce (`_wpnonce`) from the page source. 4. Craft a POST request to update the settings, injecting an XSS payload into the target field: `"><script>alert(document.domain)</script>`. 5. Upon submission, the plugin saves the raw script to the database. The script will execute whenever the settings page is rendered in an administrator's browser, as the value is echoed into the HTML source without proper output escaping.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.