❌Attacking JSON Web Tokens❌

Roshan Reju
8 min readNov 17, 2023

--

Introduction to JWT:

JWT, which stands for JSON Web Token, is a compact, self-contained means of representing information between two parties in a way that can be verified and trusted. JWTs are commonly used for authentication and authorization purposes in web development and distributed systems.

JWT Structure:

It consists of header, payload and signature and is represented by putting a “.” in between each parameter.

Structure >> “header.payload.signature”

Header: The header typically consists of two parts: the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA.

Payload: The payload contains the claims. Claims are statements about an entity (typically, the user) and additional data. There are three types of claims: registered, public, and private claims. Eg: “iss” (issuer), “sub” (subject), “exp” (expiration time), and “roles” (user roles).

Signature: To create the signature part, the encoded header and payload are combined with a secret key. The signature is used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn’t changed along the way.

How the entire concept of JWT works?

It is pretty simple. After a user logs in, a server generates a JWT and sends it to the client. The client includes this token in the headers of subsequent requests to prove its identity.

Also, JWT is used with Single Sign On(SSO) as well where a user logs in once to access multiple applications without needing to log in again for each one.

It also helps with stateless authentication as the token contains all the necessary info already, so servers can verify a user’s identity without the need for additional database queries.

Enough of the boring stuff, coming to the interesting part of this writeup, the ATTACKS:

There are quite a few attacks which can be used against JWT tokens:

Before Starting off with the attacks in deep, I would like to mention a tool that I started using recently which helps with anything related to JWT tokens be it Recon, Scanning, exploits and so on.

There are a few good tools out there. Among those, the one I liked was The JSON Web Token Toolkit by Ticarpi which does the following:

  • Checking the validity of a token
  • Testing for known exploits:

(CVE-2015–2951) The alg=none signature-bypass vulnerability

(CVE-2016–10555) The RS/HS256 public key mismatch vulnerability

(CVE-2018–0114) Key injection vulnerability

(CVE-2019–20933/CVE-2020–28637) Blank password vulnerability

(CVE-2020–28042) Null signature vulnerability

  • Scanning for misconfigurations or known weaknesses
  • Fuzzing claim values to provoke unexpected behaviours
  • Testing the validity of a secret/key file/Public Key/JWKS key
  • Identifying weak keys via a High-speed Dictionary Attack
  • Forging new token header and payload contents and creating a new signature with the key or via another attack method
  • Timestamp tampering
  • RSA and ECDSA key generation, and reconstruction (from JWKS files).

We start with understanding the values in the token:

{
"alg": "HS256",
"typ": "JWT"
}.
{
"name": "Bob Marley",
"user_name": "bob.marley",
"is_admin": false
}

Then we can scan the token for common misconfigurations using the jwt_tool.

We have intentionally taken a vulnerable JWT token. Every green line above indicates a positive finding in the scan.

Testing of these misconfigurations and manual testing on these tokens can be done using the JSON Web Tokens extension in Burp Suite.

  1. Signature Verification failure:
  • The ideal way to implement JWT is by setting one method to decode the token and another to verify it. But in some cases there might be developer logic error in decode() method or verify() method. That’s where this vulnerability rises.
  • Consider the token where we did the recon on.
  • Here, we can see the id_admin : false field. We can try and modify it to true.
  • In this case, the verification of the token signature does not happen so this modification goes through and the user get admin privileges.

2. None Algorithm Acceptance:

When algorithm parameter is given as “none” and we get a OK response, then the token is vulnerable to None Algorithm vulnerability.

We can simply strip the signature off of a token like the one shown below

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJuYW1lIjoiSm9obiBEb2UiLCJ1c2VyX25hbWUiOiJqb2huLmRvZSIsImlzX2FkbWluIjpmYWxzZX0..
(sig)fSppjHFaqlNcpK1Q8VudRD84YIuhqFfA67XkLam0_aY(sig)
eyJhbGciOiJOb25lIiwidHlwIjoiSldUIn0.
eyJuYW1lIjoiSm9obiBEb2UiLCJ1c2VyX25hbWUiOiJqb2huLmRvZSIsImlzX2FkbWluIjp0cnVlfQ.

The stripped off token goes through and the attacker can simply use it to replace the valid algorithm and then get rid of the signature.

Therefore algo types like None, none, NONE, nOnE, or any other case variations should never be accepted in the “alg” header.

3. Algorithm Confusion:

Both symmetric and asymmetric encryption algorithms are used with JWT tokens.

Eg: Symmetric algo like HMAC uses the same secret key is used both to sign and verify the JWT.

And Asymmetric algos like RSA uses private key for signing, and a corresponding public key is used for verification.

The algorithm confusion vulnerability arises when an application does not check whether the algorithm of the received token matches the expected algorithm.

  • In some libraries, the verification method might be verify(token, secret) for HMAC or verify(token, publicKey) for RSA.
  • However, in certain cases, this method alone might not ensure that the algorithm used in the token aligns with what the application expects.

Let’s say an attacker can access the public key used by the application. They can exploit this by:

# Changing the algorithm in the token to HMAC.

# Modifying the payload to suit their goals.

# Using the public key to sign the malicious token.

