JSON web tokens (JWTs) are a standardized format for sending cryptographically signed JSON data between systems.
A JWT consists of 3 parts: a header, a payload, and a signature. These are each separated by a dot, as shown in the following example:
eyJraWQiOiI5MTM2ZGRiMy1jYjBhLTRhMTktYTA3ZS1lYWRmNWE0NGM4YjUiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTY0ODAzNzE2NCwibmFtZSI6IkNhcmxvcyBNb250b3lhIiwic3ViIjoiY2FybG9zIiwicm9sZSI6ImJsb2dfYXV0aG9yIiwiZW1haWwiOiJjYXJsb3NAY2FybG9zLW1vbnRveWEubmV0IiwiaWF0IjoxNTE2MjM5MDIyfQ.SYZBPIBg2CRjXAJ8vCER0LA_ENjII1JakvNQoP-Hw6GG1zfl4JyngsZReIfqRvIAEi5L4HV0q7_9qGhQZvy9ZdxEJbwTxRs_6Lb-fZTDpW6lKYNdMyjw45_alSCZ1fypsMWz_2mTpQzil0lOtps5Ei_z7mM7M8gCwe_AGpI53JxduQOaB5HkT5gVrv9cKu9CsW5MS6ZbqYXpGyOG5ehoxqm8DL5tFYaW3lB50ELxi0KsuTKEbD0t5BCl0aCR2MBJWAbN-xeLwEenaqBiwPVvKixYleeDQiBEIylFdNNIMviKRgXiYuAvMziVPbwSgkZVHeEdF5MQP1Oe2Spac-6IfA
The header and payload parts of a JWT are just base64url-encoded JSON objects. The header contains metadata about the token itself, while the payload contains the actual “claims” about the user. For example, you can decode the payload from the token above to reveal the following claims:
{
"iss": "portswigger",
"exp": 1648037164,
"name": "Carlos Montoya",
"sub": "carlos",
"role": "blog_author",
"email": "carlos@carlos-montoya.net",
"iat": 1516239022
}
The signature is the important part of the security
jwt.io Is a helpful debugger.
JWTs aren’t really used as a standalone entity. The JWT spec is extended by both the JSON Web Signature (JWS) and JSON Web Encryption (JWE) specifications, which define concrete ways of actually implementing JWTs.
- a JWT is usually either a JWS or JWE token
JWT header parameters
jwk(JSON Web Key) - provides an embedded JSON object representing the keyjku(KSON Web Key Set URL) - Provides a URL from which servers can fetch a set of keys containing the correct keykid(Key ID) - Provides an ID that servers can use to identify the correct key in cases where there are multiple keys (such as for different kinds of data)- Arbitrary string of developer choosing, may even be the name of a file
- This can make it prone to directory traversal, perhaps not to be read, but to be
/dev/null-> especially dangerous if it’s symmetric because we know we can sign willNULL, but the contents of another file would work as well
cty(Content Type) - can be used to declare a media type, usually omitted, but underlying library may support it anyway.- Point would be to change to
text/xmlorapplication/x-java-serialized-object, enabling new vectors for XXE or deserialization attacks
- Point would be to change to
x5c(X.509) - sometimes used to pass the X.509 public key cert of the key used to sign the JWT, can be used to inject self-signed certs, similar to thejwkheader injection. Parsing these certs can introduce new vulns.
JWT Attacks
JWT attacks involve a user sending modified JWTs to the server in order to achieve a malicious goal
Lab1
You can decode the payload of the JWT in base64 and simply change the username to administrator
No Signature
{
"alg": "HS256",
"typ": "JWT"
}
typ can be set to none
In the actual solution, it was change the alg to none
This removed the last section of the JWT, though it did still end in a .
Weak Key
hashcat -a 0 -m 16500 eyJraWQiOiIxZjA0ZTY4Yi1iNTMzLTQ2ZDYtOGI1Zi02Y2UyMGExZWVlOWYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTc2NDE5Mjk2NSwic3ViIjoid2llbmVyIn0.phYMSXkl0cq_o_uD-u-6Amve7mduEFl8lDZUOIFB3iY jwt.secrets
Labs
Lab: JWT authentication bypass via jwk header injection
From instructions: lab uses a JWT-based mechanism for handling sessions. The server supports the jwk parameter in the JWT header. This is sometimes used to embed the correct verification key directly in the token. However, it fails to check whether the provided key came from a trusted source.
- This means we can use the JWT editor extensions to fiddle with it and change to the
administratoruser

- I generated a key from the JWT Editor Extension page then use that to
Signfrom this repeater tab, then copied and pasted it into the request. These were the steps that needed to be followed:- Go to the JWT Editor Keys tab in Burp’s main tab bar.
- Click New RSA Key.
- In the dialog, click Generate to automatically generate a new key pair, then click OK to save the key. Note that you don’t need to select a key size as this will automatically be updated later.
GET /adminand change the value of thesubclaim toadministrator.- At the bottom of the JSON Web Token tab, click Attack, then select Embedded JWK. When prompted, select your newly generated RSA key and click OK.
- In the header of the JWT, observe that a
jwkparameter has been added containing your public key.
Apparently I did not need to worry about the key id or the key size. Also I did need to click Attack rather than Sign or Encrypt.
Lab: JWT authentication bypass via jku header injection
This lab requires two steps:
- Generating the key and hosting on the epxloit server
- Fiddling with the JWT in the JWT Editor tab
To generate the token and host it
- Click New RSA Key.
- In the dialog, click Generate to automatically generate a new key pair, then click OK to save the key. Note that you don’t need to select a key size as this will automatically be updated later.
- In the browser, go to the exploit server and replace the contents of the Body section with an empty JWK Set as follows:
{ "keys": [ ] } - Back on the JWT Editor Keys tab, right-click on the entry for the key that you just generated, then select Copy Public Key as JWK.
- Paste the JWK into the
keysarray on the exploit server, then store the exploit. The result should look something like this:{ "keys": [ { "kty": "RSA", "e": "AQAB", "kid": "893d8f0b-061f-42c2-a4aa-5056e12b8ae7", "n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9mk6GPM9gNN4Y_qTVX67WhsN3JvaFYw" } ] }- (I most got here, but I just copied and pasted the whole key rather than public key as JWK)
To fix the JWT:
- Go back to the
GET /adminrequest in Burp Repeater and switch to the extension-generated JSON Web Token message editor tab. - Replace the current value of the
kidparameter with thekidof the JWK that you uploaded to the exploit server. - Add a new
jkuparameter to the header of the JWT. Set its value to the URL of your JWK Set on the exploit server. - Change the value of the
subclaim toadministrator. - At the bottom of the tab, click Sign, then select the RSA key that you generated in the previous section.
- Make sure that the Don’t modify header option is selected, then click OK. The modified token is now signed with the correct signature.
- Use this cookie to get to
/adminand delete carlos
(I correctly added the jku, but I failed to change the kid, and I never clicked Sign)
Lab: JWT authentication bypass via kid header path traversal
- Go to the JWT Editor tab and generate New Symmetric Key
- Replace the generated value for the k property with a Base64-encoded null byte (AA==). Note that this is just a workaround because the JWT Editor extension won’t allow you to sign tokens using an empty string.
- Back in Repeater, change the value of the
kidparameter to a path traversal sequence pointing to the/dev/nullfile such as../../../../../../../dev/null.- Don’t forget to change the value of the
subclaim toadministrator.
- Don’t forget to change the value of the
- Sign (Don’t modify header) -> The modified token is now signed using a null byte as the secret key.
- Use that Cookie, go to admin panel, delete carlos, done.