JWT — Burpsuite Web Academy Walkthrough

Chenny Ren
16 min readApr 5, 2023

--

JSON web tokens (JWTs) are a standardized format for sending cryptographically signed JSON data between systems. They can theoretically contain any kind of data, but are most commonly used to send information (“claims”) about users as part of authentication, session handling, and access control mechanisms.

Unlike with classic session tokens, all of the data that a server needs is stored client-side within the JWT itself. This makes JWTs a popular choice for highly distributed websites where users need to interact seamlessly with multiple back-end servers

Working with JWTs in Burp Suite

https://portswigger.net/web-security/jwt/working-with-jwts-in-burp-suite

JWT authentication bypass via unverified signature

This lab uses a JWT-based mechanism for handling sessions. Due to implementation flaws, the server doesn’t verify the signature of any JWTs that it receives.

To solve the lab, modify your session token to gain access to the admin panel at /admin, then delete the user carlos.

In Burp, go to the Proxy > HTTP history tab and look at the post-login GET /my-account request. Observe that your session cookie is a JWT

Double-click the payload part of the token to view its decoded JSON form in the Inspector panel. Notice that the sub claim contains your username. Send this request to Burp Repeater.

admin panel is only accessible for administrator

Unfortunately, the Inspector cannot currently decode both the header and payload at the same time. You need to select either one or the other.

truncate the payload and decode one by one

eyJraWQiOiI5ODVkN2MxYS0yYWYwLTRlOWQtODQ5OC1kOTQzMDkwMTRiZmYiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4MDIzMzYyNX0.FBPPlL26okr2sfyRytStPXyMnAYJ5QQ0A6wRudNvxvjlCSH423pcp1EaWytKJXiIi2DpU4r-cG4LCMCvCahg5eAPSfJJj-mPUZycAkGblotoWQK0A6iBF4bt0eguQTROlIMN3FQWM5bacaDd12jpYWyXsPQJRgKecOKEkADkqHC_gLL3mrxwnzLefbjxxBH2A3uXgdqrE3TLBNS8CGzWIbU44DfZeBwAYph7uNSfagqG-m4bXlruVfuaOOog9L06pYDXXwoiP7pGasliM4U6D8mXaQ4GfJ_kj8q-7nw8Yytd-FGSEtLQotO56DcH0ZBxFvjRCpoub6qOHZ19jWFKgQ

(change the sub to administrator )

and access /admin

Then we can access admin panel , however we can not delete it from the web interface because we are still logged in as wiener

Change the request to /admin/delete?username=carlos in burpsuite with the modified JWT , and we can delete it from burp

JWT authentication bypass via flawed signature verification

go to the Proxy > HTTP historytab and look at the post-login GET /my-account request. Observe that your session cookie is a JWT

Use same methods as previous exercise to inspect JWT token and change the sub to adminitrator

Path to admin

But failed to authorize this time

send the cookie to Json web token

change “alg” to none , change “sub” to administrator , and remove the signature

(No signature attack)

then we can access admin panel

delete carlos with request /admin/delete?username=carlos

JWT authentication bypass via weak signing key

This lab uses a JWT-based mechanism for handling sessions. It uses an extremely weak secret key to both sign and verify tokens. This can be easily brute-forced using a wordlist of common secrets.

To solve the lab, first brute-force the website’s secret key. Once you’ve obtained this, use it to sign a modified session token that gives you access to the admin panel at /admin, then delete the user carlos.

Part1 Brute force with hashcat

JWT attacks | Web Security Academy

Wordlist

jwt-secrets/jwt.secrets.list at master · wallarm/jwt-secrets

Intercept the request after log in

copy the JWT and bruteforce secret

eyJraWQiOiI0YmQ3N2RkMC02NDNhLTQzNTUtYmVmYS0zYmMyNzk2ZWViZmYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4MDIzNjc1Nn0.HmjH8F8r1SZHXR5wrLgjqp9nd1oLPgQhovTiJR0Ok1I
hashcat -a 0 -m 16500 jwt jwt.secrets.list

the secret key is secret1

Part 2 — Generate a forged signing key

  • Using Burp Decoder, Base64 encode the secret that you brute-forced in the previous section.
  • In Burp, go to the JWT Editor Keys tab and click New Symmetric Key. In the dialog, click Generate to generate a new key in JWK format. Note that you don’t need to select a key size as this will automatically be updated later.
  • Replace the generated value for the k property with the Base64-encoded secret.
  • Click OK to save the key.

