Skip to main content

PyJWT EUVDEUVD-2026-90388

| CVE-2026-103001 MEDIUM
Modification of Assumed-Immutable Data (MAID) (CWE-471)
2026-09-30 GitHub_M GHSA-gvp8-978c-rx2q
6.5
CVSS 3.1 · Vendor: GitHub_M
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Identify app reusing shared options mapping
  2. Delivery
    Obtain validly-signed token with invalid claims (expired/wrong aud)
  3. Exploit
    App's prior verify_signature=false call disables claim checks
  4. Execution
    Submit token to later verify_signature=true decode
  5. Persist
    Stale false flags skip claim validation
  6. Impact
    Invalid-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.

This Month Plan within 30 days. Why: MEDIUM severity (CVSS 4.0-6.9).
Patch available 0.4% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – 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

pyjwt 2.11.0, ≤ 2.13.0

Affected Packages

pypi pyjwt no fix

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

Vendor (GitHub_M) PRIMARY 6.5 MEDIUM
AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N
vuln.today AI 6.5 MEDIUM

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.

3.1 AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N
4.0 AV:N/AC:H/AT:N/PR:N/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N
Red Hat 6.5 MEDIUM

Primary rating from Vendor (GitHub_M).

CVSS VectorVendor: GitHub_M

Attack Vector
Network
Attack Complexity
High
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
High
Availability
None

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
† from your stack dependencies † transitive graph · vuln.today resolves 4-path depth
  • 45 pypi packages depend on pyjwt (40 direct, 5 indirect)

Ecosystem-wide dependent count for version 2.11.0.

Threat Intelligence Overview

EPSS Score
0.4%
Top 72% exploitable
Prevalence
High
PyJWT is the de-facto standard JWT library across Python web/API stacks
Threat Intel
1
1 sources
Exploit
POC
Public exploit code exists
Social Media
1
Sightings
Data Quality
6 sources
Confidence: High

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.

SignalValue
Severity MEDIUM
CISA KEV No
Public POC Yes
EPSS 0.4%
Patch available Yes
Active threat No
Deciding rule
MEDIUM severity (CVSS 4.0-6.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Scheduled
Exploitation
Poc
System Exposure· derived
Open
Automatable
No
Human Impact· derived
High
Technical Impact
Partial

Priority Score

52
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +32
PoC: +20
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Information disclosure occurs when an application unintentionally exposes sensitive data that aids attackers in reconnaissance or directly compromises security.

More in Pyjwt

View all
CVE-2026-102266 CRITICAL POC
9.1 Sep 28

Forged HS256 JSON Web Tokens can be accepted as authentic by PyJWT (pip package PyJWT) versions 2.13.0 through 2.13.x wh

CVE-2026-102275 HIGH POC
8.2 Sep 28

Improper validation in PyJWT 2.1.0 through 2.14.0 allows an attacker who already holds a stolen sender-constrained (DPoP

CVE-2026-102274 HIGH POC
7.5 Sep 28

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

CVE-2026-101918 HIGH POC
7.5 Sep 28

Unauthenticated denial of service in the PyJWT Python library (versions 2.0.0a1 through 2.14.0) allows a remote attacker

CVE-2024-53861 HIGH POC
7.5 Nov 29

pyjwt is a JSON Web Token implementation in Python. Rated high severity (CVSS 7.5), this vulnerability is remotely explo

CVE-2026-48526 HIGH POC
7.4 May 28

Authentication bypass in PyJWT versions prior to 2.13.0 allows remote attackers to forge valid JSON Web Tokens by exploi

CVE-2026-102265 MEDIUM POC
5.3 Sep 28

Malformed JSON Web Token headers can cause an uncaught RecursionError in PyJWT 2.13.0 through versions before 2.14.0, re

CVE-2026-48525 MEDIUM POC
5.3 May 28

Uncontrolled resource consumption in PyJWT 2.8.0-2.12.1 exposes any service that verifies detached JWS tokens to unauthe

CVE-2026-102268 CRITICAL
9.1 Sep 28

Signature forgery in PyJWT before 2.14.0 allows an attacker who knows a target's asymmetric public key to forge HMAC-aut

CVE-2026-102267 CRITICAL
9.1 Sep 28

JWKS trust-boundary bypass in PyJWT's PyJWKClient allows a configured trusted JWKS endpoint that returns an HTTP 3xx red

CVE-2026-101917 HIGH
7.5 Sep 28

Unauthenticated attackers can amplify outbound HTTP traffic from any application that validates JWTs with PyJWT's PyJWKC

CVE-2017-11424 HIGH
7.5 Aug 24

In PyJWT 1.5.0 and below the `invalid_strings` check in `HMACAlgorithm.prepare_key` does not account for all PEM encoded

Vendor StatusVendor

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy