CVE-2026-27331

Travelly – Tour & Travel Booking Manager for WooCommerce | Tour & Hotel Booking Solution <= 2.1.5 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.1.6
Patched in
7d
Time to patch

Description

The Travelly – Tour & Travel Booking Manager for WooCommerce | Tour & Hotel Booking Solution 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.5. This makes it possible for authenticated attackers, with contributor-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.5
PublishedMay 26, 2026
Last updatedJune 1, 2026
Affected plugintour-booking-manager

Source Code

WordPress.org SVN
Patched

Patched version not available.

Research Plan
Unverified

This plan details the research and exploitation strategy for **CVE-2026-27331**, a Missing Authorization vulnerability in the "Travelly" plugin. The vulnerability allows authenticated users (Contributor level and above) to perform unauthorized actions, likely related to duplicating or managing tour …

Show full research plan

This plan details the research and exploitation strategy for CVE-2026-27331, a Missing Authorization vulnerability in the "Travelly" plugin. The vulnerability allows authenticated users (Contributor level and above) to perform unauthorized actions, likely related to duplicating or managing tour data.


1. Vulnerability Summary

The Travelly – Tour & Travel Booking Manager plugin (<= 2.1.5) fails to implement proper authorization checks (e.g., current_user_can()) in its AJAX handlers. Specifically, functions related to duplicating itinerary/tour posts are registered via wp_ajax_ but only verify a CSRF nonce without checking the user's capability to edit or manage those posts. This allows a user with the Contributor role—who normally cannot edit or duplicate other users' tours—to duplicate any tour post in the system.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: tbm_duplicate_itinerary (inferred from plugin slug tbm and common functionality) or tbm_duplicate_post.
  • Vulnerable Parameter: post_id (The ID of the Tour/Itinerary to be duplicated).
  • Authentication: Contributor-level session required.
  • Preconditions: A Tour (post type itineraries) must exist, created by an Administrator.

3. Code Flow

  1. Registration: The plugin registers the AJAX hook in an admin-init context:
    add_action( 'wp_ajax_tbm_duplicate_itinerary', array( $this, 'tbm_duplicate_itinerary_callback' ) );
  2. Callback Execution: The callback function (e.g., tbm_duplicate_itinerary_callback) is reached when a POST request is sent to admin-ajax.php with the corresponding action.
  3. Insecure Verification:
    • The function calls check_ajax_referer( 'tbm_admin_nonce', 'security' ); to prevent CSRF.
    • The Sink: The function immediately proceeds to fetch the post by $_POST['post_id'] and uses wp_insert_post() to create a clone without calling current_user_can( 'edit_post', $post_id ).
  4. Action: A new post is created in the database, reflecting the unauthorized duplication.

4. Nonce Acquisition Strategy

The plugin likely localizes the required nonce for its admin scripts. Since the vulnerability requires Contributor access, we can obtain the nonce from the WordPress dashboard.

  1. Identify the Localized Script: Look for wp_localize_script calls in the plugin source that use a nonce.
  2. JS Variable Name: Likely tbm_admin or tbm_vars (inferred).
  3. Nonce Key: Likely nonce or security.
  4. Acquisition Steps:
    • Log in as a Contributor.
    • Navigate to any page in the admin dashboard (e.g., /wp-admin/profile.php).
    • Use browser_eval to extract the nonce:
      browser_eval("window.tbm_admin?.security || window.tbm_admin?.nonce || window.tbm_vars?.nonce")

5. Exploitation Strategy

  1. Setup: Create an "Admin Tour" to target.
  2. Identify Target ID: Obtain the ID of the Admin Tour.
  3. Session: Use the Contributor user's cookies.
  4. Request:
    • Method: POST
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body:
      action=tbm_duplicate_itinerary&post_id=[TARGET_ID]&security=[NONCE]
      
  5. Response: A successful response should return a 200 OK and potentially a JSON body containing the new Post ID or a success message.

6. Test Data Setup

  1. Plugin Installation: Ensure tour-booking-manager version 2.1.5 is active.
  2. Roles:
    • Administrator: Create a tour post of type itineraries titled "High Value Secret Tour".
    • Contributor: Create a standard Contributor user.
  3. Post Identification:
    • wp post list --post_type=itineraries to find the ID of the administrator's tour.

7. Expected Results

  • The admin-ajax.php request returns a success indicator.
  • A new post of type itineraries appears in the database.
  • The new post's author is the Contributor (or the Admin, depending on cloning logic), but the Contributor successfully triggered the creation.

8. Verification Steps

  1. Database Check: Use WP-CLI to verify the existence of the duplicated post:
    wp post list --post_type=itineraries --fields=ID,post_title,post_author
  2. Integrity Check: Confirm the title of the new post starts with "Copy of" or matches the target tour's title.
  3. Access Check: Verify the Contributor was the one who initiated the action by checking the post count:
    wp post list --author=[CONTRIBUTOR_ID] --post_type=itineraries

9. Alternative Approaches

If tbm_duplicate_itinerary is not the correct action name, the agent should:

  1. Search the plugin directory for all wp_ajax_ occurrences:
    grep -r "wp_ajax_" wp-content/plugins/tour-booking-manager/
  2. Filter for callbacks that do not contain the string current_user_can.
  3. Identify actions that perform state changes (saving settings, deleting data, duplicating posts).
  4. Example alternative target: tbm_save_settings.
    • Action: tbm_save_settings
    • Payload: action=tbm_save_settings&tbm_settings[any_setting]=malicious_value&security=[NONCE]
    • Verification: wp option get tbm_settings (inferred option name).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Travelly plugin for WordPress is vulnerable to unauthorized access in versions up to 2.1.5 due to a missing capability check in its AJAX handling logic. Authenticated attackers with contributor-level permissions can exploit this to duplicate tour itineraries by bypassing intended access controls.

Vulnerable Code

// wp-content/plugins/tour-booking-manager/includes/admin/class-itinerary-ajax.php (approximate)

add_action( 'wp_ajax_tbm_duplicate_itinerary', array( $this, 'tbm_duplicate_itinerary_callback' ) );

---

public function tbm_duplicate_itinerary_callback() {
    check_ajax_referer( 'tbm_admin_nonce', 'security' );
    
    // Missing capability check (e.g., current_user_can())

    $post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
    if ( $post_id ) {
        $this->tbm_duplicate_post( $post_id );
    }
    wp_die();
}

Security Fix

--- a/wp-content/plugins/tour-booking-manager/includes/admin/class-itinerary-ajax.php
+++ b/wp-content/plugins/tour-booking-manager/includes/admin/class-itinerary-ajax.php
@@ -10,6 +10,10 @@
 public function tbm_duplicate_itinerary_callback() {
     check_ajax_referer( 'tbm_admin_nonce', 'security' );
 
+    if ( ! current_user_can( 'edit_posts' ) ) {
+        wp_die( __( 'You do not have permission to perform this action.', 'tour-booking-manager' ) );
+    }
+
     $post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
     if ( $post_id ) {

Exploit Outline

The exploit involves an authenticated user (Contributor or higher) identifying the ID of a target itinerary they do not own. The attacker retrieves a valid security nonce from the WordPress dashboard, typically found in localized script variables like 'tbm_admin'. They then send an authenticated POST request to /wp-admin/admin-ajax.php with the 'action' set to 'tbm_duplicate_itinerary', the target 'post_id', and the acquired 'security' nonce. Because the server only validates the nonce and not the user's capabilities, the plugin proceeds to duplicate the itinerary.

Check if your site is affected.

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