WordPress Plugin Security Alert

Earlier this week, a friend (who, for the purposes of this post is named Stephen) relayed a story to me of an email that one of his friends had received recently.

This friend – let’s call him John – runs a local business that requires advance bookings from customers. He runs his entire booking calendar through his website, and as well as customers being able to register an account and book through the website, he can also create bookings on behalf of customers from the administration panel.

It’s this latter scenario that gave rise to the situation I’m about to delve into. John had booked this customer in to his calendar through the admin panel of his WordPress-powered website, and the customer received a confirmation email containing a “Manage Account” link.

The customer clicked this link to view his booking details, and was surprised to find that he could see a lot more bookings than just his own! He, very responsibly, emailed John to let him know, who then in turn got in touch with Stephen (who had built the website) and Stephen then relayed this whole story to me.

Immediately my ears pricked up – that’s not a great situation for a customer to find, as it suggests deep-seated problems with the website’s security, so I volunteered to take a look. The following is what I found…

Is this a WordPress problem?

Firstly, let’s get this out of the way. This is a security issue with a specific WordPress plugin – NOT WordPress itself.

What is the plugin affected by this security alert?

The plugin in question is Salon Booking System (link to the WordPress repository)

The original report from Stephen and John related to version 3.30.7 of the Premium version of this plugin, but I have confirmed that the problem exists in version 3.32.2 of the free plugin found on the WordPress repository.

What is the problem?

In short, in situations where the administrator of a website is creating a booking for a customer in the back-end of WordPress – if the admin chooses NOT to create an account for the customer, the confirmation emails (and any subsequent emails relating to that booking) that are sent to the customer’s email address include a link that, when followed, logs them into the WordPress user account that created the booking.

When the link is clicked, the customer is taken to a page that lists all bookings created by the user that created the booking – and, if the customer then goes to the /wp-admin URL (or uses the Dashboard link in the admin bar if it is present at the top) they have access to whatever the original creator of that booking does.

Essentially, this is a privilege escalation vulnerability that may result in personal data being leaked or unauthorised actions being carried out in the WordPress dashboard.

So, how is this happening?

With permission from John, I’ve collated some screenshots of the process – with certain details redacted to protect the anonymity of him, his website and any customers affected.

First, the booking needs to be created by an administrator user:

Note: The “Create a new user” tickbox has not been ticked.

This creates the booking (which is a custom post type) and sets the post_author ID to the currently logged in user, as is pretty much standard with WordPress.

An email confirmation is then sent to the email address specified, in this case my Gmail account. This email confirmation looks like this:

Any identifying marks have been redacted/pixellated to preserve anonymity.

The Manage Account button is what we’re interested in here. That button links through to this URL: https://[redacted]/?sln_customer_login=b2a96dc6

This is what we see at that URL:

Note: Booking 11330 was also created by the same administrator user, but for another customer.

Luckily, on this website at least, there doesn’t appear to be any way of seeing any other customers’ details (although the Update Your Profile link visible in that screenshot does allow the customer to see the administrator’s full name, email address, mobile phone (if present) and postal address (again, if present)

More problematic however, is that if the customer then goes to /wp-admin then they have access to the WordPress dashboard as the user that originally created the booking:

In this case, full administrator access. If another role had been used to create the booking, that role’s privileges would be available instead.

Not good at all – in this case, this would allow the personal details of any registered user or any previously booked customer to be viewed. It also obviously allows the user to edit whatever they want to on the site, if the original user has administrator privileges.

The problem code

This issue stems from the “one click login” facility that is present on the “Manage Account” button. Let’s delve into the code! I’m using version 3.32.2 of the free plugin for this analysis.

The Manage Account button can be found in views/mail/_customer_manage_buttons.php:

Line 37 appends the offending sln_customer_login parameter to the button link, with a value taken from $customer->getHash() – this can be found in src/SLN/Wrapper/Customer.php:

This code attempts to retrieve a meta field from the customer’s user, and if it is not found, generates a new one (using the generateHash() method underneath)

The hash is the first 8 characters of the MD5 hash of the concatenated string of the user’s ID, a colon (:) and the current time as a Unix timestamp (i.e. the number of seconds since Jan 1, 1970) – importantly, this hash does not change unless the existing hash is removed at any point.

When the button is clicked, some code in src/SLN/Action/Init.php takes over if the sln_customer_login parameter is detected:

As can be seen on line 206, the code fetches the Customer record by the hash described above, and if it finds a matching user ID it fetches the WP_User object, sets the authorisation cookie (using wp_set_auth_cookie) for that user and then runs any actions hooked into wp_login. It then redirects the user to the My Account page.

At this point, it’s essentially game over as the user is now logged in as the user that created the booking.

Critically, because the hash for the administrator users are constant once generated (unless they are removed through some other means) every email sent that relates to a booking created by that administrator from that point on will contain the vulnerable URL.

What can I do to prevent my site being exploited for now?

The plugin author has already published a patch to the WordPress plugin repository (v3.32.4) which fixes one element of the problem – namely the post_author being set to the administrator user in the scenario described. The fix sets the post_author to zero, meaning the Manage Account button is not added to the outgoing emails.

So, to begin with, we recommend updating your plugin to the latest version – I assume that a similar patch has been released for the Premium version of the plugin (Salon Booking System Pro)

UPDATE – 28 December 2019: A post on the WordPress Support Forum for this plugin suggests that this patch was not rolled into the Premium version of the plugin.

However, this fix does not yet prevent emails that have already been sent from potentially being exploited.

A number of possible courses of action exist:

  1. The easiest solution is most likely to create a new user in the WordPress dashboard that effectively replaces any accounts previously used to create bookings, and to then change the Role of the old accounts to Subscriber or another role that has no admin privileges.
  2. The best course of action, if you are familiar with database queries, would be to remove the generated hashes of any non-customer users.

Either way, until the vendor fixes this issue in a satisfactory manner, I would strongly advise against creating new customer bookings without ticking the “Create a new user” box – i.e. make sure that you create a new user for every customer!

Disclosure Timeline (all times are GMT+1)

  • 3rd October 2019
    • 10:59 – contacted the plugin vendor asking for a contact to send further details to
    • 11:12 – vendor responded asking for further details
    • 11:23 – sent detailed description of issue to vendor
    • 11:40 – vendor confirmed receipt, and advised that they would investigate
    • 12:49 – as per responsible disclosure guidelines, advised vendor that I would be preparing a disclosure report that would be made public after 60 days, or when a fix is published
    • 13:17 – vendor advised that they are already working on a fix
  • 4th October 2019
    • 12:23 – vendor advised that they have released a fix on the WordPress plugin repository (v3.32.4) that does not display the button on the emails – the fix sets the post_author to zero on bookings created without an attached user
    • 13:03 – suggested to vendor that code should be added to invalidate existing hashes to prevent emails that had already been sent from being exploited.
    • 13:51 – vendor replied to state that they would attempt to find a solution for the existing hashes.
  • 2nd December 2019
    • 60 day window expires, disclosure report is made public (err… except, I forgot to unprotect the article, so this didn’t happen until the 28th December)


WooCommerce: getAddress.io Postcode Lookup


It’s been too long…

1 Comment

  1. Shannon Graham

    Thanks for this report, Gareth. I’m implementing your suggested fix on the site I manage. My post on the plugin’s support forum has been moderated and is no longer viewable. Hopefully this means that the vendor is protecting their existing user’s security while they work on a more complete fix.

Leave a Reply

Your email address will not be published. Required fields are marked *

Privacy Policy & Powered by WordPress & Theme by Anders Norén