CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2025-68158

Authlib's Amnesia: How a Cache Lookup Flaw Led to 1-Click Account Takeover

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 23, 2026·6 min read·48 visits

Executive Summary (TL;DR)

Authlib < 1.6.6 failed to bind OAuth 'state' data to user sessions when using server-side caching (e.g., Redis). Attackers could generate a valid state, send the callback URL to a victim, and hijack the account via Login CSRF.

OAuth is a complex beast, and the `state` parameter is its primary defense against Cross-Site Request Forgery (CSRF). In Authlib versions prior to 1.6.6, a critical logic error occurred when developers opted for performance by storing OAuth states in a backend cache (like Redis or Memcached) instead of cookies. The library failed to cryptographically bind the cached state to the user's browser session. This decoupling allowed attackers to generate a valid OAuth flow, harvest the URL, and trick a victim into consuming it—effectively performing a Login CSRF or account linking attack with a single click.

The Hook: When Performance Kills Security

If you build Python web applications, you probably use Authlib. It is the Swiss Army knife of OAuth and OpenID Connect, powering authentication for thousands of Flask, Django, and Starlette apps. It abstracts away the misery of the OAuth 2.0 handshake, handling the redirect dances and token exchanges so you don't have to.

The cornerstone of OAuth security is the state parameter. It is a random token generated by the client (your app) to prevent CSRF. It says, "I am the one who started this request, and I am the one finishing it." Without it, anyone could force your browser to log in to their account, or worse, link their rogue Google account to your administrative profile.

Here is the twist: Authlib handled this perfectly fine in its default mode (storing state in cookies). But developers love speed. We love scaling. So, we switch the storage backend to Redis or Memcached to keep our cookies small. In Authlib versions before 1.6.6, flipping that switch was like locking your front door but leaving the keys taped to the outside of the window. The library stopped checking who owned the state, and only checked if the state existed.

The Flaw: The Valet Parking Problem

To understand this bug, imagine a valet parking service. Normally, you hand over your keys and get a ticket (the state). When you return, you show your ticket, and the valet checks two things: 1) Is this a valid ticket? and 2) Does the face matching this ticket look like the guy who dropped off the car?

Authlib's cache-backed implementation skipped step 2.

In framework_integration.py, the logic for retrieving state data looked something like this (simplified):

# The Vulnerable Logic
def get_state_data(self, session, state):
    key = f"_state_{self.name}_{state}"
    if self.cache:
        # LOOKUP ONLY BY KEY
        value = self.cache.get(key)
        return value

Do you see the issue? The get_state_data function takes a session object, but it ignores it if a cache is configured. It constructs a lookup key based entirely on the state parameter found in the URL.

This means the state was no longer a secret bound to the user's browser session. It became a global, floating token. If I, the attacker, start a login flow, Authlib writes _state_google_XYZ123 to Redis. If I then send you a link containing state=XYZ123, Authlib looks up that key in Redis, finds it, and says, "Looks legit! Proceed."

The Code: The Smoking Gun

The fix, introduced in commit 2808378611dd6fb2532b189a9087877d8f0c0489, reveals exactly how the maintainers patched the hole. They implemented a "double-binding" strategy. Even if the heavy data lives in Redis, a reference must exist in the user's session cookie.

Here is the critical diff:

  def get_state_data(self, session, state):
      key = f'_state_{self.name}_{state}'
+     # VERIFY THE SESSION HAS THIS KEY
+     if not session.get(key):
+         return None
+
      if self.cache:
          return self.cache.get(key)

Before the patch, the code blindly trusted the cache. After the patch, the code first checks session.get(key). If the user's browser doesn't have the specific cookie indicating they started this flow, the server returns None, and the attack fails. It forces the state to exist in both the shared cache (Redis) and the private session (Cookie).

The Exploit: 1-Click Account Takeover

Let's weaponize this. We are going to perform a classic Login CSRF attack. The goal is to force the victim to log in using the attacker's credentials, or to link the attacker's social profile to the victim's existing account.

