Travelly – Tour & Travel Booking Manager for WooCommerce | Tour & Hotel Booking Solution <= 2.1.5 - Missing Authorization
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:NTechnical Details
<=2.1.5Source Code
WordPress.org SVNPatched version not available.
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 slugtbmand common functionality) ortbm_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
- 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' ) ); - Callback Execution: The callback function (e.g.,
tbm_duplicate_itinerary_callback) is reached when a POST request is sent toadmin-ajax.phpwith the corresponding action. - 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 useswp_insert_post()to create a clone without callingcurrent_user_can( 'edit_post', $post_id ).
- The function calls
- 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.
- Identify the Localized Script: Look for
wp_localize_scriptcalls in the plugin source that use a nonce. - JS Variable Name: Likely
tbm_adminortbm_vars(inferred). - Nonce Key: Likely
nonceorsecurity. - Acquisition Steps:
- Log in as a Contributor.
- Navigate to any page in the admin dashboard (e.g.,
/wp-admin/profile.php). - Use
browser_evalto extract the nonce:browser_eval("window.tbm_admin?.security || window.tbm_admin?.nonce || window.tbm_vars?.nonce")
5. Exploitation Strategy
- Setup: Create an "Admin Tour" to target.
- Identify Target ID: Obtain the ID of the Admin Tour.
- Session: Use the Contributor user's cookies.
- 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]
- Response: A successful response should return a
200 OKand potentially a JSON body containing the new Post ID or a success message.
6. Test Data Setup
- Plugin Installation: Ensure
tour-booking-managerversion 2.1.5 is active. - Roles:
- Administrator: Create a tour post of type
itinerariestitled "High Value Secret Tour". - Contributor: Create a standard Contributor user.
- Administrator: Create a tour post of type
- Post Identification:
wp post list --post_type=itinerariesto find the ID of the administrator's tour.
7. Expected Results
- The
admin-ajax.phprequest returns a success indicator. - A new post of type
itinerariesappears 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
- 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 - Integrity Check: Confirm the title of the new post starts with "Copy of" or matches the target tour's title.
- 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:
- Search the plugin directory for all
wp_ajax_occurrences:grep -r "wp_ajax_" wp-content/plugins/tour-booking-manager/ - Filter for callbacks that do not contain the string
current_user_can. - Identify actions that perform state changes (saving settings, deleting data, duplicating posts).
- 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).
- Action:
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
@@ -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.