Block, Suspend, Report for BuddyPress <= 3.6.4 - Authenticated (Subscriber+) Stored Cross-Site Scripting via 'link' Parameter
Description
The Block, Suspend, Report for BuddyPress plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'link' parameter in versions up to and including 3.6.4. This is due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers with subscriber-level access and above to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v3.6.5
Source Code
WordPress.org SVNThis exploitation research plan targets **CVE-2026-4653**, a Stored Cross-Site Scripting (XSS) vulnerability in the "Block, Suspend, Report for BuddyPress" plugin. ### 1. Vulnerability Summary The vulnerability exists in the reporting mechanism of the **Block, Suspend, Report for BuddyPress** plugi…
Show full research plan
This exploitation research plan targets CVE-2026-4653, a Stored Cross-Site Scripting (XSS) vulnerability in the "Block, Suspend, Report for BuddyPress" plugin.
1. Vulnerability Summary
The vulnerability exists in the reporting mechanism of the Block, Suspend, Report for BuddyPress plugin. Authenticated users (Subscribers and above) can submit reports against other members or content. One of the parameters in the report submission, link, is stored in the database (likely as post meta for the report custom post type) and subsequently rendered in the WordPress admin dashboard without sufficient sanitization or escaping. This allows an attacker to inject arbitrary JavaScript that executes when an administrator views the report.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
bptk_report_userorbptk_submit_report(inferred from plugin naming conventions; must be verified via frontend inspection). - Vulnerable Parameter:
link - Authentication: Subscriber level or higher is required.
- Preconditions: BuddyPress must be active, and the "Report" feature must be enabled in the plugin settings.
3. Code Flow
- Submission: A Subscriber user visits a member profile and clicks the "Report" button. This triggers a JavaScript function (likely in the plugin's frontend assets) that collects the report details.
- AJAX Request: The script sends an AJAX POST request to
admin-ajax.php. - Storage: The server-side handler (likely in a frontend class not provided in the snippets) processes the request. It creates a new post of type
reportand stores thelinkparameter into a meta key (e.g.,_bptk_reported_item_link) usingupdate_post_meta(). - Admin Rendering: An administrator navigates to the "Reports" list or the single report edit screen.
- The Sink: The plugin retrieves the stored
linkmeta and echoes it. Based onadmin/class-bp-toolkit-reports-screen.php, many columns are rendered by echoing data directly. If thelinkis displayed in a metabox on the single report page or a custom column in the list table withoutesc_url()oresc_html(), the payload executes.
4. Nonce Acquisition Strategy
The reporting action requires a WordPress nonce. Because nonces are tied to the user's session, they cannot be generated via WP-CLI for use in HTTP requests.
- Identify Trigger: The "Report" button is added to BuddyPress member profiles.
- Setup Page: Create a BuddyPress environment with at least two users (Admin and Subscriber).
- Navigate: Log in as the Subscriber and navigate to the Admin's profile page (e.g.,
/members/admin/). - Extract Nonce: The nonce is likely localized in a JavaScript object. Use the
browser_evaltool to find it.- Common variable names:
window.bptk_ajax,window.bsr_data. - Common keys:
report_nonce,nonce. - Command:
browser_eval("window.bptk_ajax?.nonce || window.bsr_data?.nonce") - Note: Inspect the page source for
wp_localize_scriptcalls to find the exact object name.
- Common variable names:
5. Exploitation Strategy
- Identify Action: Use the browser's Network tab (or
browser_evalto inspect event listeners) to identify the exactactionparameter used when the "Report" button is clicked. We will assume the action isbptk_submit_report(inferred). - Craft Payload: A standard XSS payload for a link context:
javascript:alert(document.domain)//"><script>alert(1)</script>
- Submit Report: Use the
http_requesttool to simulate the report submission.
Example Request:
- Method: POST
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
(Note: Parameter names likeaction=bptk_submit_report& nonce=[EXTRACTED_NONCE]& link=javascript:alert('XSS')& reported_user_id=1& report_type=spam& details=Testing+vulnerabilityreported_user_idare inferred and should be verified during the "Identify Action" phase.)
- Trigger Execution: Log in as Admin and navigate to the Reports list:
/wp-admin/edit.php?post_type=report.
6. Test Data Setup
- Activate BuddyPress: Ensure BuddyPress is installed and components (Profiles, Activity) are active.
- Activate Plugin: Activate
Block, Suspend, Report for BuddyPress. - Create Users:
admin(Administrator)victim_sub(Subscriber)
- Configure Plugin: Ensure "Report" functionality is enabled in the BSR settings (Dashboard -> BSR -> Report Settings).
7. Expected Results
- The AJAX request should return a success status (e.g.,
{"success":true}). - A new post of type
reportshould appear in the database. - When the Admin views the "Reports" list or clicks into the specific report, the browser should execute the injected JavaScript (e.g., show an alert box).
8. Verification Steps
- Check Database: Use WP-CLI to verify the payload is stored.
wp post list --post_type=report --fields=ID,post_title # Get the latest ID, then: wp post meta list [ID] - Verify Output: Use the
http_requesttool as Admin to fetch the report list and check for the raw payload in the HTML response.# Look for the payload in the response body http_request "http://localhost:8080/wp-admin/edit.php?post_type=report"
9. Alternative Approaches
- Payload Context: If the
linkis reflected inside an<a>tag'shrefattribute, usejavascript:alert(1). If it is reflected inside avalueattribute of an<input>, use" autofocus onfocus=alert(1)//. - Submission Method: If the plugin uses a REST API endpoint instead of
admin-ajax.php, the acquisition of thewp_restnonce remains the same (extract fromwp-jsonor localized scripts), but the endpoint will change to/wp-json/bptk/v1/report.
Summary
The Block, Suspend, Report for BuddyPress plugin is vulnerable to Stored Cross-Site Scripting via the 'link' parameter in report submissions. Authenticated attackers with Subscriber-level access can inject malicious web scripts that execute when an administrator views the reports in the WordPress dashboard.
Vulnerable Code
// admin/class-bp-toolkit-reports-screen.php:121 public function add_report_columns( $column_name, $post_id ) { switch ( $column_name ) { case 'content' : echo get_the_excerpt( $post_id ); break; --- // admin/class-bp-toolkit-reports-screen.php:143 case 'reporter' : $user = get_user_by( 'ID', get_post_meta( $post_id, '_bptk_reported_by', true ) ); echo '<a href="' . get_edit_post_link( $post_id ) . '"><div>' . bp_core_fetch_avatar( array( 'item_id' => $user->ID ) ) . '<div><span>' . $user->display_name . '</span><span>' . get_the_date( "F j, Y \\a\\t g:i a", $post_id ) . '</span></div></div></a>'; break; case 'reported' : $user = get_user_by( 'ID', get_post_meta( $post_id, '_bptk_member_reported', true ) ); $substantiated = ' | Upheld <span class="bptk-reports-count">(' . bptk_substantiated_reports_about_user( $user->ID ) . ')</span>'; echo '<div>' . bp_core_fetch_avatar( array( 'item_id' => get_post_meta( $post_id, '_bptk_member_reported', true ) ) ) . '<div><span>' . $user->display_name . '</span><span>Reports <span class="bptk-reports-count">(' . bptk_reports_about_user( $user->ID ) . ')</span>' . $substantiated . '</span></div></div>'; break;
Security Fix
@@ -118,7 +118,7 @@ switch ( $column_name ) { case 'content' : - echo get_the_excerpt( $post_id ); + echo esc_html( get_the_excerpt( $post_id ) ); break; case 'type' : @@ -141,7 +141,7 @@ case 'reporter' : $user = get_user_by( 'ID', get_post_meta( $post_id, '_bptk_reported_by', true ) ); - echo '<a href="' . get_edit_post_link( $post_id ) . '"><div>' . bp_core_fetch_avatar( array( 'item_id' => $user->ID ) ) . '<div><span>' . $user->display_name . '</span><span>' . get_the_date( "F j, Y \\a\\t g:i a", + echo '<a href="' . esc_url( get_edit_post_link( $post_id ) ) . '"><div>' . bp_core_fetch_avatar( array( 'item_id' => $user->ID ) ) . '<div><span>' . esc_html( $user->display_name ) . '</span><span>' . get_the_date( "F j, Y \\a\\t g:i a", $post_id ) . '</span></div></div></a>'; break; @@ -153,7 +153,10 @@ echo '<div>' . bp_core_fetch_avatar( array( 'item_id' => get_post_meta( $post_id, '_bptk_member_reported', true ) - ) ) . '<div><span>' . $user->display_name . '</span><span>Reports <span class="bptk-reports-count">(' . bptk_reports_about_user( $user->ID ) . ')</span>' . $substantiated . '</span></div></div>'; + ) ) . '<div><span>' . esc_html( $user->display_name ) . '</span><span>Reports <span class="bptk-reports-count">(' . (int) bptk_reports_about_user( $user->ID ) . ')</span>' . esc_html( $substantiated ) . '</span></div></div>'; + if ( $reported_link = get_post_meta( $post_id, '_bptk_reported_item_link', true ) ) { + echo '<br><a href="' . esc_url( $reported_link ) . '">' . esc_html__( 'View Reported Item', 'bp-toolkit' ) . '</a>'; + } break;
Exploit Outline
1. Login to the WordPress site as a Subscriber-level user. 2. Navigate to a BuddyPress member profile or activity item where a 'Report' button is available. 3. Extract the required WordPress nonce from the page, usually found in a localized JavaScript object (e.g., window.bptk_ajax.nonce). 4. Send a POST request to /wp-admin/admin-ajax.php with the following parameters: action=bptk_submit_report, nonce=[NONCE], and the 'link' parameter set to an XSS payload such as javascript:alert(document.domain) or a link containing <script> tags. 5. The payload is stored as metadata (e.g., _bptk_reported_item_link) for a new post of type 'report'. 6. The XSS triggers when an administrator navigates to the 'Reports' list in the admin dashboard (wp-admin/edit.php?post_type=report) and the stored link is rendered without escaping.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.