CVE-2026-103001: State Pollution in PyJWT Option Merging Leads to Claim Verification Bypass
Vulnerability ID: CVE-2026-103001
CVSS Score: 6.5
Published: 2026-09-30
PyJWT versions 2.11.0 through 2.13.0 suffer from a state pollution vulnerability in the _merge_options method. When an application passes a mutable configuration mapping with signature verification disabled, the library modifies the object in-place. If this same dictionary is reused for subsequent verified decode operations, standard claim verifications (such as expiration, audience, and issuer validation) are silently bypassed.
TL;DR
A state pollution flaw in PyJWT's options merger mutates reused configuration dictionaries. When signature verification is toggled from False to True, critical claim verifications like expiration and audience checks remain permanently disabled, allowing expired or invalid tokens to be accepted.
⚠️ Exploit Status: POC
Technical Details
- CWE ID: CWE-471: Modification of Assumed-Immutable Data (MAID)
- Attack Vector: Network (Unauthenticated, high complexity due to application logic dependence)
- CVSS v3.1: 6.5 (Medium)
- EPSS Score: N/A
- Impact: Claim verification bypass (Expiration, Audience, Issuer checks bypassed)
- Exploit Status: Proof-of-Concept (PoC) available
- CISA KEV Status: Not Listed
Affected Systems
- Python applications utilizing PyJWT version 2.11.0 through 2.13.0 with reused or shared verification options dictionaries
-
PyJWT: >= 2.11.0, <= 2.13.0 (Fixed in:
2.14.0)
Code Analysis
Commit: 0c87c8c
Fix options modification in merge_options
@@ -76,16 +76,18 @@ def _merge_options(self, options: Options | None = None) -> FullOptions:
if options is None:
return self.options
+ merged_options = cast("Options", dict(options))
+
# (defensive) set defaults for verify_x to False if verify_signature is False
- if not options.get("verify_signature", True):
- options["verify_exp"] = options.get("verify_exp", False)
- options["verify_nbf"] = options.get("verify_nbf", False)
- options["verify_iat"] = options.get("verify_iat", False)
- options["verify_aud"] = options.get("verify_aud", False)
- options["verify_iss"] = options.get("verify_iss", False)
- options["verify_sub"] = options.get("verify_sub", False)
- options["verify_jti"] = options.get("verify_jti", False)
- return {**self.options, **options}
+ if not merged_options.get("verify_signature", True):
+ merged_options["verify_exp"] = merged_options.get("verify_exp", False)
+ merged_options["verify_nbf"] = merged_options.get("verify_nbf", False)
+ merged_options["verify_iat"] = merged_options.get("verify_iat", False)
+ merged_options["verify_aud"] = merged_options.get("verify_aud", False)
+ merged_options["verify_iss"] = merged_options.get("verify_iss", False)
+ merged_options["verify_sub"] = merged_options.get("verify_sub", False)
+ merged_options["verify_jti"] = merged_options.get("verify_jti", False)
+ return {**self.options, **merged_options}
Exploit Details
- GitHub Issue Tracker (Options Mutation Case): Original issue track outlining persistent side-effects on state dictionaries during merging procedures.
Mitigation Strategies
- Upgrade PyJWT to version 2.14.0 or above
- Ensure options dictionaries are not reused across sequential jwt.decode() or jwt.decode_complete() invocations
- Enforce explicit creation of option dictionary copies inside application middleware config builders
Remediation Steps:
- Locate requirement files (such as requirements.txt, setup.py, or Pipfile) and bump the pyjwt dependency to '>= 2.14.0'
- Run pip install --upgrade pyjwt in the development environment and verify the installed package version
- Audit JWT handling middleware to ensure options arguments are not stored globally or in persistent application contexts
References
- GitHub Security Advisory: Claim Verification Bypass due to State Pollution
- Fix commit implementing dict conversion within _merge_options
- PyJWT Issue 679: Dictionary Mutated in-place During Decoding Process
- Official CVE-2026-103001 Record
Read the full report for CVE-2026-103001 on our website for more details including interactive diagrams and full exploit analysis.
Top comments (0)