By Kuldeep Pandya
Note: This was originally published on Synack Red Team member’s Kuldeep Pandya’s personal website and is republished here with permission.
In this article I detail my recent discovery on Synack Red Team, a NoSQL injection.
Please note that this will not be a technical guide on why NoSQL injections exist and their breakdown. I will share the thought process and approach that I had when testing this particular application.
When I got onboarded to this program, it had one application in scope. It was an authenticated test and credentials were provided by the client. Synack’s quality period was also going on, and it had approximately 8 hours.
As always, I fired up Burp Suite, opened Burp’s in-built browser, went to the login page, and started intercepting.
I was closely monitoring every request after clicking “Login.”
First, there was a login request to the /oauth2/token endpoint. This endpoint returned the JWT token that allowed us to access the application APIs. However, no fun here.
After the login request, there was another request to a metadata endpoint. This also was not very interesting as the endpoint returned data that was going to be used to render the frontend.
But after the first two requests, a request to the/api/[CLIENT_NAME]/Customers was sent. This request in particular was very interesting as it had a parameter named$filter. And the parameter had a long NoSQL string inside it.
The request looked like this:
If you look at the value of the$filter parameter, the URL encoded string decodes to the following filter:
(id eq 2) and ((is_active eq ‘Y’) and (is_deleted eq ‘N’))
This endpoint returned basic customer information like customer name, last login date, etc.
You can see the full request-response pair below:
I had read a few blogs on NoSQL injection in the past. Especially after the HackIM CTF. So I figured this was something related to NoSQL.
Theeq in the$filter is the same as SQL’s= orLIKE.
What the endpoint really did was that it read the value of $filter and then it evaluated the filter and retrieved the data specified in the filter.
To break down the parameters in the above filter:
–id (This was the customer ID. Our current user had the customer ID of 2. If I had changed it to 1 instead of 2, this would have been an easy IDOR.)
–is_active (This was an attribute our user had. Theis_active attribute would beY if our user was active andN if not.)
–id_deleted (This was another attribute to specify if our user was deleted or not.)
The/api/[CLIENT_NAME]/Customers endpoint took the filter and returned our own user(user ID 2)’s data if and only if our user was active and not deleted.
For testing, I removed the later part which was((is_active eq ‘Y’) and (is_deleted eq ‘N’)) and just sent the following filter:
$filter=(id eq 2)
The application happily returned my data without erroring out.
As I was aware that this was NoSQL, I googled “NoSQL wildcards” and tried to play around with wildcards. I came across the following documentation by MongoDB on wildcard indices: https://www.mongodb.com/docs/manual/core/index-wildcard/
I played around with wildcards, doing things like$filter=($** eq 2), and some of it worked meanwhile some of it did not.
I also tried to forcefully put wildcards in the value and crafted this payload:$filter=(id eq $**)
But it did not have a valid syntax so it also failed.
I honestly did not put much effort into wildcards as I was not getting the syntax right.
Then a thought popped into my mind. There was one operator in the filter calledeq. What if I use some other operator? Is it possible to do it?
I googled “MongoDB syntax” which led me to this awesome documentation again by MongoDB: https://www.mongodb.com/docs/manual/tutorial/query-documents/
The above documentation neatly explains MongoDB syntax with SQL alternative syntax to properly understand it.
However, after going a little further into the documentation, the documentation linked to another documentation page which was about “Query and Projection Operators.” You can find it here: https://www.mongodb.com/docs/manual/reference/operator/query/
And this page was exactly what I needed to craft my exploit! The page listed down all the MongoDB operators and their use cases.
I decided to go with the gt operator because I wanted the endpoint to return user details of all the users whose user ID was greater than 0. I had made an assumption that user IDs will start from zero.
For that purpose, I crafted the following payload:
$filter=(id gt 0)
And the application returned the customer information of the other user as well. Sadly, there were only two users and this was a pre-production application. However, I was still happy because I got the info of the other user.
But I was still not happy with the results because only basic login information was leaked. Any sort of PII or sensitive information was not leaked from this endpoint.
I went back to my Burp history and found all the endpoints that had this$filterparameter. I had gathered a total of seven endpoints.
While closely inspecting the endpoints, I found one interesting endpoint called/api/[CLIENT_NAME]/CustomerLogins. This was interesting because it took the filter and returned PII in the response.
I used the same payload as above and sent the request. And the application leaked an email address, username, password hash and phone number of the administrator user! Not just any random user.
I reported all the endpoints and wrote a nice report. There were few other reports for the same vulnerability after the QR had ended, but my report managed to win.
You can reach out to me at @kuldeepdotexe on Twitter.