Skip to main content

JWT attacks: Everything you need to know

How it normally works vs How jwt works (nothing saved in server).

JSON web tokens (JWTs) are a standard format for sending cryptographically signed JSON data between systems. They’re commonly used in authentication, session management, and access control mechanisms. This means that if an attacker can successfully modify a JWT, they may be able to escalate their own privileges or impersonate other users.

The JWT consists of three parts: header, payload, and signature. The signature is generated using the header and payload, along with a secret key, and it is used to verify the authenticity and integrity of the JWT.

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.

Breaking a 256-bit key would indeed require an astronomical number of combinations, making it highly resistant to brute-force attacks by even the most advanced computing resources.

JWT authentication bypass via unverified signature

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

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.

Paste the cookie to below site:

The sub contains your username. Send this request to Burp Repeater.In Burp Repeater, change the path to /admin:

Double click the payload part:

In the Inspector panel, change the value of the sub claim from wiener to administrator, then click Apply changes.

Now we are logged in as administrator.

JWT authentication bypass via flawed signature verification

JWTs can be signed using a range of different algorithms, but can also be left unsigned. In this case, the alg parameter is set to none, which indicates a so-called "unsecured JWT". Due to the obvious dangers of this, servers usually reject tokens with no signature. However, as this kind of filtering relies on string parsing, you can sometimes bypass these filters using classic obfuscation techniques, such as mixed capitalization and unexpected encodings.

This lab uses a JWT-based mechanism for handling sessions. The server is insecurely configured to accept unsigned JWTs.

In the Inspector panel, change the value of the sub claim to administrator

Use the Inspector to change the value of the alg parameter to none

Remove the signature from the JWT, but remember to leave the trailing dot after the payload.

Brute-forcing secret keys: JWT authentication bypass via weak signing key

When implementing JWT applications, developers sometimes make mistakes like forgetting to change default or placeholder secrets. They may even copy and paste code snippets they find online, then forget to change a hardcoded secret that’s provided as an example. In this case, it can be trivial for an attacker to brute-force a server’s secret using a wordlist of well-known secrets.

Brute-forcing secret keys using hashcat:

hashcat -a 0 -m 16500 <jwt> <wordlist>

-a 0: This ‘a’ flag specifies the attack mode for hashcat. In this case, 0 corresponds to the attack mode "Straight" or "Dictionary." In this mode, hashcat will iterate through the wordlist provided to find a match for the hash.

-m 16500: This flag specifies the hash type or algorithm mode. In this case, 16500 refers to the JWT (JSON Web Token) algorithm.

<jwt>: This placeholder should be replaced with the actual JWT hash you're attempting to crack.

<wordlist>: This placeholder should be replaced with the path to a wordlist file.

cracked

Note that if you run the command more than once, you need to include the --show flag to output the results to the console again.

Go to jwt.io and input the secret key we found and then change the “sub” to administrator. Then input it into the “session” in Cookie header, then send the request to the admin page:

Or else use burp:

Base-64 encode the secret key.

Install these extensions:

In the request go to JSON Web Tokens:

Change the “sub” to administrator and add the key. Then send the request.

JWT header parameter injections

Public and private encryption keys

According to the JWS specification, only the alg header parameter is mandatory. JWT headers (also known as JOSE headers) often contain several other parameters.

  • jwk (JSON Web Key) - Provides an embedded JSON object representing the key.
  • jku (JSON Web Key Set URL) - Provides a URL from which servers can fetch a set of keys containing the correct key.
  • kid (Key ID) - Provides an ID that servers can use to identify the correct key in cases where there are multiple keys to choose from. Depending on the format of the key, this may have a matching kid parameter.

These user-controllable parameters each tell the recipient server which key to use when verifying the signature.

JWT authentication bypass via jwk header injection

Ideally, servers should only use a limited whitelist of public keys to verify JWT signatures. However, misconfigured servers sometimes use any key that’s embedded in the jwk parameter.You can exploit this behavior by signing a modified JWT using your own RSA private key, then embedding the matching public key in the jwk header.

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

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

No it will look like:

Sent request to /admin then we have access to admin panel

