Skip to Main Content
All Trimarc services are now delivered through TrustedSec! Learn more
February 05, 2026

Keys to JWT Assessments - From a Cheat Sheet to a Deep Dive

Written by Aaron James
Application Security Assessment

The Cheat Sheet section is for quick reference.

The Learn section is for those who have never touched the topic before.

The Implement section is for more detailed descriptions of each Cheat Sheet item.

Cheat Sheet

Session Management Cheat Sheet - JWT

How are sessions managed?

Bare minimum token(s) needed for sensitive request + find a test page

JWT regex

[= ]eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9._-]*

Gather Information

Review token contents with JSON Web Tokens' request/response tab

Look for: exp, alg

Any other interesting claims e.g., ‘password’, ‘role’, etc.

Required?

What resources require the JWT?

Signature Validation

Remove or modify last few characters from signature

[HS256] Weak HMAC Secret

Identify:

  • jwt_tool.py <JWT> -C -d <DICTIONARY.txt>
  • hashcat -a 0 -m 16500 jwt.txt wordlist.txt -r <path to hashcat>/rules/best64.rule

Exploit:

JWT Editor Extension Tab:

  • New Symmetric Key > Specify secret (paste discovered secret) > Provide ID > Generate > OK

JWT Editor Repeater Tab:

  • Modify claims
  • Sign > Select newly created key > OK

Signature Exclusion ('none' Algorithm)

Manually: Set alg to none, remove signature

JWT Editor Repeater Tab:

  • Attack > 'none' Signing Algorithm > select a variation > OK
  • Repeat for each variation

[RS256] Key Confusion

Identify public key (e.g., /.well-known/jwks.json)

JWT Editor Extension Tab:

  • New RSA Key > Format as [JWK or PEM] > Provide an ID > Paste [JWK or PEM] key > OK

JWT Editor Repeater Tab:

  • Attack > HMAC Key Confusion Attack > Select new RSA key > OK

In the event of failure, convert key format and repeat

Injecting jwk

JWT Editor Extension Tab:

  • New RSA Key > Format as JWK > Generate > OK

JWT Editor Repeater Tab:

  • Attack > Embedded JWK > Select generated key > OK

Injecting jku/x5u

JWT Editor Repeater Tab:

  • Attack > Embed collaborator payload > select variation [jku, x5u] > OK
  • Review Collaborator logs

Exploit:

  • Host a public JWKS (jku) or PEM (x5u) and point JWT header parameter to it

Injecting kid Values

JWT Editor Extension Tab:

  • New Symmetric Key > Generate
  • Replace k value with "AA==" (Base64-encoded null byte) > OK

JWT Editor Repeater Tab:

  • Replace kid parameter value with "../../../../../../../dev/null"
  • Sign > Select HS256 Key > Don't modify header > OK

New to JWTs? Learn Here.

JWT stands for JSON Web Token and a token of this format might look similar to the following:

Technically speaking, JWT is an umbrella term that includes two types of tokens, JSON Web Signature (JWS) and JSON Web Encryption (JWE). JWEs are encrypted and signed to prevent tampering and prevent reading of the embedded values. JWS on the other hand is only signed to prevent tampering but can be decoded to view the plaintext values held by the token. By far, the most common way JWTs are used is in the form of a JWS, the signed version that can be decoded. The distinction between JWT and JWS is not often made, with JWEs being somewhat rare. This post will likewise use the JWT and JWS terms interchangeably.

The token is made up of three distinct parts, all separated by a period: the header, the payload, and the signature.

These parts are Base64-encoded key-value pairs (called 'claims' in the payload, 'parameters' when in the header) represented in JSON format that contain useful information. The header includes parameters related to the cryptography used to sign the token. In this example, the alg claim says the signature will be based on the Hash-Based Message Authentication Code (HMAC) SHA-256 algorithm.

The payload contains the useful information the token is trying to convey. Here, some details are included to identify the user, when the token was issued, and when it expires. However, a JWT could contain a variety of other information the application might want to leverage when dealing with individual users.

Lastly, the signature is the product of a cryptographic operation on the header and payload and is used to ensure the data has not changed since the token was issued. The signature, again, is created using the algorithm specified in the header. Tokens are signed using symmetric algorithms, where one is used to sign and verify the token, and the other is asymmetric algorithms, which use a private key to sign the token and a public key to verify the token.

