JWT: A Deep Dive into Algorithm Confusion

JWT

Since the first time I studied JWT, I found it one of the most intriguing topics to discuss about web application security, and there are good reasons for this.

  1. Introduced in 2010, JSON Web Tokens (JWTs) only started gaining mainstream traction after 2018, marking them as a relatively modern technological advancement.
  2. Because it is new, developers struggle with their implementation, which means more fun bugs to explore for us security experts 
  3. I do not know about you guys, but playing with encryption is hands down the most fun thing to do while testing a web application, and JWTs are a prime target for encryption-based attacks like algorithm confusion.

With this in mind, we recently undertook a web application penetration test on redacted.com, signifying the need to put my web application skills to the test.

What are JWTs?

Well, if you have no clue on what black magic JWTs are, you can think of them as sessions cookies.
Just – much more efficient, faster and stronger (under correct implementation).

Here’s the bookish definition: “JSON web tokens (JWTs) are a standardized format for sending cryptographically signed JSON data between systems.”

BORING!

Here’s a better explanation.

Think of JWT like a sealed envelope. Now, this envelope has 3 parts.

Header of a JWT: On the outside, it states what type of envelope it is, and what kind of seal it’s using. Typically, this includes at least a ‘typ’ parameter indicating that the token is a JWT and the ‘alg’ parameter stating the type of encryption used to sign the JWT.

Payload of a JWT: This is the actual contents of the envelope such as the person who sent it(username), their email, if they’re your friend or not(authentication), if they’re your best friend or a normal friend(authorization) and so on.

So, when generating a JWT, the payload may include information about the user’s identity, preferences, roles, privileges, and other pertinent details. In the majority of cases, the payload is not encrypted; it’s intended to be visible and easily readable.

Signature of a JWT: This is the seal of the envelope. If this seal has been broken, it signals that there has been some form of modification to the content inside and you should simply reject it as invalid.

The signature is a base64 string generated by signing the concatenated header and payload of a JWT.One important thing to note is the generation process of the signature for a JSON Web Token (JWT):

  1. It begins by encoding the header and payload (separately) in ‘Base64Url’ format, which essentially means using base64 encoding while stripping any padding (‘=’).
  2. The resultant strings are concatenated with a period.
  3. A cryptographic algorithm applies with a secret key (HMAC) or private key (RSA/ECDSA).
  4. The resulting signature also Base64Url encodes, which then appends towards the end of the JWT.
  5. Just like a session cookie, JWTs manage authentication, sessions, authorization, access control, and so on.
  6. However, unlike a session cookie that needs storing inside the server, JWTs:
    • It exists on the client side.
    • Persist until they expire.
    • Only needs to be generated once during the authentication process.

The only thing that needs to be stored is/are (depending upon the type of encryption being used) the keys that encrypt/decrypt/validate a JWT.

For more information about JWTs, you can read our previous blog or this blog.

Algorithm Confusion Attacks

JWT encryption

Before diving into our bug, let’s briefly go over one common vulnerability that arises if JSON Web Token implementation is insecure. Broadly speaking, there are 4 algorithms that can be used to sign a JWT (seal our envelope):

  1. HMAC (Symmetric)
  2. RSA (Asymmetric)
  3. ESDSA (Asymmetric)
  4. None (no signature)

In an ideal configuration, developers should disable all unneeded algorithms, especially the “none” algorithm, which is not intended for a production environment.

However, a lot of web applications do not follow this best practice which makes them vulnerable to algorithm confusion attacks.

How does Algorithm Confusion Occur

Think of 2 types of encryption algorithms:

  • HMAC(Symmetric):
    There’s only 1 key involved, AKA the secret
    The JWT will be signed and validated using the same secret key.
  • RSA(Asymmetric):
    There’s 2 keys involved. A private key, and a public key.
    The JWT will be signed using a private key, and the signature will be validated using a public key.

