In this post, I'll be talking about an extension called Raindrop, which is one of those neat Bookmark managers for Google chrome, and believe me, it works really well in increasing productivity and keeping a track of things in an organized way. I'll be explaining how I exploited Raindrop's Google Chrome extension to use their premium services for free.
Raindrop is one of the most famous Bookmark manager apps that's being used by more than 100,000+ users according to its Chrome Extension page here -
It also has one of the nicest web interfaces at https://app.raindrop.io/. On top of all that, it's completely open sourced at https://github.com/raindropio which attracted me towards using it.
So how this extension works is you add it to your Google chrome, and then bookmark any of your pages using this extension. This will be saved in your browser's extension as well as on Raindrop's Web app because the same API is being used to interact with the application.
Raindrop features two plans, one is free and the other one is paid. The paid plan comes with some extra features which you can find on their website. One of the most useful features in their paid plan is creating nested collections for bookmarks. This is what I'll be bypassing today.
Finding a needle in the Haystack
First of all, this is an unlisted Extension and it can't be searched in Google Chrome extensions anymore. You can still find it's other extension in the Chrome store which is not vulnerable. Let me rephrase that to obfuscated.
I'll start with installing the extension normally from the Chrome store.
When this extension is installed for the first time, a screen like this will be shown in a new tab after signing up and logging in
And when we try to create a Nested Collection from the sidebar, in the free account, an error like this will be shown which I'll be bypassing soon.
I already checked my Burp to see if Pro validations are being done on any API endpoint but couldn't find any which probably means that it's happening somewhere in the source code on the client side.
Let's fire up the Chrome debugger to look deeper into the source to see where the actual validation is happening.
Going to the Sources tab, I can see a partial directory structure of the extension. From the looks of it, it seems the application logic is inside
Now since I'm looking for the function which validates if a user is Pro or Free, I'll be searching for the word
Pro inside the JS file. This gave around 932 matches which was too much to manually go through so I turned on "Match Case" which reduced the potential matches to 233 and also gave me what I was looking for. The name of the function which looks like it is the one validating if a user is Pro.
So the name of the function is
isPro(). Now onto the function definition which quickly brought me to this little part where the function is defined.
From the definition, it is obvious that the function is checking if the user is logged in and if the user is a pro user, this will return true, otherwise false. Now, all we need to do to bypass the Pro validation is to make this function return true. Let's verify this by using Chrome's Debugger.
I'll set a break-point on the function and invoke a Pro feature (Nested collection) to see if the application flow stops on my break-point. To create a break-point, just click on the line number in the JS file and it'll be shown on the right side as shown below.
Now I'll invoke the Pro feature from the sidebar. It can be seen from the below screenshot that the execution halted on this exact function.
I'll step into the next function call to see the return value and it can be seen in the Scope section that false was returned since I'm not a pro user.
This confirms that it is the same function that's validating if a user is on a Pro or a Free plan.
Now onto the next part.
Taking things apart
Here I'll modify the extension, bypass Chrome's Extension Content verification and Load our Unpacked extension back into the browser.
On Linux, the path where Chrome extensions are stored is usually in -
The extension ID can be obtained from the Extensions page in Chrome -
I'll copy that folder from the path to any other location and will be making changes in this one. Now Google Chrome does not allow modified extensions to be directly installed and will give an error saying that
This extension may have been corrupted.
To do this I need to modify some of the files inside the source. Here's what's needed to be done.
- Disable or delete the original extension in Chrome.
- Change the
short_namefields to something else which will be easily identifiable in Chrome so as to not get confused with the disabled original extension if it's not already deleted.
Now I just have to find the
isPro() function inside
app.js and make it always return true as shown below.
To proceed further, I need to save all my changes including the ones in the manifest file and the app.js file.
Heading over to
chrome://extensions/ I enabled Developer mode and loaded the unpacked extension folder from the top left option.
Now when I create a Nested collection it'll happily allow me to do so since the validation function will always return true. This also bypasses other Pro features for the extension and the application.
These changes will also be synced with the server which I think is another bug that automatically creates Pro resources due to a missing server-side validation on the endpoint.