Today, we have a fun one for you. I recently attended a conference and several college students kept asking me very similar questions. One of these questions was how can they transition from the examples they are learning in some classes for basic things like Cross Site Scripting or SQL Injection to something they might actually see in an application. Based on that concept, I wanted to share this new finding we found recently in an application. The finding starts with some weirdness we were witnessing with the authentication’s authentication tokens and morphs into a Blind SQL Injection where we dumped the entire database. So….how did we get there?
First, we need to talk about application authentication. More specifically, let’s talk about JSON Web Tokens or JWTs. For those not familiar, JWT’s are very common in modern applications. They consist of 3 parts, a header, a body, and a signature. These 3 parts are separated by a period and are base64 encoded.
The header contains the algorithm and token type, the body contains the payload or any data needed by the application, and the signature is used to sign the token to prevent anyone from being able to tamper with the data contained in the token.
Normally, these tokens are passed in an ‘Authorization’ header like below.
However, as we examined this application, these JWT’s were being passed in a very different manner. When a user submitted their credentials, the application returned a redirect page that contained html elements and quickly autosubmitted these fields. Upon closer inspection, these html elements contained two JWT’s. Best guess, is that this app was the combination of 2 other applications and this was the solution to have 1 authentication request give authentication for both applications.
This in and of itself is odd, so we decided to check out what information was being stored in these tokens. Since the data is just base64 encoded, they can be easily decoded and examined.
The ID field looks interesting, so we will keep that in mind for later.
First off, we need to see if they application is properly checking the signature of each JWT. After some testing, we came to the conclusion that something is terribly wrong. For JWT#1, all that matters is that a signature is present, no validation occurs to check if the signature is valid, only that one exists. For JWT#2, all that matters is that a header is present. So, a request that looks like the below image is acceptable and successfully logs a user in.
Because of this, we can now intercept this request in BurpSuite, and change the ID we took note of first. By iterating over that, we can now log in as any user.
This is a great finding, so how does SQL injection come in to play? One big mistake many testers make is that they stop too soon. If we further follow the login process, there is a second request where the above 2 JWT tokens are submitted to a second URL. This request only happens after the JWT validation occurs, so it is easy to miss. This is where we located the SQL Injection.
The second request looks like below. The 2 fields ‘hpw’ and ‘token’ are JWT#1 and JWT#2 from the above request. The same rules apply to these fields as well. JWT#1 needs header, body, and any signature, JWT#2 only needs the header. We were pretty sure this request was hitting a database somewhere, so we attempted to enter a few of the most generic SQL Injection statements. Once we entered the below statement, it took 5 seconds for us to get a response and we knew we had something.
‘; if (1=1) WAIT FOR DELAY ‘0:0:5’—
We had successfully identified a timing based Blind SQL Injection. To speed up the process, we decided to drop the request into SQLMap and let it iterate over the tables and dump data.
With this, we were able to dump any and all user data from the database.
What can we learn from this? The actual SQL Injection payload used in this case is one you can find on any cheat sheet. However, finding the request to use the payload was the difficult part. It first required a close examination of the login process and the identification of another finding. The basic principles of SQL Injection are here, the difficulty was in finding the right fields to fuzz. For this I say never skip the information gathering stage, and always make sure to examine important requests one at a time.
Hope this was a fun read. I know we had fun getting to the bottom of this one. Until next time.