The Setup:

  1. Attacker goes to target-app.com/login/google.
  2. Target App generates state=EVIL_STATE, stores it in Redis, and redirects the Attacker to Google.
  3. Attacker captures the URL but pauses. They do not complete the login yet. They now have a valid state sitting in the Target App's Redis cache.

The Trap: 4. Attacker constructs the callback URL: https://target-app.com/authorize?code=ATTACKER_CODE&state=EVIL_STATE. 5. Attacker sends this link to the Victim via email/chat.

The Execution: 6. Victim clicks the link. 7. Target App receives the request. It extracts state=EVIL_STATE. 8. Vulnerable Logic checks Redis for _state_google_EVIL_STATE. It exists! (Because the attacker put it there). 9. Target App logs the user in.

The Payoff: If the application supports "Social Account Linking," and the victim was already logged in, the attacker's Google account is now linked to the victim's profile. The attacker can now log in as the victim at any time using their own Google credentials.

The Impact: Why We Should Panic

While NVD rates this as a Medium (5.7) because it requires user interaction, the practical impact is often Critical. Identity is the perimeter. If I can compromise your identity layer, I don't need an RCE.

In scenarios where this vulnerability is used for Account Linking, the impact is persistent Account Takeover (ATO). The attacker gains a permanent backdoor into the user's account without changing the password.

In Login CSRF scenarios (where the victim is logged in as the attacker), the impact is subtle but dangerous. The victim might upload sensitive documents or credit card details, thinking they are in their own account, effectively handing that data directly to the attacker. For SaaS platforms handling sensitive data, this is a privacy nightmare.

The Fix: Binding the Ghost

The remediation is straightforward: Update Authlib to version 1.6.6 or higher.

If you cannot update immediately, you must modify your FrameworkIntegration or custom OAuth client setup. You need to ensure that whatever storage backend you use, you are cross-referencing the state with the user's current session.

> [!NOTE] > If you are using FileStorage or SQLAlchemy storage for states, check if your implementation relies on FrameworkIntegration. The vulnerability specifically affects the default logic where cache is passed to the registry.

After patching, ensure you rotate all active sessions. The patch changes how states are validated, so pending OAuth flows might fail immediately after deployment (a small price to pay for security).

Official Patches

AuthlibGitHub Commit Diff

Fix Analysis (1)

Technical Appendix

CVSS Score
5.7/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:N/A:N
EPSS Probability
0.02%
Top 95% most exploited

Affected Systems

Flask applications using Authlib with CacheDjango applications using Authlib with CacheStarlette/FastAPI applications using Authlib with Cache

Affected Versions Detail

Product
Affected Versions
Fixed Version
Authlib
Authlib
< 1.6.61.6.6
AttributeDetail
Vulnerability TypeCWE-352: Cross-Site Request Forgery (CSRF)
Attack VectorNetwork (Pre-generated State)
CVSS v3.15.7 (Medium)
ImpactAccount Takeover / Account Linking
Fixed Version1.6.6
Affected Componentauthlib.integrations.base_client.framework_integration

MITRE ATT&CK Mapping

T1189Drive-by Compromise
Initial Access
T1550.001Application Access Token
Use Alternative Authentication Material
CWE-352
Cross-Site Request Forgery (CSRF)

The application does not verify that the OAuth state parameter is bound to the user's current session, allowing CSRF attacks.

Known Exploits & Detection

ManualThe advisory describes the Login CSRF flow in detail.
NucleiDetection Template Available

Vulnerability Timeline

Patch Committed (v1.6.6)
2025-12-12
CVE Published
2026-01-08

References & Sources

  • [1]GHSA Advisory
  • [2]NVD Entry

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 3 hours ago•CVE-2026-9639
6.5

CVE-2026-9639: Authenticated Denial of Service via Nil-Pointer Dereference in Canonical LXD Custom Volume Backup Import

