JSON Web Tokens (JWT) are basically a way to securely transfer information (A.K.A claims) in JSON format between two parties. It is widely used for authentication and session handling in modern web applications. Unlike other session tokens, all the information a server would require is included in the JWT token itself, which makes it ideal for modern web applications with multiple back-end servers.

JWT Format in Brief

You can easily identify a JWT token when you see one, as they are generally passed in an Authorization header in xxx.yyy.zzz format.

Example of a JWT token format 

The three parts separated by dots are Header, Payload, and Signature. The header and payload are just JSON data encoded in base64URL. The header usually contains metadata about the token, like the token type and algorithm used to sign the token.

The header is the initial part of the JWT token. It usually differs from application to application, as it is upon the developers to decide. Only the alg parameter is mandatory according to JWS specifications.

Payload

Payload is the second part of the JWT token that contains information about an entity (usually user data). Since the JSON data are simply base64URL encoded, they can be decoded to reveal the underlying information.

JWT token decoded

Signature

Finally, the signature is the third part of the token, separated by a dot. A signature is created by hashing the encoded value of the first two parts, which is then signed using a secret or private key. As the hash is generated using the information from the token itself, any modification to the token will result in a mismatch of hashes. So, even if the first two parts of the token can be viewed and edited by any user, it still is tamper-proof (in theory).

Attacking JWT

When testing for JWT, our main goal is usually bypassing the signature mechanism. Since the signature is the only thing preventing users from tampering with the token, incorrect implementation of signature verification is a severe security flaw as it may lead to vulnerabilities such as privilege escalation, account takeover, and more. Some of the ways to bypass these verification checks are:

Immortal token

Check if the token expires or not. Generally, the exp parameter in the payload states the valid period of the token. Some servers might still accept the token even past its expiry. Save the token and repeat the request after expiry to test whether the server rejects the token.

Lack of validation

In some rare cases, the server does not verify the signature. Try tampering with the data without modifying the signature and see if the server rejects the token.

"ALG" : "None" attack

As stated above, the JWT header contains alg parameter, which states the algorithm being used to sign the token. Some servers might be configured incorrectly and accept unsecured JWT, i.e., unsigned tokens. To check for this misconfiguration, simply change the value of jwk parameter to none.

Demo:

  1. Get the token and try to access the endpoint restricted to the current user. As you can see in the image below, the server responds with 401 Unauthorized as the current user does not have permission to access the /admin endpoint.
Accessing the forbidden resources

2. Change the value of alg parameter to none and sub to administrator.

Changing values

3. Delete the signature portion, but keep the dot, and repeat the request with the modified token.

Repeating the request with a modified JWT token

If the server does not reject unsigned tokens, the client-side user can simply bypass any authorization measures.

Weak HMAC Secret

If the token is being signed with HMAC, you might be able to brute force the secret key itself. Although rare, there are some cases where the default key was used to sign the token.

As reported on HackerOne, Mail.ru, a large email provider, used the key secret to sign the token.

DEMO:

  1. Get the token.

Let's try to brute force the secret. Hashcat and John the ripper support brute-forcing JWT tokens out of the box.

Commands:
hashcat -a 0 -m 16500 jwt.txt wordlist.txt
john jwt.txt --wordlist=wordlists.txt --format=HMAC-SHA256
ℹ️
You can get the wordlist of common secrets here.
Using John the ripper to brute force the secret key

2. Verify if the key is correct. You can use jwt.io for it.

Verifying secret using Burp extension.

You can simply edit any value on the payload and sign it using the secret key you cracked.

Algorithm Confusion Attack

JWT supports various algorithms (symmetric and asymmetric) that can be used to sign the token. In symmetric, the same key is used to sign and verify the data, whereas in asymmetric, the data is encrypted using the private key and then verified using a public key. The server might be accepting both symmetric and asymmetric keys to verify the token, which, if not configured properly, might result in various vulnerabilities.

According to CVE-2016-10555, even if the server sends a signed token using the RSA algorithm and is expecting RSA, the attacker could simply change the alg parameter to HMAC i.e HS256, and sign the token using the public key. The server would then accept the modified token as valid since it will think the public key is an HMAC private key.

ℹ️
jitsi-meet (<2.0.5963), an open-source project, was also vulnerable to this particular exploit. You can read the report on HackerOne.

Injection Attacks

Although only alg header parameter is mandatory; JWT usually contains several other header parameters. Some of them are jwk, jku and kid. About these headers in brief:

  • jwk (JSON Web Key) - is used to embed the public key within the token itself.
  • jku (JSON Web Key Set URL) - is used to reference the URL where the server can fetch the relevant public key.
  • kid (Key ID) - is used to identify the correct key in case there are multiple keys to choose from.

These headings introduce a few other vulnerabilities if not configured properly. You can check for such misconfiguration by:

CVE-2018-0114 (jwk header injection)

According to this vulnerability disclosure, if a vulnerable server sends an RSA-signed token with an embedded public key, the attacker can simply strip and replace the embedded public key with another generated by the attacker and sign it with the corresponding private key. The server will accept that modified token if not configured properly.

jku header injection

This attack method involves signing the token with a self-generated private key and changing the value of jku parameter to reference the domain controlled by the attacker where the public key(in jwk format) will be hosted. If vulnerable, the server will accept such signed tokens even though it should only accept them from trusted domains.

kid injection

Misconfigured kid parameters can be leveraged to perform attacks such as Path Traversal, SQLi, and sometimes even RCE. If vulnerable to path traversal, you can simply forge the token using a symmetric key with a secret 2 or an empty string and point the path to /proc/sys/kernel/randomize_va_space or /dev/null to bypass authentication.

Tools

Most of these vulnerabilities can be done manually without using any tools. However, using some of them is still a good idea to save time and labour.

  • Burp with JWT Editor extension (used in most of my demos)
  • JWT_TOOL by ticarpi
  • JWT_Forgery by silentsignal
⁉️
Stay cautious of the dangers when using third-party tools. As the tools are open-source, you can audit them to ensure they are safe. Besides, ensure you're permitted before you run these tools on any site or conduct any testing.

These are a few methods to test some security JWT misconfigurations. Stay tuned to learn more about testing applications for security misconfigurations.

Subscribe and leave a comment below if you encounter any difficulties in the process!