Security Issue in JWT Secret Poisoning (Updated)

A pictorial representation of the JsonWebToken vulnerability.

This post is also available in: 日本語 (Japanese)

Updates

Jan. 30, 2023

After hearing the community's feedback about the prerequisites of the exploitation scenario of the vulnerability, we made the decision to work with Auth0 to retract CVE-2022-23529. 

The security issue described in this blog remains a concern when the JsonWebToken library is used in an insecure way. In that scenario, if all the prerequisites are met, the issue may be exploitable. We agree that the source of this risk in that case will be in the calling code, and not in the library.

Important security checks were added to the JsonWebToken code to address this issue. 

Users of jsonwebtoken 8.5.1 and earlier are encouraged to update to the latest version, 9.0.0, which presents safer code that fixes this security flaw and others, and prevents misuse of the package that was presented in this blog. 

We want to thank Auth0 for their work to address the security issue, as well as the security community for the interest and feedback. We would also like to thank GitHub for their help. The update can be read on the Auth0 GitHub.

 

Jan. 12, 2023

After receiving feedback from the community, we decided to make some clarifications regarding possible exploitation. We originally mentioned that an attacker needs to have control over the secret manager and decided that there was a practical need to make this even more clear in our language and associated figures.

Executive Summary

Unit 42 researchers discovered a new vulnerability in the popular JsonWebToken open source project. The vulnerability is identified as CVE-2022-23529, rated high severity (CVSS 7.6).

By exploiting this vulnerability, attackers could achieve remote code execution (RCE) on a server verifying a maliciously crafted JSON web token (JWT) request. This vulnerability requires several prerequisites in order to be exploitable, which makes it less likely for an attacker to use it in the wild.

If you are using JsonWebToken 8.5.1 or an earlier version, we suggest updating to JsonWebToken version 9.0.0, which includes a fix for this vulnerability.

JsonWebToken is an open source JavaScript package that allows you to verify/sign JWTs, which are mainly used for authorization and authentication purposes. Developed and maintained by Auth0, the package had over 9 million weekly downloads at the time of writing, and over 20,000 dependents (according to the JsonWebToken page). This package plays a big role in the authentication and authorization functionality for many applications.

Palo Alto Networks customers can identify assets that are running vulnerable versions of the JsonWebToken package with Prisma Cloud, and they can identify the relevant CVE within scan results.

Related Unit 42 Topics  CVE-2022-23529, remote code execution, open source, cloud

Table of Contents

JWT 101
How Does the Authentication Process Work?
JWT and Open Source
The Vulnerability (CVE-2022-23529)
Exploitation Prerequisites
JsonWebToken Fix
Disclosure Process
Conclusion

JWT 101

JWT (pronounced “jot”) is an open standard that defines a method of transferring information securely by encoding and signing JSON data. JWTs have a string structure that consists of 3 parts separated by a dot (.):

Header.Payload.Signature

JWTs are used to transmit different types of information, but are mainly used to deliver “claims,” which are pieces of information about some subject. In practice, this will most likely contain useful information about a user.

The most common use case of JWTs is for authorization and authentication. Let’s quickly go over the JWT structure.

JWT Header

As shown in Figure 1, the header consists mostly of two parameters that indicate the type of the token and the signing algorithm.

Image 1 is a screenshot of two lines of code enclosed in curly brackets. The first line is “alg: HS256” and the second line is “top” JWT.”
Figure 1. JWT header.

JWT Payload

The payload (shown in Figure 2) is the second part of the token, which will contain the claims. In most cases, this will provide useful information about a user.

Image 2 is a screenshot of two lines of code enclosed in curly brackets. The first line is “user_name: John Doe” and the second line is “admin: true.”
Figure 2. JWT payload.

There are three different types of claims: registered, public and private. You can find more information about them in RFC 7519.

The header and payload are each Base64Url encoded to form the first and second parts of a JWT string (Header.Payload.Signature):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 and eyJ1c2VyX25hbWUiOiJKb2huIERvZSIsImFkbWluIjp0cnVlfQ.

The third part of the JWT, the signature, is then computed (signed) by the following formula using a secret key. A signature is used to verify that the token isn’t forged or manipulated. Because it is signed with a secret key, we can validate the authenticity of the sender. Here is an example of a signature calculation: 

base64Url(HMACSHA256(base64Url(header) + ”.” + base64Url(payload), secret_key)) = epQym-1JoN9eep458VBZw4-cVhwfmsI1cfaGa6PE818

Putting it all together, we will get a JWT in its complete form, as shown in Figure 3.

Image 3 is a screenshot of a JSON web token string with the different parts, header, payload and signature red, green and cyan respectively.
Figure 3. Full JSON web token.

How Does the Authentication Process Work?

Let’s take a look at a simple authentication process using a JSON web token (also shown in Figure 4):

  1. To access a protected resource, the user will log in using credentials, usually username and password.
  2. A request containing this information will be sent to the authentication server.
  3. An authentication server will validate the information sent within a request and issue a JWT signed with a secret key, which can be stored on a server or in a different location using a secret manager.
  4. From now on, each user request will contain a JWT in the authorization header. This way, users with the right permissions can receive access to protected resources.
  5. When a user requests access to a protected resource, a request containing a JWT will be generated from the application to the JWT authentication server.
  6. Before the user receives access to a requested resource, the JWT that was sent in the authorization header will be verified using the secret key. This is done to verify it was not tampered along the way and that the user has the right permissions to view the requested information. In the node.js JsonWebToken package, this is done with the verify() function.
