JWT Security
Quick Wins
Run jwt_tool with mode
All Tests!and wait for green linespython3 jwt_tool.py -M at \\ -t "<https://api.example.com/api/v1/user/76bab5dd-9307-ab04-8123-fda81234245>" \\ -rh "Authorization: Bearer eyJhbG...<JWT Token>"If you are lucky the tool will find some case where the web application is incorrectly checking the JWT:

Untitled Then, you can search the request in your proxy or dump the used JWT for that request using jwt_ tool:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"Required?
Remove the token from the request and observe the result - has the result changed?
Was the token required?
Yes - Good! on to the next step
No - perhaps the JWT isn't the means of authorisation on this app. Check for other headers, cookies or POST data that might be persisting the session. You may still be able to something with the token, so keep going.
Checked?
Delete the last few characters of the signature. Does it: return an error, fail, or succeed?
Is the token checked?
If an error message occurs the signature is being checked - read any verbose error info that might leak something sensitive.
If the page returned is different the signature is being checked.
If the page is the same then the signature is not being checked - time to start tampering the Payload claims to see what you can do!
Persistent?
Resend the same token multiple times, interspersed with sending no token, or one with an invalid signature (delete a character or two from the end of the token). Does it continue to work each time the valid token is sent?
Is the token persistent?
Yes - the token stays static, which is common behaviour. However, this may indicate an immortal token if the same JWT is valid after logout, or after a very long duration. Be sure to retest this same token in ~24 hours and report it if it never expires.
No - the token has either expired, or has been invalidated by the application. Some systems invalidate tokens every so often and either just send you a new token in a normal HTTP response, or may programmatically call a "refresh token" API endpoint to retrieve a new token. This may mean you need to switch out your base test token every so often, so keep re-checking it before sending a tampered token.
Origin
Check where the token originated in your proxy's request history. It should be created on the server, not the client.
If it was first seen coming from the client-side then the key is accessible to client-side code - seek it out!
If it was first seen coming from the server then all is well.
Check claim-processing order
Alter any Payload claims that are directly reflected or processed on the page, but leave the signature the same. Did the altered values get processed?
Example: If the Payload contains a profile image URL or some text
(e.g. {"login": "ticarpi", "image": "https://ticarpi.com/profile.jpg", "about": "Hello this is my profile page."})
then tweak the address to see if a new image is reflected in the page, or the text to see if that is altered in the response.
Tampering in jwt_tool: Enter tamper mode:
python3 jwt_tool.py [token] -TFollow the menu to tamper various claims
(Optionally) set signing or exploit options via the X or S arguments
If the changes are accepted then the application is processing these before (or regardless of) signature verification. Look to see if you can tamper anything crucial.
If the changes aren't reflected then the JWT claims are being processed in the correct order.
Sensitive Data Exposure
Exploiting flawed JWT signature verification
Accepting arbitrary signatures
null signature (CVE-2020-28042):
Many JWT libraries provide one method to decode the token and another to verify it:
decode(): Only decodes the token from base64url encoding without verifying the signature.verify(): Decodes the token and verifies the signature.
Sometimes developers might mix up these methods. In that case, the signature is never verified and the application will accept any token (in a valid format). Developers might also disable signature verification for testing and then forget to re-enable it. Such mistakes could lead to arbitrary account access or privilege escalation.
Secret Keys Flaws
Crack HMAC secret Key
If you can crack the HMAC secret then you can forge anything you like in the token. This could be a
**critical**vulnerability.RSA Key Confusion (CVE-2016-5431):
Find the Public Key → Finding Public Keys · ticarpi/jwt_tool Wiki (github.com)
You will also need to use the right format of the Public Key. If the key is provided then you're fine, if not then best guess is PEM format
Note that PEM should contain a single newline character at the end, however some tools may miss this off when exporting a key.
Use jwt_tool's -V flag alongside the -pk public.pem argument to verify that the Public Key you found matches the key used to sign the token
python3 jwt_tool.py JWT_HERE -X k -pk my_public.pem
JWT Header Parameter Injection
**
jwkparameterWith the extension loaded, in Burp's main tab bar, go to the JWT Editor Keys tab.
Send a request containing a JWT to Burp Repeater.
In the message editor, switch to the extension-generated JSON Web Token tab and modify the token's payload however you like.
Click Attack, then select Embedded JWK. When prompted, select your newly generated RSA key.
Send the request to test how the server responds.
JWKS Injection (CVE-2018-0114):
$ python3 jwt_tool.py JWT_HERE -X iIf page returns valid then you have a bypass - go tampering.