A scenario where algorithm confusion is exploited can be as follows:

  1. An attacker authenticates to a web server with valid credentials.
  2. The web server generates a JWT, and signs it with an RSA private key, only known by the server.
  3. This JWT is then passed on to the attacker.
  4. The public key is exposed on an endpoint such as /.well-known/jwks.json.
  5. The attacker now:
    • Downloads the public key.
    • Tampers with the JWT.
    • Signs the JWT with the exposed public key.
    • Changes the encryption algorithm to HMAC.
    • Lastly, Sends the request across.
  1. Now, in a normal scenario, where the encryption algorithm is RSA, the server reads the signature, tries to verify it using the public key, but fails, because the public key can only verify signatures that have been generated using the private key (asymmetric encryption).
  2. However, since in this scenario the attacker had modified the encryption algorithm to HMAC (a symmetrical encryption), the server attempted and succeeded in verifying signature using its public key.
  3. This happens, because HMAC is a symmetrical encryption, and the key that is used to sign the JWT, is the same key, that will be used to validate it.
  4. And because the new tampered JWT has been signed with the exposed public key, and the algorithm in use is HMAC, the JWT gets validated.

Redfox Engagement

Pilot

Now of course, the scenario discussed above is one of many scenarios where algorithm confusion can cause critical vulnerabilities. In this topic, we’ll discuss how I found this critical vulnerability in the JWT implementation of this seemingly secure web application.

Initial Request

As I was going through this web application, I had already tested it for common vulnerabilities, such as XSS, Path traversal, PII disclosure and so on. As the web application was seemingly secure, I wanted to hop on to test the JWT implementation.

As mentioned previously, JWTs are always a good target to go for due to their modernity. Going through the web application in burp suite, I came across this simple request that used JWTs to get authentication data for the logged in user.(For context, the credentials for a test account were included in the scope)

JWTs 1

As you can see inside the request, inside the “Cookie” header, there’s 2 parameters, “jwt_session” and “jwt_token”. Both of these parameters contain JSON Web Tokens. The response, contains some normal authentication data about our test user. In an ideal scenario, there shouldn’t be 2 JWTs inside 1 “Cookie” header. This is definitely something odd that got my hacker senses tingling.

Playing with parameters

After playing around with these parameters for a bit, I realized that even if one of the two parameters is missing, but the other parameter has a valid JWT, we will get a valid response.
Here you can see, I have removed the “jwt_token” parameter, but the “jwt_session” parameter holds a valid JWT. Thus, we get a valid response.

Jwts 3
Jwts 4

Once I got a valid response, I wanted to play with the “jwt_session” parameter alone, as that’s the odd parameter. 

“Why?” you ask? Simple.

A parameter named “jwt_token” should hold a JWT and sounds like a part of a framework.

A parameter named “jwt_session” makes less sense and sounds like a parameter added in a custom way by the developer.

Of course these are just assumptions and not concrete conclusions, and all parameters should be tested in a web application test.

However, in this scenario, this was my reasoning to test the “jwt_session” parameter before I tested the “jwt_token” parameter.

I started testing the “jwt_session” parameter by performing a “none” algorithm attack:

Jwts5
Jwts6
For context, this is the “JSON Web Tokens” extension in Burp Suite

In this attack, we basically mention the “none” algorithm inside the JWT header, and remove any signature, and send the request across.

The summary of that attack is that in the “alg” parameter of the JWT header, if we mention “none”, the server will not try even try to verify the JWT, and will simply accept whatever data that is sent.

For more information you can check this out.

To my surprise (or not), the attack did not work, as this is a very common vulnerability.

It is pretty much the equivalent of trying to find an SQL injection in a login field (possible, extremely rare under very bad implementations).

Noticing that the JWT was using “RS256” (which stands for RSA-SHA256) as the encryption algorithm, the next attack I wanted to try, was an algorithm confusion attack.

