Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-75554

CVE-2026-75554: Hexpm OAuth Token Auth Bypass Vulnerability

CVE-2026-75554 is an authentication bypass in Hexpm OAuth token refresh that lets removed organization members retain private package access. This post covers technical details, affected versions, impact, and mitigation.

Published:

CVE-2026-75554 Overview

CVE-2026-75554 is an Insufficient Session Expiration vulnerability [CWE-613] in the OAuth token refresh grant of hexpm, the package manager service for the Erlang and Elixir ecosystems. A user removed from an organization can continue reading its private packages by repeatedly refreshing an OAuth token. The refresh grant re-derives access tokens from granted_scopes without re-checking organization membership. Both content delivery network (CDN) edges authorize access from the token claim alone, so revocation of membership does not take effect until the refresh token expires.

Critical Impact

A removed organization member retains read access to private packages for up to the 30-day refresh token lifetime instead of the intended 30-minute access token lifetime.

Affected Products

  • hexpm hexpm (hex.pm service)
  • Versions from 2025-10-10 before 2026-08-24
  • Private package repositories served through the hex.pm CDN

Discovery Timeline

  • 2026-08-24 - Patch commit 50cffd2 merged in the hexpm repository
  • 2026-08-24 - CVE-2026-75554 published to NVD
  • 2026-08-25 - Last updated in NVD database

Technical Details for CVE-2026-75554

Vulnerability Analysis

The vulnerability resides in the OAuth authorization pipeline of hexpm. The function expand_repositories_scope/3 in lib/hexpm/permissions.ex rewrites only the literal repositories scope. An explicitly granted repository:<org> or docs:<org> scope passes through unchanged. The refresh grant then re-derives a new access token from the stored granted_scopes, which already contains the expanded organization scope. Every refresh reproduces the organization scope without revisiting membership state in the database. Because both CDN edges authorize repository access from the token claim without querying the database, revocation of organization membership does not propagate to the token subject. This is a session management flaw classified under [CWE-613].

Root Cause

The stored granted_scopes list acts as an authoritative record of prior authorization. The refresh handler treats it as a trust anchor rather than as a request that must be re-validated. Membership changes performed after issuance are not evaluated during refresh. The design assumes scope expansion is idempotent, but explicit organization scopes bypass expansion entirely.

Attack Vector

An authenticated user who was previously a member of a private organization holds a refresh token containing repository:<org> or docs:<org> in granted_scopes. After removal from the organization, the user submits the refresh token to the OAuth token endpoint. The server returns a new access token bearing the organization scope. The user presents that token to the CDN edge and downloads private packages or documentation. The user repeats the refresh cycle until the 30-day refresh token expires.

text
// Security patch in lib/hexpm/oauth/tokens.ex
     expires_at = DateTime.add(DateTime.utc_now(), expires_in, :second)
 
     # Expand "repositories" scope to individual "repository:{org}" scopes for access tokens
-    # This allows edge verification without database lookups
-    expanded_scopes = Permissions.expand_repositories_scope(user, scopes)
+    # This allows edge verification without database lookups, so the expanded set
+    # is then held against current membership: `granted_scopes` carries an
+    # explicit organization scope across every refresh, and expansion does not
+    # revisit it.
+    expanded_scopes =
+      user
+      |> Permissions.expand_repositories_scope(scopes)
+      |> Permissions.reject_unaffiliated_scopes(user)

Source: GitHub Commit 50cffd2

Detection Methods for CVE-2026-75554

Indicators of Compromise

  • Successful OAuth refresh_token grants issued to user identifiers that no longer appear in an organization's membership table.
  • CDN access logs showing repository:<org> or docs:<org> package pulls by accounts whose organization_users row was deleted more than 30 minutes prior.
  • Sustained refresh activity from a single account against private repositories that the account has no current entitlement to read.

Detection Strategies

  • Join OAuth token issuance logs with the organization_users audit table to find refreshes that produced organization scopes for non-members.
  • Alert on any package download where the requesting subject was removed from the target organization after the refresh token was originally issued.
  • Baseline normal refresh cadence per user and flag accounts that refresh unusually often after a membership change event.

Monitoring Recommendations

  • Ingest hexpm OAuth issuance, refresh, and CDN edge authorization events into a centralized data lake for correlation.
  • Retain organization membership change events for at least 30 days to cover the refresh token lifetime.
  • Report all private package reads by former organization members within the past 30 days for retrospective investigation.

How to Mitigate CVE-2026-75554

Immediate Actions Required

  • Deploy the hexpm build that includes commit 50cffd2 or later, which introduces reject_unaffiliated_scopes/2 and re-checks membership on refresh.
  • Revoke all outstanding refresh tokens for users who were removed from organizations on or after 2025-10-10.
  • Rotate any private package publishing credentials that may have been exposed to former members during the affected window.

Patch Information

The fix is applied in lib/hexpm/oauth/tokens.ex and lib/hexpm/permissions.ex. The patched issuance path pipes the expanded scope list through Permissions.reject_unaffiliated_scopes(user), dropping any repository:<org> or docs:<org> scope for organizations the user no longer belongs to. See the GitHub Security Advisory GHSA-24ww-j3f4-p49c and the CNA advisory for CVE-2026-75554 for the full remediation record.

Workarounds

  • When removing a user from an organization, explicitly revoke that user's refresh tokens through the hexpm administrative interface.
  • Shorten refresh token lifetime for organizations that manage sensitive private packages until the patched service version is confirmed in production.
  • Rotate private organization repository names or force re-authentication of all members after high-risk personnel changes.
bash
# Verify the running hexpm service includes the fix commit
git -C /opt/hexpm log --oneline | grep 50cffd2

# Revoke refresh tokens for a removed user (illustrative mix task pattern)
mix hexpm.oauth.revoke_refresh_tokens --user-id <REMOVED_USER_ID>

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.