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



GHSA-FPW6-HRG5-Q5X5
7.4

GHSA-FPW6-HRG5-Q5X5: Irrevocable Access Tokens and Nil-Pointer Dereference in Ech0

Amit Schendel
Amit Schendel
Senior Security Researcher

May 8, 2026·7 min read·3 visits

PoC Available

Executive Summary (TL;DR)

A missing expiration claim in Ech0's 'never expire' JWTs causes panics and silently breaks token revocation, allowing attackers to maintain perpetual access with stolen tokens.

Ech0 access tokens created with the 'never expire' option generate JSON Web Tokens (JWT) missing the 'exp' claim. This structural omission causes a nil-pointer dereference during logout and prevents the JTI blacklisting mechanism from functioning. Consequently, leaked access tokens cannot be revoked by administrators.

Vulnerability Overview

Ech0 is an open-source, self-hosted publishing platform that provides token-based authentication for API and CLI integrations. Users generate these access tokens via the administration interface and can configure them to "never expire" based on integration requirements. The platform utilizes stateless JSON Web Tokens (JWT) to track and manage these authenticated sessions across application endpoints.

When a user selects the "never expire" option, the application issues a JWT that completely omits the standard exp (expiration) claim. This specific structural choice creates immediate compatibility issues with the platform's internal security controls. The underlying vulnerability maps to CWE-613 (Insufficient Session Expiration), CWE-476 (NULL Pointer Dereference), and CWE-300 (Lack of Revocation for Stolen Credentials).

The flaw manifests concurrently across multiple components of the Ech0 architecture. The absence of the expiration claim breaks the logic responsible for calculating remaining token validity, causing runtime panics during logout procedures. Simultaneously, the token revocation mechanism fails entirely, allowing an attacker who compromises a "never expire" token to maintain perpetual system access.

Root Cause Analysis

The root cause is a fundamental disconnect between stateless JWT mechanics and the system's token blacklisting implementation. The application relies on a JTI (JWT ID) blacklist to revoke active tokens before their natural expiration date. The repository method RevokeToken explicitly requires a positive remaining Time-to-Live (TTL) integer to store the JTI in the caching layer.

During a standard logout event, the handler located at internal/handler/auth/auth.go intercepts the JWT and extracts the ExpiresAt data structure. The application attempts to calculate the remaining TTL by evaluating time.Until(claims.ExpiresAt.Time). Because the exp claim is strictly absent from "never expire" tokens, the ExpiresAt pointer evaluates directly to nil.

Dereferencing this nil pointer triggers a fatal HTTP 500 panic within the Go runtime environment. Even if the panic is artificially suppressed, the resulting TTL calculation yields a zero or negative duration. The RevokeToken function contains a guard clause that silently discards any revocation request where remainTTL <= 0, ensuring the JTI is never populated into the blacklist.

Furthermore, the administrative token deletion interface operates independently of the JWT validation layer. When an administrator deletes an access token via the user interface, the application drops the token metadata from the relational database. It fails to extract the underlying JTI and propagate it to the revocation service, leaving the cryptographically valid JWT fully operational.

Code Analysis

The vulnerability exists across both the token issuance and revocation code paths. The vulnerable token generation logic omitted the ExpiresAt field entirely when a user requested an indefinite lifespan. The patch at commit eab62379c795c3f4850a9ca938ae3f27d7171541 resolves this by injecting a fixed 100-year fallback duration for these specific tokens.

The following code segment illustrates the correction applied within the CreateAccessClaimsWithExpiry function. The developer established a constant neverExpiryFallback and enforces it when the requested expiry evaluates to zero or less.

// PATCH: Assign a 100-year fallback instead of omitting the claim
const neverExpiryFallback = int64(100 * 365 * 24 * 3600)
if expiry <= 0 {
    expiry = neverExpiryFallback
}

To prevent panics from legacy tokens already in circulation, the patch introduces a safe parsing mechanism. The remainingTTLFromClaims function verifies the pointer status before attempting the dereference operation. If ExpiresAt evaluates to nil, it assumes the legacy 100-year fallback duration, allowing the revocation process to continue safely.

// PATCH: Safely calculate TTL without panicking on nil pointers
func remainingTTLFromClaims(expiresAt *jwt.NumericDate) time.Duration {
    const legacyNeverFallback = 100 * 365 * 24 * time.Hour
    if expiresAt == nil {
        return legacyNeverFallback
    }
    return time.Until(expiresAt.Time)
}

The administrative deletion gap was independently addressed by modifying the DeleteAccessToken service logic. The updated code now queries the database for the token's specific JTI prior to executing the deletion command. It subsequently invokes the RevokeToken service explicitly, ensuring the stateless JWT is properly blacklisted alongside the database record removal.