However, the problem was that I could not find a public key to sign the JWT with, so I took the next best option, which was to sign the JWT with nothing.

Confused dog

Exactly, it might sound confusing at first, but this is what I like to call an empty key attack.

I could not find a good resource that discusses this vulnerability, so I took matters into my own hands.

Null Key Attack:

Consider the following source code:

Jws function

In this function, there’s 2 separate code blocks depending on if the JWT is using RSA or HMAC.

In some scenarios, a developer may assume that the only algorithm to sign the JWT is RSA and will set a private key accordingly. Under this assumption, the server will not expect to receive any JWT signed with HMAC, leading to no key being set to validate HMAC signatures.

Due to the absence of a key to validate HMAC signatures, if the server receives a JWT signed with HMAC, it will inadvertently attempt to validate the signature with a null byte, resulting in potential security vulnerabilities.

  • In Base64 – AA==
  • In Binary –  \x00

To exploit this misconfiguration, an attacker will simply take any JWT, and sign it with a null byte, and whatever data that has been sent inside the JWT, will be validated.

This type of attack can be called an empty key attack, however the root of such an attack is algorithm confusion. The attack exists because the server is unable to handle or insecurely handles different types of encryptions.

Execution

Now that the boring theory is out of the way, we can discuss how I was able to exploit this vulnerability in the engagement.

Inside the JSON Web Token editor section of the repeater tab, I simply hovered over the “Attack” section, and chose “Sign with empty key” option and chose HS256 as the encryption algorithm:

Jwts Empty key
Jwts Empty key signing dialog

This process will sign the JWT with a null byte, and change the “alg” header inside the JWT to “HS256” (stands for HMAC-SHA256). With the changes made, we can review our JWT one final time (notice the “alg” parameter in the header, and the signature):

With this, I hit send!
Jwts Signature with null keys
And...
Jwts A Valid response
VOILA! A valid response!

This means that I can tamper with the data inside the payload of the JWT, sign it with HMAC with the secret being a null byte, send the request, and the server will validate anything inside the payload.

To test this, I changed a few id parameters and the email parameter, signed the JWT, and sent the request:

Jwts Payload

(For context, there’s 2 test accounts:

  • PentestUser-Redfox-Test01@gmail.com
  • PentestUser-Redfox-Test02@gmail.com

This is very critical, as this means that I can not only takeover accounts, but access all roles, privileges, and even change roles for other accounts.

To actually exploit this vulnerability, I simply need to copy the complete cookie header with the tampered JWT, and create a new match and replace rule inside the proxy settings for all requests:

Jwts Match and replace rules

This rule will extract the “Cookie” header from all requests and replace it with our “Cookie” header which contains the tampered JWT.

With the rule set, all we need to do is refresh the web application, and we have taken over the account.

TL;DR

Understanding the various nuances and complexities of JWT algorithms is vital to maintaining the security and integrity of your applications. Your decision of algorithm depends on various considerations including security needs, performance considerations, and compatibility with existing systems; so neglecting to stay abreast of new best practices and vulnerabilities related to cryptographic algorithms should not be done.

In this deep dive, we explored the fundamentals of JWTs, dissected the various algorithms available for signing and encrypting them, and discussed how an attacker might exploit common implementation misconfigurations to compromise a web application environment that uses JWT.

Stay ahead of the curve by learning cryptographic techniques, industry best practices, and emerging threats – mastering JWT algorithms is key to protecting applications against evolving cyber risks while guaranteeing user data confidentiality and integrity – explore our deep dive for a thorough understanding of JWT security!

Redfox Security is a diverse network of expert security consultants with a global mindset and a collaborative culture. If you are looking to improve your organization’s security posture, contact us today to discuss your security testing needs. Our team of security professionals can help you identify vulnerabilities and weaknesses in your systems and provide recommendations to remediate them.

Join us on our journey of growth and development by signing up for our comprehensive courses.