Here’s a view of the Base64-encoded value and the decoded values from https://jwt.io:

Figure 1 - Decoded Token via jwt.io

Here’s a view of that same JWT being used in an HTTP request, which is how JWTs will likely be handled by a penetration tester:

Figure 2 - JWT in HTTP Request

What are they used for? As in the screenshot above, web applications often use JWTs for authorization and stateless session management. Stateful session management and why it matters have been discussed in greater detail in our previous blog about cookies, but many of core principles also apply to stateless session management using JWTs. 

Stateless session management serves the same purpose as more traditional stateful session management: identify a user and determine if they should have access to a web resource. However, with stateless session management, the server doesn’t have to keep track of user sessions. Instead, the token is created and cryptographically signed, given to the user, and from then on it is implicitly trusted (assuming the signature is validated by the server, which ensures the token hasn’t been changed at all).

JWTs are also small and self-contained strings, which is beneficial from a performance perspective because they can be sent with little data overhead. They also include everything that needs to be communicated inside them. This means no back-end communication or database interactions are required. 

Another benefit is the ability for distributed systems to be able to leverage the token. If those systems can properly validate the signature, they will not need to communicate with a centralized resource.

The theme for JWT advantages is 'less work and faster.'

However, there are trade-offs for these benefits. Once a token is issued, it generally cannot be revoked. The server that receives it will continue to consider it valid because the token itself, as discussed, is implicitly trusted if the signature passes validation. They do, however, have expiration claims that define a timeframe the token is valid for, and after that expiration the JWT should no longer be accepted. Keeping these expiration timeframes relatively limited does help, but if a user logs out or access is changed, there will likely be a period where the token will continue to be valid before expiry. 

Also, there are fewer built-in protections against theft when compared to cookie security attributes like Secure and HttpOnly. If JWTs are stored insecurely, like in the browser’s Local Storage or Session Storage, they are as vulnerable to theft through JavaScript attacks as a session cookie without HttpOnly set.

There are a lot of excellent resources available to learn more about JWTs and how to attack them. Some good options are:

Detailed Implementation of JWT Attacks

The goal of attacks against JWTs is usually to modify the claims within the token payload to values that grant additional access or privileges. However, effective testing of JWTs requires the right tools.

Within Burp Suite there are several extensions that streamline the various tests, with the JWT Editor extension seeming to be the most fully featured. The walkthroughs here will mostly leverage that extension, but consider using the others if you see fit.

Other than these, JWT_Tool has many of the same features as well as walkthroughs and guidance on testing JWTs. You can find the Attack Methodology here.

To practice these attacks, I've written a vulnerable application that implements the various vulnerabilities through command line flags. That code is found on GitHub with installation instructions.

Other attacks against vulnerable applications can be practiced through PortSwigger Web Security Academy labs that demonstrate these issues, as well as intentionally vulnerable web applications. For instance, OWASP’s Juice Shop also uses JWTs.

Determine Session Management Mechanism/Identify JWT

When beginning any session management testing, you need to identify which token(s)/cookie(s)/header(s) are required to make an authorized request. Find an application request that returns sensitive information, particularly if that request is specific to a user, like profile data. Remove cookies or tokens one at a time until access is denied. Skip over this value and continue the process until the request is narrowed down to the required tokens or values.

JWTs are very often sent as Bearer tokens in the authorization header of a request but may show up elsewhere in headers or cookies as well. Running certain Burp Suite extensions will also highlight requests that contain a JWT.

Figure 3 - JWT Editor Highlights JWTs in Use

When extensions are not run or fail to identify a token, a regex search in Burp Suite will return all requests that contain JWTs, depending on your other search criteria. Go to Target > Site map > right-click target host > Engagement tools > Search. Check the 'regex' box in search options and include the desired regex in the search box.

Figure 4 - regex Search Returns Requests With JWTs