To prevent this vulnerability, applications must be thorough. Before blindly passing a token to the verify() method, they should explicitly check if the algorithm used in the token aligns with what the application expects.

4. Kid Parameter Injection:

The JWT token can contain a parameter known as Kid. It is often used to retrieve the key from a database or filesystem. The application verifies the signature using the key obtained through the Kid parameter.

So this can create a lot of vulnerabilities if the Kid parameter is injectable.

And that’s exactly what we are going to focus on here.

We’ll be using the valid token shown below as an example:

{
"alg": "HS256",
"typ": "JWT",
"kid": "key1"
}.
{
"name": "Bob Marley",
"user_name": "bob.marley",
"is_admin": false
}

Attack1: Getting an RCE if the Kid parameter is injectable.

{
"alg": "HS256",
"typ": "JWT",
"kid": "key1|/usr/bin/uname"
}.
{
"name": "Bob Marley",
"user_name": "bob.marley",
"is_admin": false
}

Attack2: Considering the Kid parameter is vulnerable, we can couple it with directory traversal vulnerability which can lead into signature bypass.

As we already know by now, the key parameter fetches the key from the system to verify it. So if it is vulnerable to directory traversal attack then attacker can force the application to use a file whose value the attacker can predict as a key for verification.

{
"alg": "HS256",
"typ": "JWT",
"kid": "../../../../../../dev/null"
}.
{
"name": "Bob Marley",
"user_name": "bob.marley",
"is_admin": false
}

If the directory traversal to /dev/null succeeds, the attacker will be able to sign a malicious token using an empty string.

Sounds Scary!

Attack3: Considering the Kid parameter is vulnerable, we can couple it with SQL injection vulnerability which can lead into signature bypass.

If the Kid is vulnerable to SQL Injection attack can control the value returned to the kid parameter from an SQL query and use it to sign a malicious token.

Consider that the developer uses SELECT key FROM keys WHERE key=’key1' to fetch the key.

We can use UNION SELECT statement into the Kid as an example:

{
"alg": "HS256",
"typ": "JWT",
"kid": "xxxx' UNION SELECT 'aaa"
}.
{
"name": "Bob Marley",
"user_name": "bob.marley",
"is_admin": false
}

If the Kid is vulnerable to SQL Injection attack and this payload succeeds, application will use SELECT key FROM keys WHERE key=’xxxx’ UNION SELECT ‘aaa’ to fetch the key from the system.

This query returns “aaa” into the Kid parameter and the attacker can maliciously sign the token.

To defend against the Kid parameter injection, sanitation of the Kid field is necessary.

5. Attacks on the “JKU” header in the token:

Developers use the JKU header in the JWT token to specify where the application can find the JSON Web Key (JWK) used to verify the signature which is basically the public key in JSON format.

{
"alg": "RS256",
"typ": "JWT",
"kid": "https://example.com/key.json"
}.
{
"name": "Bob Marley",
"user_name": "bob.marley",
"is_admin": false
}

The content of key.json files can be as shown below:

{
"kty": "RSA",
"n": "zTfU2oIeZDZtNQcBQye6aL9H7l3yYg8n3vXy-.......Hwr5w",
"e": "AQAB"
}

The attacker can try to replace or point the link to the public key to his own public key or a different public key and if the alternate key goes through, then the attacker can maliciously sign the tokens using his private key.

After the malicious token is sent, the application will fetch the attacker’s JWK and use it to verify the signature.

One way to prevent such attacks are whitelisting permitted hosts and have correct URL filtering in place.

We have seen some of the attacks on the JWT tokens. Now we’ll see some of the defenses that can be applied for securing the tokens:

  1. Strong Secret Keys:
  • Use long, random secret keys to sign JWTs securely.

2. Token Expiration:

  • Set reasonable expiration times for JWTs to limit their usability window.

3. Token Refresh Mechanism:

  • Implement token refresh mechanisms to mitigate the impact of compromised tokens.

4. Validate Token Signatures:

  • Always validate JWT signatures to prevent attackers from forging tokens.

5. Use HTTPS:

  • Transmit JWTs over HTTPS for secure data transit.

6. Avoid Storing Sensitive Information:

  • Minimize sensitive data in JWT payloads, keeping it on the server when possible.

7. Implement Proper Access Controls:

  • Enforce access controls on the server side, regardless of token validity.

8. Token Blacklisting:

  • Implement mechanisms to blacklist or revoke compromised tokens.

Conclusion:

In a nutshell, diving into the world of JWTs is like exploring secret codes for web security. JWTs, or JSON Web Tokens, help parties share info securely online, often used for logins and permissions.

But here’s the catch — there are potential attacks! From tricking signatures to messing with algorithms, attackers can find weak spots. Tools like the JSON Web Token Toolkit can help spot and fix these issues during security tests.

To keep things secure, use strong secret keys, set short expiration times, and check signatures. Also, be smart about HTTPS, avoid storing super-secret stuff, and have a plan for compromised tokens.

Understanding these JWT ins and outs helps everyone — from developers to security buffs — stay on top of web security. Stay sharp, stay safe, and keep those JWTs in check!

--

--

Roshan Reju

AppSec || SOC || Cybersecurity Researcher || CEH HOF Finalist