CVE-2021-4119: [Bookstack] Email harvesting via SQL "LIKE" clause exploitation
This post details a vulnerability I discovered where an unauthenticated user can dump email addresses from all users in Bookstack. This was fixed in v21.11.3.
Description
Bookstack is a popular wiki platform built in Laravel for storing and organising information and documentation. Prior to Bookstack v21.11.3, it contained an improper access control vulnerability which allowed authenticated users to harvest email addresses belonging to any user on the platform.
Discovery
This was at the time where, I was focused on learning to review PHP apps written in the Laravel framework. Laravel apps are very easy to review compared to traditional PHP apps as we can easily obtain a lot of app functionality through the routes
folder.
code in routes/web.php
in Bookstack
When testing Laravel apps, or any other apps build in the MVC architecture in general, the first thing you should do is to always review the routes present. They contain a treasure trove of information (such as hidden functionality present in the app) which are useful in mapping out the attack surface of the app.
While reviewing each route, I came across the /search/users/select
endpoint.
This endpoint points towards a function present inside a controller, particularly the forSelect()
function present in the UserSearchController()
controller. Controllers are also easy to find, they mostly reside in the app/Http/Controllers
folder.
code in app/Http/Controllers/UserSearchController
in Bookstack
From the code, we can see that the forSelect()
we can control one parameter, search
as evidenced in Line 17, search = $request->get('search', '');
The function takes this parameter and will search for an email or name similar to the input in the search parameter using the LIKE
clause in Lines 23 and 24. If the search
parameter is empty, the function returns a list of usernames. In both cases the function only returns usernames and not emails.
The LIKE
clause is used to filter out results from a query using a regex-like expression.
Lines 23 and 24: Email and name search via LIKE
In general, there are 5 kinds of special characters that can be included in the LIKE
clause
Table taken from https://www.w3schools.com/sql/sql_wildcards.asp
Looking back at $query->where('email', 'like', '%' . $search . '%')
. This is essentially filtering emails based on %{search}%
where {search}
is something we can control.
This means that for a user named admin
with email, admin@example.com
, if we input ?search=admin@
, the user named admin
will appear on the frontend, as we are filtering based on %admin@%
It also means that we can pass in wildcards, if we input ?search=admi_@
, the user named admin
will appear on the frontend, as we are filtering based on %admi_@%
where _
is a wildcard for any singular character. This will come in handy for later.
Exploit
Keep in mind, that we can only receive usernames on the frontend. Hence, to exploit this using a _
we need to first, identify the length of a users email address, which can be done easily by adding a _
character incrementally,
If we go back to the admin@example.com
example, it matches %_%
as admin@example.com
consists of at least one character, it matches %__%
as admin@example.com
consists of at least two characters, it does not match %__________________%
(18 _'s), because admin@example.com
is only 17 character long and so it does not contain at least eighteen characters.
So the first phase is to adding a _
character incrementally, until our target user no longer appears on the endpoint. We then take the one less of the number of _
characters required to identify the length of the username
The second phase is to just fill in the blanks, by iterating through each _
replacing them and trying every possible character that can appear in emails. For example if the email is admin@example.com
we try every character for the first _
then the user should only appear at %a_________________%
.
Note we begin: with length-1
as we only need to append length-1
_
s when we start of the first character.
The final exploit:
Note that this PoC slightly differs from the one at https://huntr.dev/bounties/135f2d7d-ab0b-4351-99b9-889efac46fca/. The PoC in the huntr report still works as it is alright if the calculated length is greater than the actual length, just that we will take 28 additional tries (the size of the character list) for every extra character.
Access Control
Though this post details a SQL LIKE clause exploitation. It is at its heart an access control vulnerability, because both unauthenticated and low-privileged users should not have permission to search for users by their emails. This vulnerability was fixed by restricting access to this particular endpoint to only users with user management permissions, as well as removing email-based search entirely.
Acknowledgements
This vulnerability was discovered by me (@Haxatron) and reported via huntr.dev, a bug-bounty website for open-source software. Kudos to @ssddanbrown (maintainer) for acknowledging and fixing the vulnerability promptly as well as huntr.dev for the platform.
Last updated