# JSON Web Tokens (JWTs)

JSON Web Token (JWT) is a format for transmitting cryptographically secure data.  JWTs are typically used by web applications as a stateless session token.

in JWT-based authentication, the session token is replaced by a JWT containing user information. After verifying the token's signature, the server can retrieve all user info from the JWT claims sent by the user.

JWTs consist of three main parts: a **header**, a **payload**, and a **signature**, which are **base64-encoded** and separated by **dot** `.` characters:

```
<Base64_Header>.<Base64_Payload>.<Base64_Signature>
```

* *<mark style="color:$success;">**Header**</mark>* - contains ***metadata*** about the token itself, holding information that allows interpreting it such as the ***ecryption algorithm*** used to secure the JWT token
* *<mark style="color:$success;">**Payload**</mark>* - contains the actual ***data*** making up the token. This data comprises multiple standard (registered) ***claims*** or arbitrary, user-defined claims
* *<mark style="color:$success;">**Signature**</mark>* - computed based on the JWT's header, payload, and a ***secret signing key***, using the algorithm specified in the header. The integrity of the JWT token is protected by the signature.

{% hint style="danger" %}
If any data within the header, payload, or signature itself is manipulated, *the signature will no longer match the token, thus enabling the detection of manipulation.*

Having knowledge of the secret signing key is required to compute a valid signature for a signed JWT.
{% endhint %}

***

### Useful Tools and Resources

* <https://jwt.io/> and <https://jwt.lannysport.net/>
* <https://gchq.github.io/CyberChef/>&#x20;
* <https://github.com/ticarpi/jwt_tool>
* <https://github.com/silentsignal/rsa_sign2n>

***

## Attacking Signature Verification

The signature protects data within the JWT's payload. Without knowing the JWT's secret key, it's impossible to manipulate the token without invalidating it.&#x20;

However, there are some misconfigurations in web applications that lead to improper signature verification, enabling us to manipulate the data within a JWT's payload.

### Basic Misconfigurations

#### Case 1 - JWT signature verification is not in place

The first easy misconfiguration is when the web application does not check the JWT's integrity. If the web application is misconfigured to accept JWTs without verifying their signature, we can manipulate our JWT to escalate privileges or change our user data.

#### Case 2 - Signing algorithm set to None

Setting a manipulated JWT's algorithm to none implies that the JWT does not contain a signature, and the web application should accept it without computing one, which sometimes allows bypassing signature verification checks.

To forge a JWT with the `none` algorithm, we must set the `alg`-claim in the JWT's header to `none`

{% hint style="warning" %}
Note: Even if JWT does not contain a signature, the final `.` character is required.
{% endhint %}

<figure><img src="/files/HZk1rf9clMKkNMoUhvrd" alt=""><figcaption></figcaption></figure>

### Algorithm Confusion

#### <mark style="color:$success;">Description</mark>

Algorithm confusion is a JWT attack that forces the web application to use a different algorithm to verify the JWT's signature than the one used to create it.

If a web application uses an **asymmetric algorithm** like **RS256**, it signs JWTs with a **private key** and verifies them using a **public key**. However, if an attacker crafts a JWT that claims to use a **symmetric algorithm** like **HS256**, the situation changes, as it uses the **same key** for both signing and verification.

If the application naively trusts the `alg` field in the token header, it will attempt to verify the token using HS256 **with whatever key it already has**, which in this case is the **public key**.

Because the public key is, by definition, public, an attacker can sign a forged HS256 token using that public key, and the application will incorrectly accept it as valid.

{% hint style="danger" %}
This vulnerability only exists if the application chooses the verification algorithm based on the token’s `alg` header instead of enforcing a fixed, expected algorithm.

To prevent this attack, the application must **ignore the JWT’s `alg` header** and **always use the server-side configured algorithm** (e.g., always RS256).
{% endhint %}

#### <mark style="color:$success;">Performing the Attack</mark>

To execute the algorithm confusion attack, we need the public key used by the web application for signature verification, which you can get:

* from the web application's certificate
* from the web application's JKWS key set usually at `https://example.com/.well-known/jwks.json`
* from the JWT itself