Part 3 Modify and sign the JWT

  • Go back to the GET /admin request in Burp Repeater and switch to the extension-generated JSON Web Token message editor tab.
  • In the payload, change the value of the sub claim to administrator
  • At the bottom of the tab, click Sign, then select the 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.
  • Send the request and observe that you have successfully accessed the admin panel.
  • In the response, find the URL for deleting Carlos (/admin/delete?username=carlos). Send the request to this endpoint to solve the lab.

JWT authentication bypass via jwk header injection

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

To solve the lab, modify and sign a JWT that gives you access to the admin panel at /admin, then delete the user carlos.

Intercept a request on Get /account

The admin panel is only accessible for administrator

  • 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.
  • Go back to the GET /admin request in Burp Repeater and switch to the extension-generated JSON Web Token tab.
  • In the payload, change the value of the sub claim to administrator.
  • 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 jwk parameter has been added containing your public key.
  • Send the request. Observe that you have successfully accessed the admin panel.

Delete carlos with GET /admin/delete?username=carlos

JWT authentication bypass via jku header injection

This lab uses a JWT-based mechanism for handling sessions. The server supports the jku parameter in the JWT header. However, it fails to check whether the provided URL belongs to a trusted domain before fetching the key.

To solve the lab, forge a JWT that gives you access to the admin panel at /admin, then delete the user carlos

Instead of embedding public keys directly using the jwk header parameter, some servers let you use the jku(JWK Set URL) header parameter to reference a JWK Set containing the key. When verifying the signature, the server fetches the relevant key from this URL.

Intercept request of get /account

Part 1 — Upload a malicious JWK set

  • 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.
  • In the browser, go to the exploit server.

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 keys array on the exploit server, then store the exploit. The result should look something like this:
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"kid": "42e778bb-3fdd-4632-85cf-0399b9179eaa",
"n": "p-eWG9z7cvlFN4tiNYIVxHOaY9kAm_mKQU3ZURU5R4ML4qF6-3SrYGVMT5xhi0RJ0GEu4wROpbjzJai9qSoUWjMbrEiy2LqBZNsdY8UrO7iSYiCJfLuQkLU2IiQXhJ5sgDeFGIeInEQoZYo7iN_kQzVcemU1FOCHQDxC1iV5MHu8DF1I5WCeHzpJFRZmCz4v63WnxrGjMI3vHoMSAuQuHFV_ukueNYuSD8Lx5N-rZQpnJ7Gi0NxdEleE0dxBckqJUG6FgYCQGrlrb68QJlbn1Hoz3XeOID2NS5cmTgzksQXyKqHSpgCavH0FdG5p_Q6VquyPPVBEbxxMKqJOoA_qOQ"
}
]
}

file name : /.well-known/jwks.json

Part 2 — Modify and sign the JWT

Go to JSON Web token editor

  • In the header of the JWT, replace the current value of the kid parameter with the kid of the JWK that you uploaded to the exploit server.
  • Add a new jku parameter to the header of the JWT. Set its value to the URL of your JWK Set on the exploit server.
  • In the payload, change the value of the sub claim to administrator.
  • 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.
  • Send the request. Observe that you have successfully accessed the admin panel.
  • In the response, find the URL for deleting Carlos (/admin/delete?username=carlos). Send the request to this endpoint to solve the lab.

JWT authentication bypass via kid header path traversal

This lab uses a JWT-based mechanism for handling sessions. In order to verify the signature, the server uses the kid parameter in JWT header to fetch the relevant key from its filesystem.

To solve the lab, forge a JWT that gives you access to the admin panel at /admin, then delete the user carlos

Servers may use several cryptographic keys for signing different kinds of data, not just JWTs. For this reason, the header of a JWT may contain a kid (Key ID) parameter, which helps the server identify which key to use when verifying the signature.

Verification keys are often stored as a JWK Set. In this case, the server may simply look for the JWK with the same kid as the token. However, the JWS specification doesn't define a concrete structure for this ID - it's just an arbitrary string of the developer's choosing. For example, they might use the kid parameter to point to a particular entry in a database, or even the name of a file.

If this parameter is also vulnerable to directory traversal, an attacker could potentially force the server to use an arbitrary file from its filesystem as the verification key.