Injecting self-signed JWTs via the jku parameter

  • jku (JSON Web Key Set URL) - Provides a URL from which servers can fetch a set of keys containing the correct key.
  • 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.
  • Generate New RSA Key and Copy Public Key as JWK. Paste the JWK into the keys array on the exploit server.

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.

Click Sign, then select the RSA key that you generated in the previous section. Don’t modify header option is selected, then click OK.

Injecting self-signed JWTs via the kid parameter

In order to verify the signature, the server uses the kid parameter in JWT (in some cases) header to fetch the relevant key from its filesystem.

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.

Note*: the JWS specification doesn’t define a concrete structure for this ID — it’s just an arbitrary string of the developer’s choosing.

If this parameter is also vulnerable to directory traversal,{ "kid": "../../path/to/file",especially dangerous if the server also supports JWTs signed using a symmetric algorithm. In this case, an attacker could potentially point the kid parameter to a predictable, static file, then sign the JWT using a secret that matches the contents of this file.

/dev/null, on most Linux systems, as this is an empty file, reading it returns an empty string. Therefore, signing the token with a empty string will result in a valid signature.

In Burp, load the JWT Editor extension.

  • In JWT Editor Keys tab in Burp’s main tab bar. Click New Symmetric Key, click Generate to generate a new key in JWK format.

The JWT Editor extension won’t allow you to sign tokens using an empty string so replace the generated value for the k with a Base64-encoded null byte (AA==).

Point to the /dev/null file:../../../../../../../dev/null

Click Sign, then select the symmetric key that you generated in the previous section.

Other interesting JWT header parameters

cty (Content Type) - Sometimes used to declare a media type for the content in the JWT payload. You can try injecting a cty header to change the content type to text/xml or application/x-java-serialized-object, which can potentially enable new vectors for XXE and deserialization attacks.

x5c (X.509 Certificate Chain) - Sometimes used to pass the X.509 public key certificate or certificate chain of the key used to digitally sign the JWT. Inject self-signed certificates, similar to the jwk header injection attacks

JWT algorithm confusion

The JWT algorithm confusion attack occurs when an application fails to properly validate the algorithm specified in the header of the JWT. An attacker may attempt to change the algorithm from a secure one (e.g., HMAC-SHA256) to a weak one (e.g., none or HS256) that doesn’t involve actual signing, or to an algorithm with known vulnerabilities.

If the server uses the manipulated algorithm, it might consider the JWT as valid, granting unauthorized access to the attacker.

Algorithms like HS256 (HMAC + SHA-256) use a “symmetric” key, referred to as secret key algorithms because it use one key that is kept secret by the systems engaged in the encryption and decryption processes. This single key is used for both encryption and decryption.

Other algorithms, such as RS256 (RSA + SHA-256) use an “asymmetric” key pair.Asymmetric encryption algorithms use two different keys for encryption and decryption.

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 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);

if the server receives a token signed using a symmetric algorithm like HS256, 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.

Perform algorithm confusion attack:

  1. Obtain the server’s public key
  2. Convert the public key to a suitable format
  3. Create a malicious JWT with a modified payload and the alg header set to HS256.
  4. Sign the token with HS256, using the public key as the secret.

Obtain the server’s public key

Servers sometimes expose their public keys as JSON Web Key (JWK) objects via a standard endpoint mapped to /jwks.json or /.well-known/jwks.json

Copy the JWK object from inside the keys array.

Convert the public key to a suitable format

Click New RSA Key in JWT Editor Keys. Then paste the JWK that you just copied. Click OK. Then select Copy Public Key as PEM. Base64 encode this PEM key

Click New Symmetric Key. Replace the generated value for the k property with a Base64-encoded PEM.

Create a malicious JWT with a modified payload and the alg header set to HS256.

Sign the token with HS256, using the public key as the secret.

What if the keys are not exposed

We may still be able to test for algorithm confusion by deriving the key from a pair of existing JWTs.

  1. Log in to your account
  2. Copy your JWT session cookie and save it somewhere for later.
  3. Log out and log in again.
  4. Copy the new JWT session cookie and save this as well. You now have two valid JWTs generated by the server.