CVE-2026-9639 (GHSA-j93m-3j9p-m5m8) is a null pointer dereference vulnerability in Canonical LXD affecting versions 5.0.0 through 5.21.4 and 6.0 through 6.8. The flaw resides in the (*lxdBackend).CreateCustomVolumeFromBackup method within lxd/storage/backend_lxd.go. An authenticated attacker with the can_create_storage_volumes permission can upload a crafted custom volume backup tarball that omits the expires_at field on a snapshot entry, triggering an unhandled SIGSEGV panic and terminating the LXD daemon.

Amit Schendel
Amit Schendel
5 views•9 min read
•about 4 hours ago•CVE-2026-33050
8.8

CVE-2026-33050: OS Command Injection in GeoNode Vector Upload Handler

An operating system command injection vulnerability (CWE-78) exists in GeoNode versions prior to 5.1.0. The vulnerability occurs in the vector upload processing pipeline when handling user-supplied geospatial files. By supplying a crafted layer name containing shell metacharacters, an authenticated attacker with basic data upload privileges can execute arbitrary commands on the underlying server or container hosting the GeoNode application and Celery worker processes.

Alon Barad
Alon Barad
5 views•9 min read
•about 5 hours ago•CVE-2026-46429
5.5

CVE-2026-46429: Unrestricted File Upload and Validation Bypass in GeoNode

GeoNode versions prior to 5.0.3 contain an unrestricted file upload vulnerability (CWE-434) within the document and dataset ingestion architecture. Authenticated low-privileged users can bypass upload validation via direct REST API interactions, file extension masquerading, metadata mutation via PATCH requests, or malicious archive submissions. This allows attackers to store and serve dangerous file types, alter on-disk file references, or trigger path traversal and resource exhaustion during archive extraction.

Amit Schendel
Amit Schendel
6 views•10 min read
•about 6 hours ago•CVE-2026-55649
6.3

CVE-2026-55649: Stored Cross-Site Scripting in GeoNode Document Metadata Abstract Field

CVE-2026-55649 is a stored Cross-Site Scripting (XSS) vulnerability in GeoNode versions prior to 5.0.3. The flaw exists because document abstract metadata fields are ingested via the metadata management pipeline and stored in the backend database without HTML sanitization or input neutralization. An authenticated attacker with privileges to create or edit document metadata can inject arbitrary HTML and JavaScript payloads into these fields. When another user, such as an administrator, views the affected document page or catalog listing, the unencoded payload is rendered into the Document Object Model (DOM) and executes within the victim's browser session.

Amit Schendel
Amit Schendel
7 views•9 min read
•about 7 hours ago•GHSA-QX75-2P3R-PWM5
7.7

CVE-2026-55622: Cross-Project Instance Copy Authorization Bypass in Canonical LXD and LXC Incus

CVE-2026-55622 (GHSA-qx75-2p3r-pwm5) is an improper access control vulnerability (CWE-284) in Canonical LXD and LXC Incus (tracked as GHSA-c9f5-j9c3-mhrg). In multi-tenant deployments utilizing project-based isolation, the REST API handler for instance creation via POST /1.0/instances verifies that the authenticated user has authorization in the destination project specified by the query parameter, but fails to verify whether the user holds read or view permissions for the source instance when executing a copy operation across projects. Consequently, an authenticated client restricted to a single project can clone instances or snapshots from unauthorized projects into their own project, resulting in complete disclosure of the target instance filesystem and embedded credentials.

Amit Schendel
Amit Schendel
6 views•11 min read
•about 8 hours ago•GHSA-7MR3-28H5-M5VX
7.7

GHSA-7mr3-28h5-m5vx: Authorization Bypass and Cross-Project Volume Copy in Canonical LXD

An authorization bypass vulnerability in Canonical LXD (tracked as GHSA-7mr3-28h5-m5vx and CVE-2026-55621, and as GHSA-64f3-v33m-w89f in Linux Containers Incus) allows an authenticated user with restricted project access to copy custom storage volumes and volume snapshots from unauthorized projects. The flaw resides in the storagePoolVolumesPost REST API handler, which verifies permissions on the destination project but fails to check whether the caller holds view entitlements on the source project or volume specified in the request body.

Alon Barad
Alon Barad
5 views•10 min read