{
"kid": "../../path/to/file",
"typ": "JWT",
"alg": "HS256",
"k": "asGsADas3421-dfh9DGN-AFDFDbasfd8-anfjkvc"
}

Part 1 — Generate a suitable signing key

  • Go to the JWT Editor Keys tab in Burp’s main tab bar.
  • Click New Symmetric Key.
  • In the dialog, click Generate to generate a new key in JWK format. Note that you don’t need to select a key size as this will automatically be updated later.
  • 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.

Null Byte : AA==

  • Click OK to save the key.

Modify and sign the JWT

  • Go back to the GET /admin request in Burp Repeater and switch to the extension-generated JSON Web Token message editor tab.
  • In the header of the JWT, change the value of the kid parameter to a path traversal sequence pointing to the /dev/null file:../../../../../../../dev/null
  • In the JWT payload, change the value of the sub claim to administrator.
  • At the bottom of the tab, click Sign, then select the symmetric 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 using a null byte as the secret key.
  • Send the request and observe that you have successfully accessed the admin panel.
  • In the response, find the URL for deleting Carlos (/admin/delete?username=carlos). Send the request to this endpoint to solve the lab.

JWT authentication bypass via algorithm confusion

This lab uses a JWT-based mechanism for handling sessions. It uses a robust RSA key pair to sign and verify tokens. However, due to implementation flaws, this mechanism is vulnerable to algorithm confusion attacks.

To solve the lab, first obtain the server’s public key. This is exposed via a standard endpoint. Use this key to sign a modified session token that gives you access to the admin panel at /admin, then delete the user carlos.

Algorithm confusion attacks | Web Security Academy

The following pseudo-code shows a simplified example of what the declaration for this generic verify() method might look like in a JWT library:

function verify(token, secretOrPublicKey){
algorithm = token.getAlgHeader();
if(algorithm == "RS256"){
// Use the provided key as an RSA public key
} else if (algorithm == "HS256"){
// Use the provided key as an HMAC secret key
}
}

Problems arise when website developers who subsequently use this method assume that it will exclusively handle JWTs signed using an asymmetric algorithm like RS256. Due to this flawed assumption, they may always pass a fixed public key to the method as follows:

publicKey = <public-key-of-server>;
token = request.getCookie("session");
verify(token, publicKey);

In this case, if the server receives a token signed using a symmetric algorithm like HS256, the library’s generic verify() method will treat the public key as an HMAC secret. This means that an attacker could sign the token using HS256 and the public key, and the server will use the same public key to verify the signature

Part 1- Obtain the server’s public key

Go to /jwks.json and observe that the server exposes a JWK Set containing a single public key.

copy the key array

{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "6fb522ca-81e0-4aa9-9228-ffe6d4e51a68",
"alg": "RS256",
"n": "wXF_jqFn35gmHzF43gG4KwJAs-ZXhJffZ4O8-JBwsqXjg55_ATjOp10k498yE8B7h3YHuKhHvRuptsO23EbcQjSQnrDEkw2yb0TCIJ0gjIdvo9GZYiq6518M9Scig_pttfoiNNhakCCdOdkVuM2p4v_IZ3K1z1sn5EYKQ0MrBDqo2WEp1JowgjKYi54EZ47JTItYpXZN8Oc5v7gxTscvh5KxIpg9wFCHpcyVeVToKMMOt5sU73negVVd1OUAy2LRcT9U8USbce1MlbBWBTew50fLBusDlCZPjU1WkIy6Fc-Ec6kHA0hm1u1qm831Kcw-S60fSJlOMWmAvb4jl67EnQ"
}

Part 2 — Generate a malicious signing key

  • In Burp, go to the JWT Editor Keys tab in Burp’s main tab bar.
  • Click New RSA Key.
  • In the dialog, make sure that the JWK option is selected, then paste the JWK that you just copied. Click OK to save the key.
  • Right-click on the entry for the key that you just created, then select Copy Public Key as PEM.
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXF/jqFn35gmHzF43gG4
KwJAs+ZXhJffZ4O8+JBwsqXjg55/ATjOp10k498yE8B7h3YHuKhHvRuptsO23Ebc
QjSQnrDEkw2yb0TCIJ0gjIdvo9GZYiq6518M9Scig/pttfoiNNhakCCdOdkVuM2p
4v/IZ3K1z1sn5EYKQ0MrBDqo2WEp1JowgjKYi54EZ47JTItYpXZN8Oc5v7gxTscv
h5KxIpg9wFCHpcyVeVToKMMOt5sU73negVVd1OUAy2LRcT9U8USbce1MlbBWBTew
50fLBusDlCZPjU1WkIy6Fc+Ec6kHA0hm1u1qm831Kcw+S60fSJlOMWmAvb4jl67E
nQIDAQAB
-----END PUBLIC KEY-----
  • Use the Decoder tab to Base64 encode this PEM key, then copy the resulting string.
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF3WEYvanFGbjM1Z21IekY0M2dHNApLd0pBcytaWGhKZmZaNE84K0pCd3NxWGpnNTUvQVRqT3AxMGs0OTh5RThCN2gzWUh1S2hIdlJ1cHRzTzIzRWJjClFqU1FuckRFa3cyeWIwVENJSjBnaklkdm85R1pZaXE2NTE4TTlTY2lnL3B0dGZvaU5OaGFrQ0NkT2RrVnVNMnAKNHYvSVozSzF6MXNuNUVZS1EwTXJCRHFvMldFcDFKb3dnaktZaTU0RVo0N0pUSXRZcFhaTjhPYzV2N2d4VHNjdgpoNUt4SXBnOXdGQ0hwY3lWZVZUb0tNTU90NXNVNzNuZWdWVmQxT1VBeTJMUmNUOVU4VVNiY2UxTWxiQldCVGV3CjUwZkxCdXNEbENaUGpVMVdrSXk2RmMrRWM2a0hBMGhtMXUxcW04MzFLY3crUzYwZlNKbE9NV21BdmI0amw2N0UKblFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
  • Go back to the JWT Editor Keys tab in Burp’s main tab bar.
  • Click New Symmetric Key. In the dialog, click Generate to generate a new key in JWK format. Note that you don’t need to select a key size as this will automatically be updated later.
  • Replace the generated value for the k property with a Base64-encoded PEM that you just created.
  • Save the key.

Part 3 — Modify and sign the token

Go back to the GET /admin request in Burp Repeater and switch to the extension-generated JSON Web Token tab.

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

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

At the bottom of the tab, click Sign, then select the symmetric 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 using the server’s public key as the secret key.

Send the request and observe that you have successfully accessed the admin panel.

In the response, find the URL for deleting Carlos (/admin/delete?username=carlos). Send the request to this endpoint to solve the lab.

JWT authentication bypass via algorithm confusion with no exposed key

This lab uses a JWT-based mechanism for handling sessions. It uses a robust RSA key pair to sign and verify tokens. However, due to implementation flaws, this mechanism is vulnerable to algorithm confusion attacks.

To solve the lab, first obtain the server’s public key. Use this key to sign a modified session token that gives you access to the admin panel at /admin, then delete the user carlos.

Algorithm confusion attacks | Web Security Academy

Part 1 — Obtain two JWTs generated by the server

log in and intercept with burpsuite , copy the JWT session cookie

log out and log in again , copy the new JWT session cookie

eyJraWQiOiJjMTE0Y2ZlYi00YjE0LTQyYTktOGVlYS1kM2QyMmY3MDc3MDMiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4MDY3NzM0OH0.i1xQpc5vaL9FeWInchoJqmGwiyh7GZE_SHiHi8_u4c81UoIe92T7-0ilbUL4hYA21FN7CKjf595hKB_j7Wl4ZOS8FvhcjSto5wVPT7EXri_JQFVGMPjo-IwbccvhSRE-fZBGkvzEOqMoAviB3EtHXQkDHJR5EfEHAbghf1T-M_fmFKDgWJYeUEH1TNLxEKGEiFSCouaDF_g-BftavERuiu1Bfk3dusz2zFY1fW2fAHs1rjhgBh3lkqtW1DcqCnH4ktWiVKCufOJ9j8a34BtsiFtFhSh3GT8euJ5IlvFFnmG0qSsv8p_LT0V7zhVy5pZZExTCiz1WmQ1O4haen4c20g
eyJraWQiOiJjMTE0Y2ZlYi00YjE0LTQyYTktOGVlYS1kM2QyMmY3MDc3MDMiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4MDY3NzQ5M30.TgJBB1A5OmVfEjXWa06LlBZ70FRsthhVOWWSoKB15RIl_YcdaCLkv0X7wXCCJ2ZFNJFQYi2w9t6Tqm1K1gOHE-A9qbtKMXft5dB0mFfk3vH71uAIZDOgOHP9zuA43R4O08pT8PzbpPGJ6u-orCXw9ErhOFolmYOIE3-AtQnkUBFfHPzI5U6mVBVdxNAbQ9W8wzGttyutgt9C8U6b30Um0cx8V7e7L4mY3QYFbUqSTQ6R_XwSE1L5FLYNk9PU_E-tfnGjtqBKtRL11yYTtLgWN15cYwGsaxcXtwZYfskN1sFyiupmqTGoLS1jkxyo4qn5g33FRrCGBE6MdYKxI4hYRw

