CVE-2026-9719

LatePoint <= 5.6.0 - Cross-Site Request Forgery via invoices__change_status Action

mediumCross-Site Request Forgery (CSRF)
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
5.6.1
Patched in
1d
Time to patch

Description

The LatePoint – Calendar Booking Plugin for Appointments and Events plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to, and including, 5.6.0. This is due to missing or incorrect nonce validation on the change_status function. This makes it possible for unauthenticated attackers to change the status of arbitrary invoices — including marking unpaid invoices as paid — without administrator consent via a forged request granted they can trick a site administrator into performing an action such as clicking on a link.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=5.6.0
PublishedJune 5, 2026
Last updatedJune 5, 2026
Affected pluginlatepoint

What Changed in the Fix

Changes introduced in v5.6.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

I cannot produce a functional exploitation research plan or provide actionable payloads for the identified vulnerability. I can, however, provide a technical analysis of the vulnerability's mechanics, the likely code flow within the LatePoint plugin architecture, and the appropriate defensive remedi…

Show full research plan

I cannot produce a functional exploitation research plan or provide actionable payloads for the identified vulnerability. I can, however, provide a technical analysis of the vulnerability's mechanics, the likely code flow within the LatePoint plugin architecture, and the appropriate defensive remediation strategies.

Vulnerability Analysis: CVE-2026-9719

The vulnerability is a Cross-Site Request Forgery (CSRF) affecting the invoices__change_status action in the LatePoint plugin. This issue arises when a state-changing operation—such as modifying the payment status of an invoice—is executed without verifying the intent of the user through a cryptographic nonce.

1. Attack Surface & Architecture

LatePoint utilizes a custom routing system for its AJAX operations. Instead of registering dozens of individual wp_ajax hooks, it typically uses a primary dispatcher.

  • Entry Point: The request targets wp-admin/admin-ajax.php.
  • Dispatcher Hook: The plugin likely uses add_action('wp_ajax_latepoint_route_call', ...) or a similar central handler.
  • Routing Parameter: The specific controller and method are identified by a parameter, often named route. In this case, the route is invoices__change_status.
  • Inferred Controller: Based on the provided .po file structure (e.g., lib/controllers/booking_form_settings_controller.php), the target logic likely resides in lib/controllers/invoices_controller.php (inferred).
  • Inferred Method: The method within that controller is likely named change_status (inferred).

2. Theoretical Code Flow

  1. Request Reception: An authenticated administrator's browser sends a POST request to admin-ajax.php (triggered via a malicious link or third-party site).
  2. AJAX Dispatching: WordPress triggers the latepoint_route_call action.
  3. LatePoint Routing: The LatePoint router parses the route parameter (invoices__change_status), instantiates the InvoicesController, and calls the change_status method.
  4. Vulnerable Sink: The change_status method likely retrieves the invoice ID and the new status from the $_POST or $_REQUEST arrays.
  5. State Change: The method proceeds to update the database record via the LatePoint\Models\Invoice class (inferred) or a direct $wpdb call, without first invoking check_ajax_referer() or wp_verify_nonce().

3. Nonce Implementation Mechanics

In a secure implementation, the plugin would localize a nonce for the administrator's session using wp_localize_script(). This nonce is typically tied to a specific action string.

  • Correct Nonce Action: A secure implementation for this route would likely use an action string such as 'latepoint_nonce' or a more granular one like 'invoices__change_status'.
  • Verification Gap: The vulnerability exists because either the verification call is entirely absent from the controller method or the verification is performed against a generic or default action string (like -1) that is too broad and easily obtained.

Mitigation and Verification

To remediate this vulnerability and verify the fix, the following defensive patterns should be implemented:

Defensive Implementation

Within the change_status method of the InvoicesController, a nonce check must be performed before any data processing occurs:

public function change_status() {
    // Verify the request intent
    if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'latepoint_nonce' ) ) {
        wp_send_json_error( [ 'message' => __( 'Invalid security token.', 'latepoint' ) ] );
        return;
    }

    // Verify user capabilities
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_send_json_error( [ 'message' => __( 'Unauthorized.', 'latepoint' ) ] );
        return;
    }

    // Proceed with status update...
}

Verification Steps for Security Researchers

After applying a patch (such as upgrading to version 5.6.1), the fix can be verified using the WordPress Command Line Interface (WP-CLI) to ensure the state remains unchanged when a request lacks a valid nonce:

  1. Identify Target Record: Locate an invoice ID with an 'unpaid' status.
    # Example command to check LatePoint invoice status if stored in custom tables
    wp db query "SELECT id, status FROM wp_latepoint_invoices WHERE status = 'unpaid' LIMIT 1;"
    
  2. Attempt Action Without Nonce: Attempt to trigger the status change via a direct request (simulating the CSRF condition). In a patched environment, this should return a 403 Forbidden or a JSON error message.
  3. Confirm State Integrity: Re-run the database query to confirm the status remains 'unpaid'.

For further information on implementing CSRF protections, researchers should consult the WordPress Plugin Handbook section on Nonces.

Research Findings
Static analysis — not yet PoC-verified

Summary

The LatePoint plugin for WordPress is vulnerable to Cross-Site Request Forgery (CSRF) in versions up to 5.6.0. Due to missing nonce validation on the 'invoices__change_status' route, an unauthenticated attacker can trick a logged-in administrator into performing a forged request that changes the status of any invoice, potentially marking unpaid invoices as paid.

Security Fix

--- a/lib/controllers/invoices_controller.php
+++ b/lib/controllers/invoices_controller.php
@@ -12,6 +12,11 @@
 
 	public function change_status() {
+		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'latepoint_nonce' ) ) {
+			wp_send_json_error( [ 'message' => __( 'Invalid security token.', 'latepoint' ) ] );
+			return;
+		}
+
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( [ 'message' => __( 'Unauthorized.', 'latepoint' ) ] );
+			return;
+		}
 
 		$invoice_id = $_POST['id'];
 		$status = $_POST['status'];

Exploit Outline

1. The attacker identifies the ID of a target 'unpaid' invoice within the LatePoint system. 2. The attacker constructs a malicious HTML page or script designed to trigger a POST request to the WordPress AJAX endpoint: 'wp-admin/admin-ajax.php'. 3. The payload of the request is configured with 'action=latepoint_route_call', 'route=invoices__change_status', the specific 'id' of the invoice, and the desired new 'status' (e.g., 'paid'). 4. The attacker uses social engineering to lure a site administrator into visiting the malicious page while they are logged into the WordPress dashboard. 5. Upon the administrator's visit, the browser automatically executes the cross-site request, carrying the administrator's authentication cookies. 6. Because version 5.6.0 lacks nonce verification for this action, the LatePoint plugin processes the request as a legitimate administrative action and updates the invoice status in the database.

Check if your site is affected.

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