JSON Web Token Exploitation for Red Team

Hello researchers, hope you are doing great during these tough times. However recently I was doing some research on JWT (JSON Web Token), for CTF-Purpose, however I couldn’t get what I wanted, so here is my article dedicated to Red Team/Pentesters.

Brief explanation for JWT (JSON Web Token)

Wikipedia explains this part very well

JSON Web Token is an internet standard for creating JSON-based access tokens that assert some number of claims. The tokens are signed either using a private secret or a public/private key. For example, a server could generate a token that has the claim “logged in as admin” and provide that to a client.

On this short explanation “a private secret or a public/private key” is what grabbed my attention.

How JWT is built?

I have explained this part very shortly, because my focus on this article is the exploitation. For more info how JWT is built is on this article, which I highly recommend.

JWT consists in 3 parts. Its respective format is very compact and easy to understand. The following example are the 3 parts of JWT with a short-explanation.

This is an example of JWT

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Header — shows the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA.

Payload — contains additional data, such as name, password, expiration date, city and so on.

Signature - verify the message wasn’t changed along the way.

In Red Team we need to mostly analyze Payload and sometimes with Header.

Exploiting the JWT. How hard can it get?

Below are the most common vulnerabilities to JWT. Take a closer look to the following exploits.

1. SQL Injection

SQL Injection can vary in different websites, in their own flow. One example is the below image, which is taken from a vulnerable website to SQL Injection, specifically in username query.

Username query is vulnerable to SQLi

Let’s login by using a regular account and analyze the request (I recommend Burp Suite).

The red rectangle is the session JWT that we got

Let’s take a closer look and see how we can exploit it. Remember that we know the website is vulnerable to SQLi, since we saw the source code.

This is the pasted JWT from the Burp

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Im5hZGVzaG90IiwiaWF0IjoxNTE2MjM5MDIyfQ.Cp6XPz6P0HnGP8e_c2-HeLYGilzCGpOg6pztVqMNegg

Lets paste it on jwt debugger and see what information it contains.

Decoded JWT

While encoded, we see that it’s a HS256, quite common encryption for JWT. Also we see the name which I’m registered in.

What we really need is the username. To do SQLi, we simply change the name’s value (this case: nadeshot) to the payload we want to inject. For example here is my payload:

SELECT * FROM users WHERE username = 'admin'--' AND password = ''

SQLi Payload added to the name parameter, replacing our username

Copy the new JWT and replace it with the one in Burp and send it.

Replaced JWT and the SQLi Payload execution

We receive credentials of the administrator (which is hidden because we respect the privacy).

1.2 SQLi with KID

When you decode the JWT, you will see a parameter called KID, or different JWT Claims which retrieves a key file from the file system. You can try exploit them if they aren’t properly sanitized. Here is an example (What we are working here is the Header, the Payload and Signature aren’t changed):

{
“alg” : “HS256”,
“typ” : “JWT”,
“kid” : “1” // KID is often used to retrieve a key file from the file system
}

Instead of value 1, we can modify it and add our SQLi payload. The final JWT will look something like this:

{
“alg” : “HS256”,
“typ” : “JWT”,
“kid” : “SELECT * FROM users WHERE username = 'admin'--' AND password = ''
}

2. Local File Inclusion

This vulnerability has the same process as the SQLi one, but we use different payload:

../../../../etc/passwd or ....//....//....//....//etc/passwd

Based on my experience, LFI and SQL Injection are more likely to happen in “KID”, which is easily manipulated. Here is an example how we can do it (What we are working here is the Header, the Payload and Signature aren’t changed):

{
“alg” : “HS256”,
“typ” : “JWT”,
“kid” : “1” // KID is often used to retrieve a key file from the file system
}

If KID parameter isn’t properly sanitized, it becomes vulnerable to LFI. We simply add our payload and the final JWT will look something like this (What we are working here is the Header, the Payload and Signature aren’t changed):

