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
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 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.
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).
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:
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
- 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 Unauthorizedas the current user does not have permission to access the
2. Change the value of
alg parameter to
3. Delete the signature portion, but keep the dot, and repeat the request with the modified 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.
- 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.
hashcat -a 0 -m 16500 jwt.txt wordlist.txt
john jwt.txt --wordlist=wordlists.txt --format=HMAC-SHA256
2. Verify if the
key is correct. You can use jwt.io for it.
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.
alg header parameter is mandatory; JWT usually contains several other header parameters. Some of them are
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:
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 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
/dev/null to bypass authentication.
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
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!