Untitled **
jkuparameterjku stands for JWK Set URL. If the token uses a “jku” Header claim then check out the provided URL. This should point to a URL containing the JWKS file that holds the Public Key for verifying the token. Tamper the token to point the jku value to a web service you can monitor traffic for.
kidparameterReveal key
If the claim "
kid" is used in the header, check the web directory for that file or a variation of it. For example if"kid":"key/12345"then look for/key/12345and/key/12345.pemon the web root.
Other Headers Attacks | JWT Vulnerabilities (Json Web Tokens) - HackTricks
According to the JWS specification, only the alg header parameter is mandatory. In practice, however, JWT headers (also known as JOSE headers) often contain several other parameters. The following ones are of particular interest to attackers.
jwk(JSON Web Key) - Provides an embedded JSON object representing the key.jku(JSON Web Key Set URL) - Provides a URL from which servers can fetch a set of keys containing the correct key.x5u and x5cheader parameter - The x5u and x5c header arguments, like the jku and jwk headers, allow attackers to define the public key certificate or certificate chain used to verify the token. x5u defines information in URI form, whereas x5c permits certificate data to be incorporated in the token. Details of these attacks are beyond the scope of these materials, but for more details, check out CVE-2017-2800 and CVE-2018-2633.x5t parameter- The "x5t" (x.509 certificate thumbprint) header argument returns a base64url encoded SHA-256 thumbprint (i.e., digest) of an X.509 certificate's DER encoding, which may be used to match a certificate. As a result, it is equivalent to the key identifier or the kid claim!!cty(Content Type) - Sometimes used to declare a media type for the content in the JWT payload. This is usually omitted from the header, but the underlying parsing library may support it anyway. If you have found a way to bypass signature verification, you can try injecting actyheader to change the content type totext/xmlorapplication/x-java-serialized-object, which can potentially enable new vectors for XXE and deserialization attackskid(Key ID) - Provides an ID that servers can use to identify the correct key in cases where there are multiple keys to choose from. Depending on the format of the key, this may have a matchingkidparameter.
Algorithm confusion attacks
Symmetric vs asymmetric algorithms
Asymmetric (RSA)
Private key
Public key
Symmetric (HMAC)
Shared secret
Shared secret
'none' Algorithm (CVE-2015-9235):
JWT authentication bypass via algorithm confusion
go to the standard endpoint
/jwks.jsonOR/.well-known/jwks.jsonand observe that the server exposes a JWK Set containing a single public key.Copy the JWK object from inside the
keysarray. Make sure that you don't accidentally copy any characters from the surrounding arraygo to the JWT Editor Keys tab in Burp's main tab bar, Click New RSA Key.
Right-click on the entry for the key that you just created, then select Copy Public Key as PEM.
Use the Decoder tab to Base64 encode this PEM key, then copy the resulting string.
Click New Symmetric Key. In the dialog, click Generate to generate a new key in JWK format
Replace the generated value for the k property with a Base64-encoded PEM that you just created
In the header of the JWT, change the value of the
algparameter toHS256.In the payload, change the value of the
subclaim toadministrator.At the bottom of the tab, click Sign, then select the symmetric key that you generated in the previous section.
JWT authentication bypass via algorithm confusion with no exposed key
Obtain two JWTs generated by the server [ login → copy jwt → logout → login again → copy another jwt ]
Brute-force the server's public key [
docker run --rm -it portswigger/sig2n <token1> <token2>] https://github.com/silentsignal/rsa_sign2n/blob/release/standalone/jwt_forgery.pyIn Burp, go to the JWT Editor Keys tab and click New Symmetric Key,
Replace the generated value for the
kproperty with a Base64-encoded key that you just copied. Note that this should be the actual key, not the tampered JWT that you used in the previous section.Switch to the extension-generated JSON Web Token tab.
Change algorithm from RS256 to HS256
The algorithm HS256 uses the secret key to sign and verify each message. The algorithm RS256 uses the private key to sign the message and uses the public key for authentication.
If you change the algorithm from RS256 to HS256, the back end code uses the public key as the secret key and then uses the HS256 algorithm to verify the signature.
Then, using the public key and changing RS256 to HS256 we could create a valid signature. You can retrieve the certificate of the web server executing this:
Miscellaneous attacks
JWT Expire Abuse
Duration → Check if the token lasts more than 24h... maybe it never expires. If there is a "
exp" filed, check if the server is correctly handling it.Cross-service relay attacks
JWT ID
Other Attacks
Usage of Example JWT Tokens
However, some JS files were accessible without authentication. Testing revealed that the application used JWT tokens that were sent via the Microsoft SSO system after a secure login.
On the back-end mechanism, there was a security misconfiguration that didn’t check if the JWT token was generated for that specific application–instead, it accepted any JWT token that had a valid signature. So, using an example JWT token from Microsoft’s website:
Within generic values:
It was possible to access the internal endpoints, leaking the company data.
ATO from IDOR
Key Database Mismanagement
Hacking JWT Tokens: Key Database Mismanagement
Verification Key Mismanagement
Hacking JWT Tokens: Verification Key Mismanagement
Hacking JWT Tokens: Verification Key Mismanagement II
Hacking JWT Tokens: Verification Key Mismanagement III
Hacking JWT Tokens: Verification Key Mismanagement IV
Vulnerable Key Generator
Hacking JWT Tokens: Vulnerable Key Generator
Transaction Replay
Hacking JWT Tokens: Transaction Replay
Hacking JWT Tokens: Transaction Replay II
JWS Standard for JWT
Hacking JWT Tokens: JWS Standard for JWT
Hacking JWT Tokens: JWS Standard for JWT II
Bypassing NBF Claim
Hacking JWT Tokens: Bypassing NBF Claim
Special Version Claim
Hacking JWT Tokens: Special Version Claim
Cross Service Relay Attack — Missing audience claim
Hacking JWT Tokens: Cross Service Relay Attack - Missing audience claim
Cross Service Relay Attack — Misconfigured audience claim
Hacking JWT Tokens: Cross Service Relay Attack - Misconfigured audience claim
Client Side Token Decode
Hacking JWT Tokens: Client Side Token Decode
Usage of Example JWT Tokens
However, some JS files were accessible without authentication. Testing revealed that the application used JWT tokens that were sent via the Microsoft SSO system after a secure login.
On the back-end mechanism, there was a security misconfiguration that didn’t check if the JWT token was generated for that specific application–instead, it accepted any JWT token that had a valid signature. So, using an example JWT token from Microsoft’s website:
Within generic values:
It was possible to access the internal endpoints, leaking the company data.
ATO from IDOR
Last updated
Was this helpful?