JWT Security

Quick Wins

  • Run jwt_tool with mode All Tests! and wait for green lines

    python3 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] -T

      • Follow 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):

    1. 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

    2. python3 jwt_tool.py JWT_HERE -X k -pk my_public.pem

JWT Header Parameter Injection

  • **jwk parameter

    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.

    JWKS Injection (CVE-2018-0114):

    $ python3 jwt_tool.py JWT_HERE -X i

    • If page returns valid then you have a bypass - go tampering.

    Untitled
  • **jku parameter

    jku stands for JWK Set URL. If the token uses a “jkuHeader 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.

  • kid parameter

    • Reveal 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/12345 and /key/12345.pem on the web root.

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 x5c header 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 a cty header to change the content type to text/xml or application/x-java-serialized-object, which can potentially enable new vectors for XXE and deserialization attacks

  • kid (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 matching kid parameter.

Algorithm confusion attacks

Symmetric vs asymmetric algorithms

Algorithm
Key used to sign
Key used to verify

Asymmetric (RSA)

Private key

Public key

Symmetric (HMAC)

Shared secret

Shared secret

  • 'none' Algorithm (CVE-2015-9235):

  • JWT authentication bypass via algorithm confusion

    1. go to the standard endpoint /jwks.json OR /.well-known/jwks.json and observe that the server exposes a JWK Set containing a single public key.

    2. Copy the JWK object from inside the keys array. Make sure that you don't accidentally copy any characters from the surrounding array

    3. go to the JWT Editor Keys tab in Burp's main tab bar, Click New RSA Key.

    4. Right-click on the entry for the key that you just created, then select Copy Public Key as PEM.

    5. Use the Decoder tab to Base64 encode this PEM key, then copy the resulting string.

    6. Click New Symmetric Key. In the dialog, click Generate to generate a new key in JWK format

    7. Replace the generated value for the k property with a Base64-encoded PEM that you just created

    8. In the header of the JWT, change the value of the alg parameter to HS256 .

    9. In the payload, change the value of the sub claim to administrator.

    10. 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

    1. Obtain two JWTs generated by the server [ login → copy jwt → logout → login again → copy another jwt ]

    2. 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.py

    3. In Burp, go to the JWT Editor Keys tab and click New Symmetric Key,

    4. Replace the generated value for the k property 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.

    5. 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?