Hacker Targeted WordPress Plugin Booking Calendar Contains Vulnerability That Exposes Customer Data
On one of our websites and in third-party data we monitor, we saw what appeared to be a hacker probing for usage the WordPress plugin Booking Calendar today. In the past year, there was a serious vulnerability fixed in the plugin and lesser security issues fixed in the plugin. Those could possibly explain a hacker’s interest in the plugin, especially the serious vulnerability fixed. To make sure there wasn’t something still in the plugin that might be targeted, we did a quick check of the plugin for security issues that are commonly targeted by hackers. What we found was that the plugin still lacks basic security and that at least allows a hacker to easily gain access to all the customer data submitted through the plugin.
We would recommend avoiding the plugin unless a thorough security review, like the ones we do, is done and all the issues found are addressed.
We warned members of our zero-day vulnerability exploitation info sharing partnership of this vulnerability earlier today.
Information Disclosure
In the plugin’s main file, /booking_calendar.php, the plugin registers multiple functions to be accessible through WordPress’ AJAX functionality to even those not logged in to WordPress. As one example of that, there is a function for exporting customer data, wpdevart_export(), that is registered that way:
189 190 | add_action( 'wp_ajax_nopriv_wpdevart_export', array($this,'wpdevart_export') ); add_action( 'wp_ajax_wpdevart_export', array($this,'wpdevart_export') ); |
The only restriction that function has before allowing an export of customer data to be done is to check for a nonce:
518 519 520 521 522 523 524 | public function wpdevart_export() { if(!check_ajax_referer('wpdevart_ajax_nonce', 'wpdevart_nonce')) { die('Request has failed.'); } require_once(WPDEVART_PLUGIN_DIR . 'admin/controllers/Reservations.php'); $controller = new wpdevart_bc_ControllerReservations(); $controller->export_as_csv(); |
A nonce check is used to prevent cross-site request forgery (CSRF) and, as WordPress’ relevant documentation notes, “should never be relied on for authentication, authorization, or access control”.
While a nonce check often does the equivalent of access control even though it shouldn’t be relied for that, with this plugin, the nonce value is intentionally provided to those not logged in to WordPress.
As the proof of concept below confirms, anyone can export the customer data submitted through the plugin.
WordPress Causes Full Disclosure
As a protest of the moderators of the WordPress Support Forum’s continued inappropriate behavior we changed from reasonably disclosing to full disclosing vulnerabilities for plugins in the WordPress Plugin Directory in protest, until WordPress gets that situation cleaned up, so we are releasing this post and then leaving a message about that for the developer through the WordPress Support Forum. (For plugins that are also in the ClassicPress Plugin Directory, we will follow our reasonable disclosure policy.)
You can notify the developer of this issue on the forum as well.
After four years, the moderators have finally tacitly admitted they were behaving inappropriately and have made moves to fix the problems (though incompletely), so these full disclosures can be ended if they simply restore access to our accounts and plugins in the Plugin Directory. Hopefully that takes less than four years.
Update: To clear up the confusion where developers claim we hadn’t tried to notify them through the Support Forum (while at the same time moderators are complaining about us doing just that), here is the message we left for this vulnerability:
Proof of Concept
The following proof of concept will export the customer data for the calendar with ID 1.
Make sure to replace “[path to WordPress]” with the location of WordPress and “[nonce]” with the value of ajaxNonce found on frontend pages of the website.
http://[path to WordPress]/wp-admin/admin-ajax.php?action=wpdevart_export&wpdevart_nonce=[nonce]&calendar_id=1&wpdevart_page=1&reserv_period_start=&reserv_period_end=&wpdevart_serch=&all_pages=0&reserv_status=[]