Exploitation Methodology

Exploitation of this vulnerability strictly requires the attacker to first obtain a valid "never expire" access token. These tokens are commonly deployed in CI/CD pipelines, hardcoded into configuration files, or embedded in external integration scripts. If an attacker compromises a developer's local environment or uncovers a hardcoded secret in a source repository, they gain the prerequisite cryptographic material.

Once the token is acquired, the attacker authenticates to the Ech0 API using standard Bearer authorization headers. The platform validates the JWT cryptographic signature successfully, ignoring the missing expiration claim. The attacker can then interact with the system using the full operational privileges defined by the compromised token's scopes.

If the administrator detects the anomalous activity, they typically attempt to terminate the session via the Ech0 administration panel. The user interface successfully reflects that the token is deleted. However, because the system fundamentally fails to blacklist the JTI, the attacker's subsequent API requests continue to authenticate without interruption.

A Python-based proof of concept explicitly confirms this revocation failure. The script generates a "never expire" token, issues an API request to verify functional access, and triggers the administrative deletion endpoint. A final API request using the deleted token returns an HTTP 200 OK response, demonstrating the complete failure of the revocation mechanism.

Impact Assessment

The primary impact of this vulnerability is the persistent, unmitigated authorization of compromised credentials. Stateless authentication mechanisms require robust JTI blacklisting to handle premature token invalidation effectively. By bypassing this specific control, the vulnerability grants attackers permanent access to the affected Ech0 instance.

The CVSS v3.1 base score for this vulnerability is calculated at 7.4 (High). The vector CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N correctly reflects high confidentiality and integrity impacts. The attack complexity is marked as high because the vulnerability itself does not grant initial access; it strictly prevents the termination of an existing, stolen session.

The nil-pointer dereference introduces a secondary, highly localized availability impact. Authenticated users attempting to interact with specific endpoints that trigger the TTL calculation experience localized HTTP 500 errors. However, this panic does not crash the broader Go application service, limiting the overall denial-of-service capability against the platform.

The fix implemented in the patch completely mitigates both the nil-pointer dereference and the logic bypass. By establishing a hardcoded 100-year limit and bridging the gap between database deletion and JWT revocation, the application successfully restores administrative control over session lifecycles.

Remediation and Mitigation

The vendor addressed the vulnerability in commit eab62379c795c3f4850a9ca938ae3f27d7171541. Administrators must upgrade their Ech0 instances to version 1.4.8 or later to apply the patched token generation and revocation logic. This specific update automatically handles legacy tokens gracefully via the newly introduced remainingTTLFromClaims helper function.

If an immediate update is functionally unfeasible, administrators can execute a global secret rotation to invalidate all existing tokens. By modifying the JWT_SECRET variable in the core application configuration, the server will reject all current cryptographic signatures. This action requires all legitimate users and automated systems to re-authenticate and provision entirely new access tokens.

Security teams should actively monitor application logs for unexpected HTTP 500 errors originating specifically from the /api/auth/logout endpoint. These errors serve as a high-fidelity indicator that a system process or user is attempting to invalidate a legacy token. Database administrators can simultaneously audit the access_token_settings table for active tokens configured with an empty or never expiry value.

Developers implementing JWT authentication workflows should ensure that the exp claim is consistently populated, even for heavily extended sessions. Establishing a maximum realistic bound, such as the 100-year fallback utilized in the Ech0 patch, maintains structural consistency while fulfilling the functional requirement of non-expiring credentials.

Official Patches

lin-snowOfficial fix commit in the Ech0 repository
GitHubRepository Security Advisory

Fix Analysis (1)

Technical Appendix

CVSS Score
7.4/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N

Affected Systems

Ech0 (all versions prior to commit eab62379)

Affected Versions Detail

Product
Affected Versions
Fixed Version
Ech0
lin-snow
< 1.4.81.4.8
AttributeDetail
CWE IDCWE-613, CWE-476, CWE-300
Attack VectorNetwork
CVSS Score7.4
ImpactPersistent Authorization Bypass
Exploit StatusProof of Concept
ComponentJWT Handler & Authentication Repository

MITRE ATT&CK Mapping

T1528Steal Application Access Token
Credential Access
T1078Valid Accounts
Defense Evasion
CWE-613
Insufficient Session Expiration

The application does not properly enforce expiration or revocation of session tokens, allowing an attacker to reuse a captured token indefinitely.

Vulnerability Timeline

Fix commit pushed to the repository
2026-05-03
GHSA-FPW6-HRG5-Q5X5 published in the GitHub Advisory Database
2026-05-07

References & Sources

  • [1]GitHub Advisory Database
  • [2]OSV Data Record

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.