Brute-force the server’s public key

This step is made simple with tools like :

  1. jwt_forgery.py in

2. Portswigger have created a simplified version of this tool, which you can run as a single command:

docker run --rm -it portswigger/sig2n <token1> <token2>

Copy the tampered JWT

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 this was the wrong X.509 key. In this case, repeat this step using the tampered JWT for each X.509 key
  • copy the Base64-encoded of the correct X.509 key from your terminal

Generate New Symmetric Key in JWK format.

Replace the generated value for the k property with a Base64-encoded key that you just copied

Modify and sign the token

Here are some ways to prevent JWT attacks:

  1. Use Strong Algorithms: Choose strong cryptographic algorithms for signing and encryption, such as HMAC-SHA256 or RSA-256. Avoid using weaker algorithms that can be easily compromised.
  2. Verify JWT Signatures: Always verify the integrity of the JWT by checking the signature. This ensures that the token hasn’t been tampered with. Ensure that only trusted keys are used for verification.
  3. Validate JWT Claims: JWTs typically contain claims about the user or session. Validate these claims rigorously. Check the expiration time (exp claim), issuer (iss claim), audience (aud claim), and other claims based on your application’s requirements.
  4. Use Short Expiry Times: Set short expiration times for tokens. This reduces the window of opportunity for attackers to exploit stolen tokens.
  5. Implement Token Revocation: Have a mechanism to revoke JWTs in case they are compromised or no longer needed. This could involve maintaining a token blacklist or using token revocation lists (CRLs).
  6. Protect Private Keys: If you’re using asymmetric cryptography, protect private keys properly. Store them securely and limit their access to authorized personnel.
  7. Implement Proper CORS: If your application uses Cross-Origin Resource Sharing (CORS), ensure that it’s properly configured to only allow requests from trusted domains.
  8. Implement Rate Limiting: Prevent brute-force attacks by implementing rate limiting on token requests.

SSRF Bug Boundy Hunting:



 

Comments

Popular posts from this blog

Bug Boundy Methodology, Tools & Resources

Start by defining a clear objective, such as exploiting a remote code execution (RCE) vulnerability or bypassing authentication on your target. Then, consider how you can achieve this goal using various attack vectors like XSS, SSRF, or others - these are simply tools to help you reach your objective. Use the target as how a normal user would, while browsing keep these questions in mind: 1)How does the app pass data? 2)How/where does the app talk about users? 3)Does the app have multi-tenancy or user levels? 4)Does the app have a unique threat model? 5)Has there been past security research & vulnerabilities? 6)How does the app handle XSS, CSRF, and code injection?

Install & set up mitmweb or mitmproxy in Linux

Step 1: Go to the mitmproxy page and download the binaries. Step 2: Install the downloaded tar file with the command " tar -xzf <filename>.tar.gz " Step 3: In the FoxyProxy add the proxy 127.0.0.1:8080  and turn it on. Step 4 : In the terminal run command " ./mitmweb " Step 5: Go to the page  http://mitm.it/   and download the mitmproxy's Certificate. Step 6: If you downloaded the certificate for Firefox, then go to " settings -> Privacy & Security -> Click View Certificates -> Click  Import ", then import the certificate.  Step 7: Now you are ready to capture the web traffic. Step 8 : In terminal run " ./mitmweb"

pip error in Kali Linux: error: externally-managed-environment : SOLVED

 error: externally-managed-environment × This environment is externally managed ╰─> To install Python packages system-wide, try apt install     python3-xyz, where xyz is the package you are trying to     install.     If you wish to install a non-Kali-packaged Python package,     create a virtual environment using python3 -m venv path/to/venv.     Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make     sure you have pypy3-venv installed.     If you wish to install a non-Kali-packaged Python application,     it may be easiest to use pipx install xyz, which will manage a     virtual environment for you. Make sure you have pipx installed.     For more information, refer to the following:     * https://www.kali.org/docs/general-use/python3-external-packages/     * /usr/share/doc/python3.12/README.venv note: If you believe this is a mistake, please contac...