Part 2 — Brute-force the server’s public key

  • In a terminal, run the following command, passing in the two JWTs as arguments.docker run --rm -it portswigger/sig2n <token1> <token2>
  • Note that the first time you run this, it may take several minutes while the image is pulled from Docker Hub.

Docker

docker pull portswigger/sig2n

Notice that the output contains one or more calculated values of n. Each of these is mathematically possible, but only one of them matches the value used by the server. In each case, the output also provides the following:

  • A Base64-encoded public key in both X.509 and PKCS1 format.
  • A tampered JWT signed with each of these keys.

Copy the tampered JWT from the first X.509 entry (you may only have one).

eyJraWQiOiI1NTk4NTk3My0xMGVjLTQwMmMtOTNkYi01OGQzNjFkOTIxMzkiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiAicG9ydHN3aWdnZXIiLCAic3ViIjogIndpZW5lciIsICJleHAiOiAxNjgwNzYzMjE5fQ.DHmRV3arYdlx6mv-S48DRwDVH5qdGi4JbYM5H68HaoE

Go back to your request in Burp Repeater and change the path back to /my-account.

Replace the session cookie with this new JWT and then send the request.

  • If you receive a 200 response and successfully access your account page, then this is the correct X.509 key.
  • If you receive a 302 response that redirects you to /login and strips your session cookie, then this was the wrong X.509 key. In this case, repeat this step using the tampered JWT for each X.509 key that was output by the script.

Part 3 — Generate a malicious signing key

  • From your terminal window, copy the Base64-encoded X.509 key that you identified as being correct in the previous section. Note that you need to select the key, not the tampered JWT that you used in the previous section.
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFwZ3lDVWZpVTFCSW5ocDBrU0dlMgpCV0poOEV1WEliQlhBM2ZONnpjeThHdnpUVTFwNm9GZE5BeUl5RFNvTlpRd05NTzFQVUJ5Y3V2US80RlVxdWl4CkdWUFhZS0JJcXkvREtVYStaMkxrd01aVjdvRFY2Nk1obFJjb1RrLzdsbkpLelR5Vm1oSlpNN2lBY2poRXlwUWgKRkZmc0l2TmJpVUNxR2trQUY5QWFvakJNT2ozL25DWWZjSldXVzBZVEFCR1B6emVWUFNFa1o4T2hOZWdNOVkreQpyM3RQWEFNYjdzcGdRZHExZGV4UkZiSlU5SW5OUlEyNlZMR0QvdTVscDBkUTIrelpKUlFPVTNScjZIZVZGNTlaCjlaT1BsbFh6Qko1RXJVVmZpbXlmNVY5L0NnNFZ5UkQ5WmxxUFBDREhWM2ZkNWkxZ29TcUFYRUlEOFl3NDdQQ1QKUFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==

In Burp, go to the JWT Editor Keys tab and 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 key that you just copied. Note that this should be the actual key, not the tampered JWT that you used in the previous section.

Part 4 — Modify and sign the token

Go back to your request in Burp Repeater and change the path to /admin.

Switch to the extension-generated JSON Web Token tab.

In the header of the JWT, make sure that the alg parameter is set to HS256.

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

At the bottom of the tab, click Sign, then select the symmetric 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 using the server’s public key as the secret key.
  • Send the request and observe that you have successfully accessed the admin panel.
  • In the response, find the URL for deleting Carlos (/admin/delete?username=carlos). Send the request to this endpoint to solve the lab.

--

--

Chenny Ren

OSCP | OSWP | OSEP | CRTP |CRTE | CRTO | Red Team Professional | SOC engineer