Configuring the search for just response headers will probably show you only JWTs being issued, so tailor the criteria to your needs. Some regex to try:

  • [= ]eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9._-]*
  • eyJ[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+
  • [= ]eyJ[A-Za-z0-9_\/+-]*\.[A-Za-z0-9._\/+-]*

Information Gathering

Decode the JWT to review claims and parameters to identify any useful or interesting information. Tokens can be Base64 decoded to reveal the contents, but there are convenient options to make them easily readable.

The JWT Editor extension allow tokens to be decoded in a dedicated request or response tab:

Figure 5 - JWT Editor Request Tab

JWT_Tool can do the same thing, and the syntax is simple:

python3 jwt_tool.py &lt;PUT YOUR TOKEN HERE&gt;
Figure 6 - JWT_Tool Decoding a JWT

Collect what information you can from the decoded token. The question with session management is always going to be how does the application know who I am or that I’m allowed to access this? That information should be in the JWT, and it is what we are going to try to subvert. Ultimately, we’d like to forge a token, and looking at the above token, id, username, email, and role could be lucrative claims to target.

Good information to collect at this stage is also how long the token will remain valid, and that is typically in the exp claim. The token above, issued by the OWASP Juice Shop application token, doesn’t have an exp claim, which may mean the token never expires.

Also take note of the alg claim; this is the algorithm used to sign the token and will inform and help shape future attack attempts. Here it is as RS256, an asymmetric algorithm.

Even if the JWT implementation ends up being secure, the information in the token may still be useful elsewhere during testing. For instance, this Juice Shop token has some interesting-looking claims, like password and totpSecret at a minimum, and those may end up contributing to attacks that are not specifically tied to session management.

Required

To begin active testing, we want to know if the token is actually required to gain access to sensitive resources, and what resources specifically. For instance, is it possible the JWT is only used for API endpoints and other application resources are accessed with a separate cookie value? 

Occasionally a token may be repeated. In the case of Juice Shop, it is the token cookie value and not the authorization header value that is used. Failing to recognize this will confuse testing, so ensure the correct token is targeted.

Figure 7 - Which Token is Used?

Signature Validation

The JWT signature verifies that the contents of the token have not been modified from the time the token was issued. The server must validate the signature before processing any claims within the token payload to ensure the claims remain valid. If the server fails to validate the token signature, claims can be arbitrarily modified, and the application will be vulnerable to maliciously crafted tokens that allow unauthorized access to resources.

To test this, the last few characters from the JWT signature can be deleted and sent to the server in an otherwise normal request. If the response content is different or an explicit error is returned, the signature is being validated.

In some cases, signatures are validated for length, not content, so modifying the last few characters within the Base64 character set instead of deleting them may work as well. However, if the response content is identical to previous requests with the valid signature, then the signature is not validated. At this point claims can be modified within the token arbitrarily to exploit the application.

At a high-level, these are steps to test signature validation:

Signature Validation Testing

Goal:

Verify whether the server validates JWT signatures

Step 1:

Remove or modify the last several characters from the token signature

Step 2:

Send token with malformed signature and verify response content

Success:

Server allows access to sensitive resources with forged token

Failure:

Server rejects forged token, often with explicit 401 Unauthorized response code

Exploit:

Modify available claims to gain illicit access

The companion code by default will correctly validate the JWT signature. Any modifications will result in a 401 Unauthorized response.

Figure 8 - Signatures Correctly Validated (Test Failure)

However, running the companion app with the --disable-validation flag will skip signature validation steps. The last several characters were modified arbitrarily within a Repeater request and the request was sent to the server, resulting in access to protected data.

Figure 9 - Arbitrarily Modified Token

Exploitation

This tells us the token signature is not being validated, yet it is still accepted, as evidenced by the valid data being returned. A simple way to exploit this further is to intercept a request or send to Repeater and edit claims directly in JWT's tab when an extension like JWT Editor is loaded. Claims can be quickly modified in this tab and changes applied before sending the request.

Figure 10 - Quickly Editing Claims in JWT Editor

Without validation the claims can be modified at will to impersonate other users and gain unauthorized access to administrative functionality or data.

Weak HMAC Secret

This testing is valid for HMAC-based signatures (HS256, HS384, HS512). Skip this if the alg claim starts with RS.

JWT signatures can be generated in two broadly distinct ways, with asymmetric or symmetric keys. When using asymmetric keys, a secret private key is used to create the signature, and a public key can be used to validate the signature. However, symmetric algorithms like HS256 create a signature using a single key, and that same key is used to validate the signature. From an attacker’s perspective, this is useful because many guesses can be made offline, meaning no requests are sent anywhere. With enough time and resources, the secret key could be guessed if it is weak. If an attacker is successful and a key is guessed, arbitrary tokens can be created and signed with that key, and the receiving server will believe the JWTs are valid.

Weak HMAC Secret Brute-Force Steps

Goal:

Identify weak HMAC secret using brute-force cracking techniques

Step 1:

Confirm alg is HMAC-based (HS256, HS384, HS512)

Step 2:

Provide token to cracking software (JWT_Tool, hashcat) and review results

Success:

Brute-force cracking tool reports secret key

Failure:

Brute-force cracking tool fails to identify secret key

Exploit:

JWT Editor Extension Tab:

  • New Symmetric Key > Specify secret (paste discovered secret) > Provide ID > Generate > OK

JWT Editor Repeater Tab:

  • Modify claims
  • Sign > Select newly created key > OK
  • Send

Running the companion app with the --weak-secret flag will sign tokens with a very weak HMAC secret. For this type of testing, confirm symmetric keys are in use. The alg claim will be HS256, HS384, or HS512 (other algorithms will use asymmetric public/private keys). Like this:

Try to brute-force the HMAC secret with JWT_Tool with a dictionary file. JWT_Tool has a few lists of common keys included on GitHub, and these may be expanded as you see fit.

python3 jwt_tool.py &lt;PUT THE JWT HERE&gt; -C -d &lt;YOUR DICTIONARY FILE&gt;
Figure 11 - Nailed it.

Something similar can be done with hashcat:

hashcat -a 0 -m 16500 jwt.txt wordlist.txt
Figure 12 - hashcat Cracking JWT HMAC Key

If hashcat does not initially return the token's secret, consider running rule-based attacks as well. This attack mode will modify, cut, or extend the words in a provided list to create similar words, increasing the likelihood the secret key is found.

hashcat -a 0 -m 16500 jwt.txt wordlist.txt -r &lt;path to hashcat&gt;/rules/best64.rule

Password cracking is its own beast, and there is a lot to tinker with here, but best64.rule or rockyou.rule are good starting points for rule-based attacks.

Burp Suite is also doing some passive analysis to find weak keys, and that gets reported as a finding.

Figure 13 - Passive Burp Suite Finding

Exploitation

Within the JWT Editor extension tab, select New Symmetric Key, specify the secret that was cracked, name the key something sensible, and select Generate.

Figure 14 - Create a New Symmetric Key

With this key generated, the token can be modified JSON Web Token request tab. Re-sign the token by choosing Sign, select the sensibly named key, and click OK.

Figure 15 - Sign Request With Cracked Key

Now the request will be signed with the cracked key and be accepted as valid by the server.

Weak HMAC Secret Troubleshooting

If cracking software like JWT_Tool and hashcat cannot find the JWT secret, it may be because the secret is long and complex enough to make cracking infeasible with commonly available hardware. But before giving up on the effort, ensure the dictionary wordlist is large and you've applied some modifying rules to increase the likelihood of success.

Signature Exclusion

RFC 7519 includes a provision for ‘unsecured JWTs’ (https://datatracker.ietf.org/doc/html/rfc7519#section-6), tokens that are secured in some other way and do not need a cryptographic signature to ensure validity. Within the header of an unsecured JWT, the alg claim would have a value of none, and the JWT would have no signature.

This can introduce security issues because when a server receives a JWT, the server must first read the algorithm used to sign the token before the server can use that algorithm to validate the token. If the server accepts unsecured JWTs, an attacker can modify an otherwise legitimate JWT to be an unsecured JWT with arbitrary claims. 

To check if the server is improperly configured to accept the none algorithm, replace the alg parameter value with none and remove the signature from the JWT.

These are the high-level steps to perform a signature exclusion attack:

Signature Exclusion Attack

Goal:

Verify whether the server accepts unsecured JWTs (none algorithm)

Step 1:

Set alg parameter value to none

Step 2:

Remove signature

Step 3:

Send and review response

Success:

Server allows access to sensitive resources with forged token

Failure:

Server rejects forged token, often with explicit 401 Unauthorized response code

Exploit:

Modify available claims to gain illicit access

Running the companion app with the --allow-none flag will permit unsecured JWTs to be considered valid by the server.

OWASP’s Juice Shop is likewise vulnerable to signature exclusion attacks and can be used to demonstrate or practice the attack.

JWT Editor has built-in attacks for the none algorithm that can be launched in the JSON Web Token request tab. Click Attack > 'none' Signing Algorithm > select a version of 'none' > OK.

Figure 16 - 'none' Algorithm Signing Attack

There are multiple versions of 'none' in case the server accepts or prevents a specific case.

This can also be done manually by setting the alg claim to none and removing the signature altogether. Either way, the raw JWT will look like the one below. Notice the trailing period.

Figure 17 - 'none' and No Signature Accepted

If the server accepts one of these requests with the modified JWT, this means the server is accepting unsigned tokens that can be manipulated by the client. Claims can be modified arbitrarily, likely allowing unauthorized access to data or resources depending on what claims are available to be modified. These claims can be modified directly and sent to the server following the same steps used while identifying the vulnerability.

Signature Exclusion Troubleshooting

A common mistake during this testing is leaving off a trailing period that normally separates the payload from the signature. Even though there is no signature, the period is still required as a delimiter. A properly formatted token should look like this:

Additionally, the server may reject tokens using an explicit list of unacceptable algorithms, such as none. Varying the case of the spelling, such as None, NONE, or nOnE, may bypass this restriction.

Key Confusion

JWTs can be signed with algorithms that broadly fit into one of two categories—those that use symmetric keys and those that use asymmetric key pairs. Algorithms using a symmetric key use the same key to create the signature as well as verify signatures of received tokens, such as HS256. Algorithms that use asymmetric key pairs, on the other hand, use one key (a private key) to create the signature and another (a public key) to verify received tokens have not been tampered with, a common example being RS265.

If an application expects the RS256 algorithm to be the only algorithm in use, it may assume all tokens were signed with the private key, and so blindly checks the validity of all received tokens with the public key. This would work fine for legitimate tokens.

However, an attacker could create a new JWT and sign it with a symmetric key algorithm, like HS256, and use the server’s public key to do so. In such a scenario, when the server receives the token, the typical process of validating tokens will be followed, which is to use the public key to validate the token. In this attack, the JWT will be considered valid because the public key matches the key used by the symmetric key algorithm to sign the token.

To conduct a key confusion (or this can be called an algorithm confusion) attack, a public key needs to be found and then the RS256 token needs to be re-signed using that public key and a symmetric key algorithm like HS256.

Key Confusion Attack Steps

Goal:

Re-sign an RS256 token with HS256 and the public key

Step 1:

Identify public key, either in JWK or PEM format

Step 2:

JWT Editor Extension Tab:

  • New RSA Key > Format as [JWK or PEM] > Provide an ID > Paste [JWK or PEM] key > OK

Step 3:

JWT Editor Repeater Tab:

  • Attack > HMAC Key Confusion Attack > Select new RSA key > OK

Step 4:

Send and review response

Success:

Server allows access to sensitive resources with forged token

Failure:

Server rejects forged token, often with explicit 401 Unauthorized response code

Convert the discovered public key to the other format (either JWK or PEM) and repeat

Running the companion app with the --alg-confusion flag will permit/enable key/algorithm confusion attacks. 

The first step is to find a potential public key. One potential way of doing this is to look for common application paths that host public keys. Having public keys available allows distributed systems to validate the JWT as well.

Try identifying public keys through file and directory brute-forcing. Some common application paths that host public keys are:

  • /.well-known/jwks.json
  • /.well-known/keys
  • /.well-known/openid-configuration
  • /api/.well-known/jwks.json
  • /api/.well-known/openid-configuration
  • /api/keys
  • /api/v1/keys
  • /auth/.well-known/jwks.json
  • /auth/.well-known/openid-configuration
  • /auth/keys
  • /certs
  • /common/discovery/keys
  • /jwks
  • /jwks.json
  • /jwks_endpoint
  • /jwt.pem
  • /jwt.pub
  • /keys
  • /oauth2/jwks
  • /oauth2/keys
  • /oauth2/v1/keys
  • /openid-configuration
  • /openid/connect/jwks.json
  • /public-cert.pem
  • /public-keys
  • /public.pem
  • /public_keys
  • /publickey
  • /rsa/public.key
  • /v1/keys
Figure 18 - Public Key Discovered Through Brute-Force

Some other ideas may be to look at the token itself to see if there are jku or x5u header parameters containing URLs that point to the location of the public key. Otherwise, perhaps the tokens are signed with the web server's TLS private key and the X.509 certificate can be used as the public key. Read more about finding public keys here.

A quick note on keys is that they are generally formatted in one of two ways: JWKS or PEM. A JWKS is a JSON Web Key Set, and within that set is a JWK, a JSON Web Key. An example JWKS looks like the following, with the JWK highlighted in red:

I make the distinction between the JWKS and the JWK it contains because sometimes you need one, and other times you'll need the other, so it is important to pay attention.

A PEM formatted key will look like this:

Once a public key is found, it can be used to sign a forged token. However, the format of the key used to sign the token will need to be the same as the format of the key the server is using to validate the token. These formats will often be either JWKS or PEM, and because it won't be known what format the server is using, both can be tried.

In the case of the companion app, the JWKS is available at /.well-known/jwks.json. We can attempt to forge a token by creating a new RSA key with the JWK, changing the alg claim to HS256, and re-signing the token.

JWT Editor Extension Tab > New RSA Key > Format as JWK > Provide an ID > Paste key > Ok

In a JWT Editor Repeater tab, load the HMAC Key Confusion Attack.

JWT Editor Repeater Tab > Attack > HMAC Key Confusion Attack > Select new RSA key > OK

This, however, will result in a 401 Unauthorized failure because the server requires the token to be signed with the public key in PEM format instead. Converting between JWKS and PEM (or vice versa) is simple, and there are a variety of tools available to do so. A simple script can also be made to do the conversion.

Figure 19 - JWKS-to-PEM Conversion

Now create an RSA key again, this time in PEM format.

JWT Editor Tab > New RSA Key > Format as PEM > Provide an ID > Paste Key > OK

Figure 20 - Creating a New RSA Key in PEM Format

Now in the JSON Web Token request tab you can modify the JWT by selecting Attack > HMAC Key Confusion.

Figure 21 - Re-Signing JWT With Public RSA Key as Symmetric Key

Key Confusion Troubleshooting

If you're using a wordlist to scan for public key endpoints using Burp Suite Intruder, make sure it isn't URL-encoding characters you don't want it to, as it does that by default in Intruder. Also, when copying a JWKS into JWT Editor to make a new key, copy just the key object, not the key container.

Additionally, the key will have to match what the server is expecting exactly; any differences like trailing new lines will cause a failure. The HMAC Key Confusion Attack has an option to remove trailing new line characters, so try this or anything similar you can think of before moving on.

Injecting jwk Values

An attacker can create their own key pair and include the public key in a JWK in a token header within the aptly named jwk parameter. The token could then be signed with the attacker’s private key. When the server receives the token, it will verify the signature using the public key embedded in the jwk parameter and accept the token, and any claims within it, as valid.

jwk Injection Attack Steps

Goal:

Coerce server into accepting a self-signed JWT by including an attacker-controlled public key in the jwk header parameter

Step 1:

JWT Editor Extension Tab:

  • New RSA Key > Format as JWK > Generate > Ok

Step 2:

JWT Editor Repeater Tab:

  • Attack > Embedded JWK > Select generated key > OK

Step 3:

Send and review response

Success:

Server allows access to sensitive resources with forged token

Failure:

Server rejects forged token, often with explicit 401 Unauthorized response code

Exploit:

Modify available claims to gain illicit access

Running the companion app with the --jwk-injection flag will cause the server to accept a JWK in the JWT header as a valid public key.

The point of this attack is to sign a token with an attacker-controlled private key, then instruct the server to validate a token using the corresponding public key. The server is instructed to do this by providing a key in JWK format in the jwkheader claim.

Begin this attack by creating a new RSA key.

JWT Editor Extension Tab > New RSA Key > Format as JWK > Generate > Ok

Make a note of the ID that is automatically generated. Now within a JWT Editor request tab, embed the JWK.

Attack > Embedded JWK > Select generated key > OK

Figure 22 - Embedded JWK Used to Validate Token

Injecting jku and x5u Values

Other header parameters supported by JWTs are the jku and x5u parameters. These point to the locations of the public key and instruct the server to request those to validate the token. If these URLs are not strictly validated against a list of known-good URLs, an attacker may be able to entice an application into fetching an attacker-controlled public key. In that event, a token signed with the corresponding attacker-controlled private key would be considered valid.

jwk/x5u Injection Attack Steps

Goal:

Coerce server into accepting a self-signed JWT by including an attacker-controlled URL in the jwk or x5u header parameter that points to a public key.

Step 1:

JWT Editor Repeater Tab:

  • Attack > Embed Collaborator Payload > select jku or x5u > OK
  • (JWT header has been modified and now includes a Collaborator URL)

Step 2:

Send modified token to application server

Step 3:

Review Collaborator logs for request

Success:

Application server sends request for Collaborator URL

Failure:

Collaborator URL is not requested

Exploit:

Host a remote JWKS (jku) or PEM (x5u) and point the header parameter to that URL

Running the companion app with the --jku-injection or –x5u-injection flag will cause the server to request a valid public key from the provided URL.

Testing for this vulnerability is relatively simple with the JWT Editor extension. With a base request in Repeater, click Attack > Embed collaborator payload and send it. If this test causes the server to reach out to the Collaborator endpoint, it means the server is processing the jku or x5u header claim and reaching out for a key.

Figure 23 - Collaborator URL in jku Injected Into JWT Header
Figure 24 - Successful Collaborator Interaction

To exploit this flaw, a valid public key will need to be hosted in a network-accessible location, in either JWKS (jku) or PEM (x5u) format. Tokens will need to be signed using the corresponding private key. That complete process is skipped for brevity here, but the Collaborator test is sufficient to demonstrate the issue.

kid Attacks

The kid parameter in a JWT header specifies the ID of the key that should be used to verify the token signature. If this kid value is not validated and is used to load the key dynamically, several security issues may arise. In some cases, the kid value may be a filename, and modifying this value could lead to path traversal. JWTs signed with symmetric keys could be validated against files with known or predictable contents.

[HS256] kid Injection Attack Steps

Goal:

Leverage a path traversal in the kid header parameter to coerce the server into a predictable file as a symmetric key, verifying the self-signed JWT

Step 1:

JWT Editor Extension Tab:

  • New Symmetric Key > Generate
  • Replace k value with "AA==" (Base64-encoded null byte) > OK

Step 2:

JWT Editor Repeater Tab:

  • Replace kid parameter value with "../../../../../../../dev/null"
  • Sign > Select HS256 Key > Don't modify header > OK

Step 3:

Send and review response

Success:

Access to sensitive data with forged token

Failure:

401 Unauthorized

Exploit:

Modify available claims to gain illicit access

Running the companion app with the --kid-injection flag will cause the server to load a file from the filesystem with the name of the kid value.

This attack takes advantage of a path traversal pointing to a known file with predictable contents when the server dynamically loads a keyfile based on an attacker-supplied kid. The goal is to use those predictable file contents as a symmetric key.

To do this, create a new symmetric key in the JWT Editor extension tab. After generating a new key, update the k value with a value of "AA==". This is the Base64-encoded null byte. Click OK to save the key.

Figure 25 - Base64-Encoded Null Byte Used for Key

In the JWT Editor subtab in Repeater, modify the kid header parameter to reflect the "../../../../../../../dev/null". This file will always be empty in Unix systems, so the response will be predictable.

Figure 26 - Path Traversal in kid Claim

Lastly, sign the token with the previously created symmetric key and the 'Don't modify header' option selected. If the application is using a kid to dynamically load a symmetric key, and that process is vulnerable to path traversal, JWTs can be arbitrarily forged.

Alternatively, asymmetric JWTs could be forged by pointing to an attacker-supplied public key on the filesystem, if an attacker has the ability to introduce files and identify their path through a file upload or similar means.

Conclusion

JWTs are a primary means of ensuring user authorization in web applications, and flaws in their implementation can have devastating effects on confidentiality and integrity of business-critical data. Potential vulnerabilities must be well understood and thoroughly and consistently assessed during web application penetration testing to ensure the application does not allow unauthorized access to sensitive resources.