Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessIdentify app reusing shared options mapping
-
DeliveryObtain validly-signed token with invalid claims (expired/wrong aud)
-
ExploitApp's prior verify_signature=false call disables claim checks
-
ExecutionSubmit token to later verify_signature=true decode
-
PersistStale false flags skip claim validation
-
ImpactInvalid-claim token accepted
Vulnerability Assessment
| Weakness |
CWE
|
| Exploitation Conditions | Requires PyJWT 2.11.0 through 2.13.0 AND a specific application coding pattern: the consuming app must (1) call decode()/decode_complete() with a mutable options mapping where verify_signature is false, then (2) reuse that SAME mapping object for a subsequent call after setting verify_signature to true. Under those conditions _merge_options() has already mutated the mapping so verify_exp/verify_nbf/verify_iat/verify_aud/verify_iss/verify_sub/verify_jti stay false. Limiting factors: applications that create a fresh options mapping per decode call are entirely unaffected; the token must still carry a valid signature for the correct key (this is not a signature bypass); and the payoff is limited to bypassing registered-claim checks (accepting expired/wrong-audience/wrong-issuer tokens). |
| Impact | This is a genuine but conditional integrity flaw, not a broad-impact critical. The CVSS 3.1 vector (AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N, base 6.5) correctly captures the tension: it is network-reachable and needs no privileges (PR:N), but AC:H reflects that exploitation depends entirely on a specific application-side anti-pattern - the app must reuse the SAME mutable options mapping across decode() calls and toggle verify_signature from false back to true. Applications that build a fresh options dict per call (the documented/common usage) are not affected at all. Critically, this is NOT a signature-verification bypass: the attacker's token must still be validly signed with the correct key; only the registered-claim checks (exp/nbf/iat/aud/iss/sub/jti) get silently disabled. No EPSS score, no CISA KEV listing, and no public exploit code were provided - exploitation status is 'no public exploit identified at time of analysis'. The 'Information Disclosure' tag understates and mischaracterizes the issue: the impact is integrity of claim validation (I:H, C:L), i.e. accepting expired or wrong-audience/issuer tokens, not data leakage. Net real-world priority is low-to-medium: high-prevalence library, but the exploit is gated behind a non-default coding mistake in the consuming application. |
| Exploit ScenarioDerived | A Python service using PyJWT 2.11.0-2.13.0 constructs one options dict (e.g. {'verify_signature': False}) to peek at a token, then reuses that same dict object for a later authenticated decode() with verify_signature flipped to true. Because the earlier call mutated the dict in place, verify_exp/aud/iss/etc. remain false, so the service now accepts a correctly-signed but expired or wrong-audience token. No public exploit code exists; success requires the attacker to hold a validly signed token whose registered claims would normally be rejected. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Sep 30, 2026
Reuse of a caller-supplied options mapping in PyJWT 2.11.0 through 2.13.0 can silently leave registered-claim verification disabled on subsequent decode calls, allowing a correctly signed but otherwise invalid token to be accepted. When an application first calls decode() or decode_complete() with a mutable options mapping containing verify_signature set to false, PyJWT._merge_options() mutates that same mapping in place, writing verify_exp, verify_nbf, verify_iat, verify_aud, verify_iss, verify_sub, and verify_jti to false; if the application then reuses that identical mapping for a later call after flipping verify_signature to true, the stale false flags persist and expired, not-yet-valid, wrong-audience, wrong-issuer, or wrong-subject tokens are accepted even though signature verification is nominally on. This is a conditional integrity flaw rather than a signature bypass, it is reachable by unauthenticated remote attackers (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N) but only in applications that reuse a mutable options mapping, and no public exploit code or confirmed active exploitation was identified at time of analysis.
RemediationAI
Primary fix: upgrade PyJWT past the affected 2.11.0-2.13.0 range to a release that includes upstream commit 0c87c8c8b1a74cac99ad8115f3050efcb7fbed35, which copies the options mapping before mutating it - the fix is upstream-verified via that commit and its test suite, but a released patched version number was not independently confirmed, so verify the version present in your environment rather than assuming an unbumped 2.13.0 is safe. Because the flaw is a caller-side mutation pattern, an effective and low-cost compensating control that can be applied immediately without upgrading is to never share a mutable options mapping across decode calls: construct a fresh dict per invocation, pass the options literal inline, or hand the library a copy (for example decode(token, key, algorithms=[...], options=dict(options))), which fully neutralizes the stale verify_* flags at the price of a trivial per-call allocation. A second control is to avoid a workflow that first disables verify_signature and later flips it back to true on the same mapping object; if such a workflow is unavoidable, re-assert all seven claim switches (verify_exp, verify_nbf, verify_iat, verify_aud, verify_iss, verify_sub, verify_jti) explicitly on the second call rather than assuming defaults, since the vulnerable library will not reset them. Audit code paths and dependency wrappers that cache or module-globalize options dictionaries, as those are the realistic carriers of this pattern, and monitor the vendor advisory https://github.com/jpadilla/pyjwt/security/advisories/GHSA-gvp8-978c-rx2q and issue https://github.com/jpadilla/pyjwt/issues/679 for the corresponding package release.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in pypi
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
The flaw is an instance of CWE-471 (Modification of Assumed-Immutable Data, also tracked as MAID), and it lives in the option-merging logic of PyJWT's PyJWT class in jwt/api_jwt.py. Prior to the fix, _merge_options() read the caller's options mapping directly and, whenever verify_signature was falsy, wrote default false values back into that same object for the seven registered-claim switches (verify_exp, verify_nbf, verify_iat, verify_aud, verify_iss, verify_sub, verify_jti) before returning {self.options, options}. Because those writes targeted the caller's dict rather than a private copy, the mutation escaped the library call and became observable to the application's own variable - a classic assumed-immutable-data violation in a Python dict, which is a mutable MutableMapping. The upstream patch (commit 0c87c8c8b1a74cac99ad8115f3050efcb7fbed35) introduces merged_options = cast("Options", dict(options)) and performs all reads and writes against that copy, leaving the caller's mapping untouched; accompanying tests exercise a plain dict, a custom MutableMapping without copy semantics, and the PyJWT constructor to confirm no input mutation occurs. Notably, signature cryptography itself is not weakened - the token must still carry a valid signature for the key the application supplies, and the vulnerability only removes the registered-claim validation layer. The underlying technology is JSON Web Token processing in Python; per the CPE data (cpe:2.3:a:jpadilla:pyjwt:*:*:*:*:*:*:*:*) PyJWT is the sole affected component, and the description bounds the affected range to releases 2.11.0 through 2.13.0.
Know when Pyjwt gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N
AC:H because it needs a non-default option-reuse-and-toggle pattern in the app; PR:N/UI:N as the attacker just submits a signed token; I:H for claim-check bypass, C:L, A:N.
Primary rating from Vendor (GitHub_M).
CVSS VectorVendor: GitHub_M
DescriptionCVE.org
PyJWT is a Python implementation of JSON Web Token standards. From 2.11.0 through 2.13.0, PyJWT's PyJWT._merge_options() method can modify a caller-supplied mutable options mapping when verify_signature is false. If an application reuses that same mapping for a later decode() or decode_complete() call and changes verify_signature to true, the mapping can retain false values for expiration, not-before, issued-at, audience, issuer, subject, and JWT ID checks. A signed token with invalid registered claims can then be accepted without disabling signature verification, but applications that create a fresh options mapping for each call are not affected.
Blast Radius
ecosystem impact- 45 pypi packages depend on pyjwt (40 direct, 5 indirect)
Ecosystem-wide dependent count for version 2.11.0.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Information disclosure occurs when an application unintentionally exposes sensitive data that aids attackers in reconnaissance or directly compromises security.
Forged HS256 JSON Web Tokens can be accepted as authentic by PyJWT (pip package PyJWT) versions 2.13.0 through 2.13.x wh
Improper validation in PyJWT 2.1.0 through 2.14.0 allows an attacker who already holds a stolen sender-constrained (DPoP
Denial of service in PyJWT 2.9.0 through 2.13.0: a single malformed RSA JWK embedded in a JWK Set aborts construction of
Unauthenticated denial of service in the PyJWT Python library (versions 2.0.0a1 through 2.14.0) allows a remote attacker
pyjwt is a JSON Web Token implementation in Python. Rated high severity (CVSS 7.5), this vulnerability is remotely explo
Authentication bypass in PyJWT versions prior to 2.13.0 allows remote attackers to forge valid JSON Web Tokens by exploi
Malformed JSON Web Token headers can cause an uncaught RecursionError in PyJWT 2.13.0 through versions before 2.14.0, re
Uncontrolled resource consumption in PyJWT 2.8.0-2.12.1 exposes any service that verifies detached JWS tokens to unauthe
Signature forgery in PyJWT before 2.14.0 allows an attacker who knows a target's asymmetric public key to forge HMAC-aut
JWKS trust-boundary bypass in PyJWT's PyJWKClient allows a configured trusted JWKS endpoint that returns an HTTP 3xx red
Unauthenticated attackers can amplify outbound HTTP traffic from any application that validates JWTs with PyJWT's PyJWKC
In PyJWT 1.5.0 and below the `invalid_strings` check in `HMACAlgorithm.prepare_key` does not account for all PEM encoded
EUVD-2026-90388
GHSA-gvp8-978c-rx2q
NVD
CVE.org
EPSS
Vulnrichment
Exploit-DB