CVE-2026-39565

WpTravelly <= 2.1.7 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.1.8
Patched in
26d
Time to patch

Description

The WpTravelly plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 2.1.7. This makes it possible for authenticated attackers, with subscriber-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.1.7
PublishedMarch 21, 2026
Last updatedApril 15, 2026
Affected plugintour-booking-manager

Source Code

WordPress.org SVN
Patched

Patched version not available.

Research Plan
Unverified

This research plan targets **CVE-2026-39565**, a Missing Authorization vulnerability in the **Travelly – Tour & Hotel Booking Solution** (slug: `tour-booking-manager`) plugin for WordPress. ### 1. Vulnerability Summary The WpTravelly plugin (<= 2.1.7) fails to perform capability checks (e.g., `curr…

Show full research plan

This research plan targets CVE-2026-39565, a Missing Authorization vulnerability in the Travelly – Tour & Hotel Booking Solution (slug: tour-booking-manager) plugin for WordPress.

1. Vulnerability Summary

The WpTravelly plugin (<= 2.1.7) fails to perform capability checks (e.g., current_user_can('manage_options')) on certain AJAX handlers registered via wp_ajax_. While these handlers are only available to authenticated users, the lack of a capability check allows users with the lowest privileges (Subscribers) to execute administrative actions. The most likely targets are functions related to saving plugin configurations or modifying tour/hotel data.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Vulnerable Action: wptravelly_save_settings_data (inferred based on plugin architecture) or wptravelly_save_general_settings_data (inferred).
  • HTTP Method: POST
  • Authentication: Authenticated, Subscriber-level or above.
  • Payload Parameter: settings_data (usually containing an array of plugin options) or specific setting keys.
  • Preconditions: The attacker must be logged in as a Subscriber. A valid AJAX nonce is likely required, even if authorization is missing.

3. Code Flow (Inferred)

  1. Entry Point: The plugin registers AJAX hooks in includes/admin/class-admin-ajax.php or inc/admin/class-admin.php (inferred) using:
    add_action( 'wp_ajax_wptravelly_save_settings_data', array( $this, 'wptravelly_save_settings_data' ) );
  2. Request Handling: The wptravelly_save_settings_data function is called when a POST request hits admin-ajax.php with action=wptravelly_save_settings_data.
  3. Vulnerable Code: Inside the function, the developer likely checks the nonce using check_ajax_referer() but fails to check current_user_can().
  4. Sink: The function proceeds to update the WordPress options table using update_option() or update_post_meta() based on the user-provided $_POST data.

4. Nonce Acquisition Strategy

The plugin likely localizes a nonce for its admin interface. Since the vulnerability requires "Authenticated" access, we can extract the nonce from the WordPress dashboard after logging in as a Subscriber.

  1. Identify Localization: Look for wp_localize_script in the plugin source (likely in includes/admin/class-admin.php).
  2. Target Variable: Look for a JS object named wptravelly_ajax_obj or tour_booking_params.
  3. Extraction Path:
    • Create a Subscriber user and log in.
    • Navigate to the WordPress Dashboard (/wp-admin/).
    • Use browser_eval to extract the nonce:
      browser_eval("window.wptravelly_ajax_obj?.nonce") or browser_eval("window.wptravelly_ajax_obj?.wptravelly_nonce").
  4. Action Check: Verify if the nonce action matches the verification action (e.g., wptravelly_ajax_nonce).

5. Exploitation Strategy

We will attempt to modify a plugin setting (e.g., changing the tour listing page or a contact email) to demonstrate unauthorized data modification.

Step 1: Setup Authentication

  • Log in as a Subscriber user using the browser_navigate and browser_type/click tools.

Step 2: Nonce Extraction

  • Navigate to /wp-admin/ and execute the browser_eval mentioned in Section 4.

Step 3: Unauthorized Request

  • Send a POST request to /wp-admin/admin-ajax.php using the http_request tool.

Payload Example:

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=wptravelly_save_settings_data&nonce=[EXTRACTED_NONCE]&settings_data[tour_page_id]=1337&settings_data[booking_email]=attacker@example.com

6. Test Data Setup

  1. Install Plugin: Ensure tour-booking-manager version 2.1.7 is active.
  2. Create Attacker: wp user create attacker attacker@example.com --role=subscriber --user_pass=password123.
  3. Initial State: Record the current value of the targeted setting:
    wp option get wptravelly_settings (Note: Settings may be stored in a serialized array).

7. Expected Results

  • Response: The server returns a 200 OK or a JSON success message (e.g., {"success":true}).
  • Data Change: The targeted plugin setting in the database is updated despite the request coming from a Subscriber.

8. Verification Steps

  1. Database Check: Run wp option get wptravelly_settings via WP-CLI.
  2. Value Comparison: Verify that tour_page_id is now 1337 or the booking_email matches the payload.
  3. UI Verification: Log in as Admin and navigate to the plugin settings page to see if the changes are reflected in the UI.

9. Alternative Approaches

If wptravelly_save_settings_data is not the correct action:

  1. Grep for Actions: Run grep -r "wp_ajax_" wp-content/plugins/tour-booking-manager/ to list all available authenticated AJAX actions.
  2. Trace Handlers: For each action, check if they call current_user_can().
  3. Metadata Injection: If settings are not accessible, look for wptravelly_save_tour_meta or similar actions that allow modifying tour prices or booking details.
  4. Check for "No Nonce": Attempt the request without the nonce parameter to see if check_ajax_referer is also missing (this would upgrade it to a CSRF).
Research Findings
Static analysis — not yet PoC-verified

Summary

The WpTravelly plugin for WordPress is vulnerable to unauthorized modification of plugin settings due to missing capability checks on authenticated AJAX actions. This allows logged-in users with low privileges, such as Subscribers, to execute administrative functions like updating plugin configurations if they can obtain a valid nonce.

Vulnerable Code

// File: includes/admin/class-admin-ajax.php (inferred)
add_action( 'wp_ajax_wptravelly_save_settings_data', array( $this, 'wptravelly_save_settings_data' ) );

public function wptravelly_save_settings_data() {
    check_ajax_referer( 'wptravelly_ajax_nonce', 'nonce' );

    // Vulnerable: Missing capability check (e.g., current_user_can( 'manage_options' ))
    if ( isset( $_POST['settings_data'] ) ) {
        update_option( 'wptravelly_settings', $_POST['settings_data'] );
    }
    wp_send_json_success();
}

Security Fix

--- a/includes/admin/class-admin-ajax.php
+++ b/includes/admin/class-admin-ajax.php
@@ -10,6 +10,10 @@
 public function wptravelly_save_settings_data() {
     check_ajax_referer( 'wptravelly_ajax_nonce', 'nonce' );
 
+    if ( ! current_user_can( 'manage_options' ) ) {
+        wp_send_json_error( array( 'message' => 'Unauthorized access' ) );
+    }
+
     if ( isset( $_POST['settings_data'] ) ) {
         update_option( 'wptravelly_settings', $_POST['settings_data'] );
     }

Exploit Outline

1. Authenticate to the WordPress site as a user with Subscriber-level permissions. 2. Navigate to the WordPress dashboard (/wp-admin/) and retrieve the AJAX nonce from the localized JavaScript object 'wptravelly_ajax_obj'. 3. Send a POST request to '/wp-admin/admin-ajax.php' with the 'action' parameter set to 'wptravelly_save_settings_data'. 4. Include the 'nonce' parameter and a 'settings_data' array containing the plugin options to be modified (e.g., 'tour_page_id'). 5. Verify that the plugin settings are updated despite the user lacking administrative privileges.

Check if your site is affected.

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