The Exploit
Unauthenticated attackers can retrieve customer records by calling the exposed AJAX action with a valid publicly accessible nonce and a target email address.
curl 'https://TARGET_DOMAIN/wp-admin/admin-ajax.php' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-raw 'action=hotel_booking_fetch_customer_info&nonce=PUBLIC_NONCE&[email protected]'
The request returns customer data for the supplied email address in the AJAX response. In practice the response contains full name, billing address, phone number, and email for the matched booking record.
What the Patch Did
Before:
'fetch_customer_info' => true,
'sig' => base64_encode( serialize( $params ) ),
After:
//'fetch_customer_info' => true,
// removed serialize() usage from parse_booking_params
The patch removed the public hotel_booking_fetch_customer_info AJAX action registration, preventing unauthenticated access, and eliminated unsafe use of serialize() when building signed parameter payloads.
Root Cause
This was a broken access control bug combined with sensitive information exposure: action=hotel_booking_fetch_customer_info was registered for unauthenticated requests, so an attacker could send email=... and nonce=... to admin-ajax.php without any capability check. The AJAX handler then returned customer details based on that email, crossing the trust boundary from anonymous HTTP client to protected booking data. The underlying CWE is principally CWE-284 (Improper Access Control) leading to CWE-200 (Sensitive Information Exposure).
Why It Works
The load-bearing fix is the removal of the unauthenticated AJAX action registration. If fetch_customer_info remains set to true, the endpoint is still reachable by unauthenticated clients and the bug remains exploitable even if serialize() is removed. The serialization change is defensive hardening: it removes an unrelated unsafe data encoding path that could expose the plugin to future PHP object injection if those parameters were ever unserialized. The author likely kept the serialize() fix as a complementary safety improvement, but the critical security control is disabling the public AJAX hook entirely.
Hardening Checklist
- Register sensitive AJAX endpoints only for authenticated users using
wp_ajax_{action}, notwp_ajax_nopriv_{action}. - Enforce capability checks inside handlers with
current_user_can('manage_options')or the least-privilege capability for the action. - Combine
check_ajax_referer( 'nonce_action', 'nonce' )with authentication checks; a nonce alone is not enough for access control. - Avoid
serialize()/unserialize()on attacker-influenced payloads; preferwp_json_encode()/wp_json_decode()for data exchange. - Limit returned customer data to the minimum required and never expose full PII in unauthenticated-facing code paths.
References
- https://nvd.nist.gov/vuln/detail/CVE-2025-14075