Oct 9, 2026·4 min read·6 visits
Administrative changes via the pyLoad REST API (such as password changes or permission downgrades) do not invalidate active filesystem-based Flask sessions. Affected users retain their original high-privilege permissions for the entire session lifetime (up to 31 days) unless sessions are manually purged.
An insufficient session invalidation vulnerability exists in pyLoad (pyload-ng) versions 0.5.0b3.dev98 through 0.5.0b3.dev101. When administrative actions like privilege revocation or password changes are executed via the public REST API, active user sessions on disk are not updated or invalidated. Consequently, affected sessions remain fully authenticated with stale permissions for up to 31 days.
pyLoad utilizes a Flask-based web interface to expose configuration, download management, and user controls. To reduce database overhead during client requests, pyLoad caches security credentials—such as the user's role identifier and permission bitmask—directly inside the filesystem-backed Flask session when a user first authenticates.
This design introduces a critical trust-boundary problem. The cached session data acts as the absolute source of truth for subsequent authorization decisions, bypassing real-time database validation. The primary security vulnerability, tracked under GHSA-JQ7H-WRVP-3RGX, stems from a complete lack of synchronization between administrative updates performed via the backend REST API/RPC layer and these cached session structures.
The root cause of this flaw is insufficient session invalidation (CWE-613). When administrators interact with pyLoad's Web UI administration panel, the application explicitly coordinates state changes, calling helper functions like clear_all_user_sessions(username) to force deletion of associated filesystem cookies.
However, pyLoad also exposes a direct REST API and RPC dispatcher (/api/) handled by the module src/pyload/core/api/__init__.py. When administrative tasks are executed via these RPC endpoints—including user deletion, privilege modification, or password alterations—the system directly updates the backend SQLite database but has no logical awareness of the active Flask session manager layer.
Consequently, database records reflect the updated privileges, but the corresponding .sess files stored inside SESSION_FILE_DIR remain active, unexpired, and intact. Because parse_permissions() continues reading authorization bits directly from the stale cookie session, affected clients preserve high-privilege access for the duration of the session's active lifespan, which defaults to 31 days.
The sequence below illustrates the communication breakdown between the Core API layer, SQLite database, and Web UI Session Files, culminating in unauthorized persistent access:
This architectural separation prevents the database-level modifications from propagating to active transport-layer sessions, creating a vulnerability window of up to 31 days.
Before the patch, core user configuration methods had no references to Flask-layer sessions. To resolve this, the patch introduces a callback registration mechanism allowing the core API to request session invalidation safely.
# File: src/pyload/core/api/__init__.py (Post-patch)
def __init__(self, core):
# ...
self._session_invalidator = None
def set_session_invalidator(self, invalidator: Optional[Callable[[str], int]]) -> None:
self._session_invalidator = invalidator
def _invalidate_user_sessions(self, user: str) -> None:
if self._session_invalidator is not None:
# Invokes the lambda registered by the Web UI layer
self._session_invalidator(user)The Web UI initializes this callback during web application configuration:
# File: src/pyload/webui/app/__init__.py
api = app.config["PYLOAD_API"]
api.set_session_invalidator(lambda user: clear_all_user_sessions(user))Any critical user updates within the Core API are then wrapped to trigger the callback upon successful database execution:
# File: src/pyload/core/api/__init__.py
@legacy("setUserPermission")
@post
def set_user_permission(self, user: str, permission: int, role: int) -> None:
# Database change operation returned boolean status
changed = self.pyload.db.set_user_permission(user, permission, role)
if changed:
self._invalidate_user_sessions(user)An attacker must establish or compromise a valid cookie-authenticated session before privilege changes or password changes are made.
Once an administrative action is taken via the RPC API—for example, resetting a compromised user's password or reducing their user level to 1—the attacker can test persistence by making direct API calls targeting administrative features, such as get_userdir or get_config.
Because the underlying Flask framework authorizes the request using the existing filesystem session token, the client session is deemed authentic. No request middleware cross-references the session's cached values with the current DB table, maintaining administrative privileges for the lifetime of the session.
While the registered callback mechanism successfully fixes the structural bypass, security engineers should monitor two specific implementation aspects.
First, inside clear_all_user_sessions in src/pyload/webui/app/helpers.py, the removal of the general try-except structure during directory parsing introduces a minor robustness issue. If a concurrent request removes a session file, or if a zero-byte session file exists on disk, _read_session_file or os.remove will raise an unhandled exception. This will abort execution mid-loop, resulting in a server error (HTTP 500) and leaving other target user sessions still active.
Second, the synchronization is wholly dependent on the runtime environment registering the Flask configuration callback. If the application is launched in an embedded context without executing _configure_session(), the callback pointer remains None, leaving the API-level methods without any functional session invalidation capabilities.
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
pyload-ng pyload | >= 0.5.0b3.dev98, <= 0.5.0b3.dev101 | Commit 3e726ba271a6b1015e3a0ea6dc7e46a8f71a62ad |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-613 |
| Attack Vector | Network |
| CVSS Score | 7.5 |
| Exploit Status | Proof of Concept available |
| Impact | Privilege Escalation / Persistent Session Hijack |
The application does not invalidate a session when the user's password or privilege level is modified via the API dispatcher.
A critical security vulnerability has been identified and patched in the WebUI component of pyLoad, a popular Python-based open-source download manager. This vulnerability allows attackers to completely bypass API rate limits and spoof client IP addresses in audit logs due to unsafe parsing of the X-Forwarded-For HTTP header.
An authorization bypass and credential oracle vulnerability in pyload-ng allows authenticated users with minimal or no privileges to brute-force the administrator password. This is achieved through sensitive API methods exposed globally combined with a non-constant-time password hash comparison algorithm.
An authentication bypass vulnerability in pyLoad allows unauthenticated remote attackers to gain administrative API access. The vulnerability is caused by a logical flaw in the API key cache validation lookup, where authentication states are cached using only the public key identifier, skipping cryptographic token verification on cache hits.
An authorization bypass vulnerability in Strawberry GraphQL between versions 0.217.0 and 0.326.1 occurs when a synchronous permission handler returns an awaitable object (such as an unawaited coroutine). Due to Python's truthiness rules, the unawaited coroutine is evaluated as True, leading to an immediate bypass of security policies.
An uncontrolled resource consumption vulnerability in Strawberry GraphQL allows unauthenticated remote attackers to trigger a Denial of Service on persistent WebSocket connections using the legacy graphql-ws protocol. When the server enforces max_subscriptions_per_connection, naturally terminating subscriptions are not cleared from memory registries, leading to exhaustion of connection slots.
A high-severity type-confusion vulnerability exists in NearForm fast-jwt prior to version 6.3.0. The vulnerability allows attackers to bypass crucial claim validation steps (such as expiration, issuer, audience, and subject validations) by presenting a validly signed JSON Web Token structured as a JSON array instead of a JSON object. This occurs because the library's decoder fails to reject JSON arrays during type evaluation.