To gain the key from the JWT, get two valid JWTs from the web application (by logging in two times) and use [rsa\_sign2n](https://github.com/silentsignal/rsa_sign2n)&#x20;

```
git clone https://github.com/silentsignal/rsa_sign2n
cd rsa_sign2n/standalone/
docker build . -t sig2n
docker run -it sig2n /bin/bash
python3 jwt_forgery.py <JWT1> <JWT2>
```

The tool may output multiple public key candidates based on the two JWTs you provided.

{% hint style="success" %}
To reduce the number of candidates, we can rerun it with different JWTs captured from the web application
{% endhint %}

Additionally, the tool automatically creates symmetric JWTs signed with the computed public key in different formats, which you can use to test for an algorithm confusion vulnerability.

If a token is accepted, you have confirmed the algorithm confusion vulnerability exists.

To forge a new token with edited claims, use the public key saved by the tool to a local file named `filename_x509.pem` within the docker container in CyberChef *`JWT Sign`*. Set the signing algorithm to HS256 and paste the public key into the Private/Secret key field.&#x20;

{% hint style="danger" %}
Remember to add a newline (`\n`) at the end of the public key!!
{% endhint %}

***

## Cracking the JWT Secret

After gaining access to a valid JWT, it is possible to attempt to brute-force the signing secret to obtain it. JWT supports three symmetric algorithms based on potentially guessable secrets: `HS256`, `HS384`, and `HS512`. If your token uses one of those algorithms, you might be able to crack its secret key.

{% hint style="success" %}
Having access to the signing secret key means we can forge and sign any new valid token.
{% endhint %}

To crack a JWT you can either use hashcat or jwt\_tool:

```bash
hashcat -m 16500 jwt.txt /path/to/wordlist.txt
python3 jwt_tool.py JWT -C -d /path/to/wordlist.txt
```

Some wordlists other than `rockyou.txt` to crack JWTs are:

1. <https://github.com/wallarm/jwt-secrets/blob/master/jwt.secrets.list>
2. <https://github.com/BBhacKing/jwt_secrets/blob/master/jwt_secrets_all.txt>
3. <https://github.com/danielmiessler/SecLists/blob/master/Passwords/scraped-JWT-secrets.txt>

After finding the JWT's secret key, you can forge a new one using [Cyberchef](https://gchq.github.io/CyberChef/) *`JWT Sign`* or [jwt\_tool](https://github.com/ticarpi/jwt_tool)

***

## Exploiting JWT Standard Claims

Several standard claims might be leveraged by an attacker to exploit JWTs

### jwk claim

{% hint style="info" %}
jwk contains information about the public key verification for asymmetric JWTs
{% endhint %}

If the web application is misconfigured to accept arbitrary keys provided in the `jwk` claim, **you can forge a JWT, sign it with your private key, and then provide the corresponding public key in the `jwk` claim** for the web application to verify the signature and accept the JWT.

To do that, first generate your keys using

```bash
openssl genpkey -algorithm RSA -out exploit_private.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in exploit_private.pem -out exploit_public.pem
```

Then, you can manually sign the new JWT using Cyberchef, or use the following script to generate the JWT (note: edit your payload accordingly)

{% code overflow="wrap" lineNumbers="true" %}

```python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from jose import jwk
import jwt

# JWT Payload
jwt_payload = {'parameter1': 'value1', 'parameter2': 'value2'}

# convert PEM to JWK
with open('exploit_public.pem', 'rb') as f:
    public_key_pem = f.read()
public_key = serialization.load_pem_public_key(public_key_pem, backend=default_backend())
jwk_key = jwk.construct(public_key, algorithm='RS256')
jwk_dict = jwk_key.to_dict()

# forge JWT
with open('exploit_private.pem', 'rb') as f:
    private_key_pem = f.read()
token = jwt.encode(jwt_payload, private_key_pem, algorithm='RS256', headers={'jwk': jwk_dict})

print(token)
```

{% endcode %}

Then run:

```bash
pip3 install pyjwt cryptography python-jose
python3 exploit.py
```

***

### jku claim

{% hint style="info" %}
jku is similar to the jwk claim: it holds a URL that serves the key details rather than holding them directly.&#x20;
{% endhint %}

When a web application does not correctly check this claim, it can be exploited using a nearly identical process to the jwk claim: instead of embedding the key details into it, the attacker hosts the key details on their web server and sets the JWT's jku claim to the corresponding URL.

{% hint style="danger" %}
It is crucial that the `kid` in the JWT matches exactly that of the public key exposed on the server, so that the server uses the correct key in the JWK Set
{% endhint %}

<mark style="color:$danger;">Also, the jwk claim can be exploited for blind GET based SSRF attacks!</mark>

***

### kid claim

{% hint style="info" %}
The kid claim tells the server which public key to use to verify the JWT's signature by specifying the key's identifier. The web application will then look for a matching key in its key store.
{% endhint %}

Depending on how the server manages the value of the kid and checks for a corresponding key, injection vulnerabilities such as SQLi or Path traversal can occur.

If the kid allows a path traversal vulnerability, it is possible to arbitrarily edit a JWT by making the kid point to a file whose contents are known, then sign the JWT with a symmetric key whose value corresponds to the contents of this file.

The easiest idea is to redirect the kid claim's value to the `/dev/null` file: since this file is empty, the attacker can create a symmetric key whose value is an empty character string. Since the kid points to an empty value, the attacker can modify the JWT as he wishes and sign it with his empty symmetric key. Since the `/dev/null` file (and therefore the symmetric key) has an empty value, the JWT’s signature will be valid.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://notes.sfoffo.com/web-applications/web-attacks/json-web-tokens-jwts.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