Image 4 is a diagram showing the process of JWT authentication, starting with the user and their credentials. The process follows from the server issuing the JWT to the authentication server.
Figure 4. Diagram of the process of authentication with JWT.

JWT and Open Source

Open source projects help a lot of organizations to save time and other resources. At times, this can serve as an elegant and quick problem solver. JWT became a very popular technology that many organizations rely on, and this is one of the reasons that some of the open source projects implementing JWT have become a great success.

One such project is a well-known JavaScript solution for signing, verifying and decoding JWTs named JsonWebToken. This tool is developed and maintained by Auth0, part of Okta.

The Vulnerability (CVE-2022-23529)

Typically, attacks on JWT will involve different forgery techniques abusing buggy JWT implementations. These kinds of attacks have severe consequences because, in most cases, a successful attack allows an attacker to bypass authentication and authorization mechanisms to access confidential information or steal and/or modify data.

One of the methods provided by the JsonWebToken package is verify. The verify method receives three parameters: token, secretOrPublicKey and options. This function verifies the validity of the JWT and returns the decoded payload part.

According to the documentation, secretOrPublicKey is a string or buffer. We can see the lines of code shown in Figure 5 in the JsonWebToken verify.js source code.

Image 5 is a screenshot of 4 lines of code taken from verify.js showing the secretOrPublicKey.
Figure 5. Code snippet from verify.js.

When no allowed algorithms are provided within the options algorithms list, the values within the privacy enhanced mail (PEM) file, which is provided by the secretOrPublicKey parameter, will be assigned instead. This presents a problem: There is no check in place that secretOrPublicKey is actually a valid PEM file’s content, and this unverified object’s toString method is blindly being used. Attackers with control over this object can supply their own toString method, which will then be executed by JsonWebToken’s verify in line 114.

Let's observe the following scenario, using node.js (a JavaScript runtime environment) version 18.9.1 and JsonWebToken package version 8.5.1, and see what will happen if we pass a malicious object to the verify function via the secretOrPublicKey parameter and override its toString() method.

Image 6 is a screenshot of many lines of code where the researchers use JsonWebToken to execute a verify function with a malicious object.
Figure 6. Arbitrary Write File PoC.

Our malicious code will execute and exit the node process before the .includes(‘BEGIN CERTIFICATE’)check in the verify function, resulting in an arbitrary write file on the hosting machine.

Image 7 is a screenshot of the title bar and contents of an arbitrary write file. The file name is “malicious.text” and the text is “PWNED! ! ! ! Arbitrary File Write on the host machine.”
Figure 7. PoC result, file created.

With the same technique, it is possible to achieve RCE, but we will have to slightly modify our payload by using the child_process module (as shown in Figure 8).

Image 8 is a screenshot of 3 lines of code where the payload has been modified using the child_process module.
Figure 8. RCE PoC.

Secrets can be stored outside of the authenticating server – for example, they can be kept within a secret manager in a different location. An attacker that only had write access to the secret manager can now execute code on the authentication server. If there was a check in place that the malicious object was a valid secret, code execution was not possible.

Exploitation Prerequisites

In the previous section, we demonstrated how a poisoned secret key could lead to an RCE. In reality, keeping and maintaining secret keys usually involves secret managers, secret key rotations, encryption and other best practices. 

In order to exploit the vulnerability described in this post and control the secretOrPublicKey value, an attacker will need to have control or exploit a flaw within the secret management process. Even then, exploitation might be complicated as the output of a secret manager is uncertain. It can be of a type not suited for an exploitation (a string, for example) or many other possible scenarios. 

Due to the complexity of exploitation of this vulnerability, we initially suggested a CVSS score of 6.6. However, we decided to accept the CVSS calculation made by the vendor, which yielded a higher severity of 7.6.

JsonWebToken Fix

JsonWebToken version 9.0.0 contains the following fix:

Image 9 is a screenshot of multiple lines of code. It is the JsonWebToken patch released with version 9.0.0 where checks for the type of secretOrPublickey are now in place.
Figure 9. Patch.

The vulnerable code was removed and replaced with checks for the type of secretOrPublickey parameter, which prevents secretOrPublicKey from containing malicious objects.

Disclosure Process

  • July 13, 2022 – Unit 42 researchers sent a disclosure to the Auth0 team under responsible disclosure procedures
  • July 27, 2022 – Auth0 team updated that the issue was under review
  • Aug. 23, 2022 – Unit 42 researchers sent an update request
  • Aug. 24, 2022 – Auth0 team updated that the engineering team was working on the resolution
  • Dec. 21, 2022 – A patch was provided by the Auth0 engineering team

Conclusion

Open source projects are commonly used as the backbone of many services and platforms today. This is also true for the implementation of sensitive security mechanisms such as JWT, which play a huge role in authentication and authorization processes.

Security awareness is crucial when using open source software. Reviewing commonly used security open source implementations is necessary for maintaining their dependability, and it's something the open source community can take part in.

As part of the commitment of Palo Alto Networks to open source software security, we regularly conduct security research efforts that include identifying security vulnerabilities in open source projects.

Palo Alto Networks Prisma Cloud customers can detect affected images and hosts under the Vulnerabilities tab. The platform detects JsonWebToken packages and alerts on entities running with a vulnerable version. In addition, our users can search for CVE-2022-23529 in the Vulnerability Explorer section to discover more details about the vulnerability and assets affected by it.

We would like to thank the Auth0 team for professionally handling the disclosure process and providing a patch for the reported vulnerability.

Updated January 12, 2023, at 3:31 p.m. PT.
Updated January 30, 2023, at 7:20 a.m. PT.