{
“alg” : “HS256”,
“typ” : “JWT”,
“kid” : “../../../etc/passwd”
}

Copy the JWT and replace the JWT into the Burp (which is generated by the website). You will see the content of /etc/passwd.

3. Command Execution

The process is the same as 1.2 (SQLi with KID) and 2 (Local File Inclusion). Here is an example (What we are working here is the Header, the Payload and Signature aren’t changed)

{
“alg” : “HS256”,
“typ” : “JWT”,
“kid” : “1”
}

Change the KID into a code, which is going to be executed:

{
“alg” : “HS256”,
“typ” : “JWT”,
“kid” : “1” | whoami;
}

4. HS/RSA Key Confusion and Public Key Leaked

When it comes to encryption’s algorithms for JWTs, HMAC and RSA are the most common ones. HMAC token is signed with a key, and because of the symetric encryption, it is later verified again with the same key. On the other hand, RSA works little different, the token is created with a private key and later it is verified with the corresponding public key. This public key is really handy for an attacker to perform a high-severity attack, especially when it is leaking into JWT. Lets see the following example to better understand how this vulnerability works.

Below is a part of source code from a website, which is vulnerable to this attack.

So, we can edit the JWT because we can modify the algorithm used to “HS256” (and the private key will be the public key that is already inside the JWT). This exploit is CVE-2018–0114.

Ok, but what do we really do with this exploit?

Simple, we can perform common attacks (Command Execution, SQL Injection, LFI… ) which are high-severity vulnerabilities and really high-paid in Bug-Bounty section. (Note: These exploits won’t work if the server is vulnerable to those, duh…)

There is a quite helpful and handy tool for this exploitation, which we are going to use for this article: https://github.com/ticarpi/jwt_tool

First of all, we need 2 things: JWT (which we can easily get with Burp when we login and not only) and our JWT Public key.

4.1 Detecting the vulnerability

When we login to the website, we get this JWT, which we will copy and decode it on jwt.io

Request captured and the value of “session=” is the JWT
Decoded JWT, Public Key is leaked into “pk” parameter

Now that Public Key is leaked into the “pk” parameter, we know that we can go further exploiting.

4.2 Creating our JWT Public Key

Create a simple file (empty) named jwt_public or whatever you want. Than execute the following command:

For aes256

openssl genrsa -out /path/to/jwt_public -aes256 4096

For RSA

openssl rsa -pubout -in /path/jwt_public -out config/jwt/public.pem

4.3 Using jwk_tool.py

python3 jwt_tool.py <JWT>
Then we enter “1” and press Enter.

We don’t want to modify Headers values so press 0 and Enter (however you can still play by adding values like “KID” and try LFI and SQLi, but in this case I don’t want to change it, because website is vulnerable only to SQLi in username value)

We press 1 (we want to change only the username value, from nadeshot -> nadeshot_new). I am just changing the name to see the response from SQL. If I get SQL Error that our new name doesn’t exists, the website is vulnerable.

We press 0 since we are finished with editing.

Since we know server leaks Public Key, we press 4.

Now it’s time to use the jwd_public file we generated before (I used the first command for the file generation). And press enter.

We got the final JWT, we copy it and replace it on Burp.

Lets go back to Burp and replace the existing JWT with the one we got from the python tool.

Our new name got successfully queried into the database, that is why we got an error that our username doesn’t exist. Now for the SQL Injection part, we can repeat the same process but we change the name value with a SQLi Payload instead.

4.4 It is time for creativity

Now what do I have to do? Well until here we did a standard method, but further you have to go on your own, maybe the website has a LFI on KID, or SQL Injection or maybe Command Execution.

The final JWT from Python Tool, looks like this when decoded.

Now we can follow the First, Second and Third section of this article (LFI, SQLi and Command Execution method) to start abusing using the New Public Key.

Happy Hacking!

Red Team Operator | Bug Hunter