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

CVE-2026-86254: wger Authorization Bypass Vulnerability

CVE-2026-86254 is an authorization bypass flaw in wger that allows gym staff with gym=None to delete, deactivate, or activate user accounts. This post explains its technical details, affected versions, impact, and mitigation steps.

Published:

CVE-2026-86254 Overview

CVE-2026-86254 is an incomplete authorization bypass in the wger open-source fitness tracking application. The flaw resides in wger/core/views/user.py, where three user-management views retain a legacy gym-scope check using raw integer comparison instead of the is_same_gym() helper. Gym staff accounts with gym=None affiliation and the gym.manage_gym permission can delete, deactivate, or reactivate any other user with gym=None. The root cause is Python's None != None evaluating as False, causing the scope check to pass unexpectedly. The vulnerability is tracked under CWE-862: Missing Authorization.

Critical Impact

Authenticated attackers with gym-manager privileges can permanently delete, lock out, or reactivate any unaffiliated user account across the wger instance.

Affected Products

  • wger fitness manager (all versions through master branch prior to the fix)
  • Self-hosted wger deployments with multi-gym staff accounts
  • wger instances exposing the gym.manage_gym permission to non-scoped staff

Discovery Timeline

  • 2026-09-06 - CVE-2026-86254 published to NVD
  • 2026-09-08 - Last updated in NVD database

Technical Details for CVE-2026-86254

Vulnerability Analysis

The wger project scopes administrative actions per gym so that gym staff can only manage users affiliated with their own gym. A prior authorization fix introduced the is_same_gym() helper to standardize this check across user-management views. Three views in wger/core/views/user.py responsible for user deletion, deactivation, and activation were not migrated to the helper. These views continue to compare request.user.userprofile.gym_id against the target user's gym_id using a direct inequality operator.

When both values are None, the comparison None != None returns False, so the guard fails to reject the request. Any staff account with the gym.manage_gym permission and no gym affiliation is treated as being in the same scope as every other unaffiliated user. This grants the attacker the ability to permanently remove accounts, lock legitimate users out through deactivation, or reverse defensive deactivations performed by administrators.

Root Cause

The root cause is inconsistent authorization enforcement. Raw identifier comparison does not model the semantics of a null tenant scope, while is_same_gym() explicitly handles that case. The three unmigrated views therefore expose a scope-confusion condition classified under CWE-862.

Attack Vector

Exploitation requires an authenticated account with gym.manage_gym and gym=None. The attacker issues standard authenticated HTTP POST requests to the vulnerable user delete, deactivate, and activate endpoints, supplying the user ID of any other gym=None account. The server passes the flawed scope check and executes the requested state change. See the GitHub Security Advisory GHSA-mw8f-w6p8-xrf4 and the VulnCheck Advisory on the Incomplete Authorization Fix for full technical details.

Detection Methods for CVE-2026-86254

Indicators of Compromise

  • Unexpected DELETE or state-change events on user records where the acting staff account and target user both have gym_id set to NULL.
  • Django audit log entries showing user deactivation or reactivation performed by accounts without an assigned gym.
  • Sudden lockouts reported by users whose accounts share the unaffiliated (gym=None) scope.

Detection Strategies

  • Instrument the three affected views in wger/core/views/user.py to log the acting user, target user, and both gym_id values on every invocation.
  • Alert on any successful user-management action where either the actor or target has gym_id IS NULL.
  • Correlate authentication events with subsequent administrative actions to identify staff accounts abusing gym.manage_gym.

Monitoring Recommendations

  • Forward Django request logs and application audit trails to a centralized SIEM for retention and correlation.
  • Monitor privileged permission grants such as gym.manage_gym, especially when assigned to accounts without a gym affiliation.
  • Track user account state transitions (active, inactive, deleted) and baseline normal rates to surface anomalies.

How to Mitigate CVE-2026-86254

Immediate Actions Required

  • Audit all wger staff accounts and remove gym.manage_gym from any account that does not require it.
  • Assign an explicit gym value to every staff account so that no privileged user remains in the gym=None scope.
  • Review recent user deletion, deactivation, and activation events for unauthorized changes and restore affected accounts.

Patch Information

Apply the upstream fix from the wger project that migrates the three affected views in wger/core/views/user.py to use the is_same_gym() helper. Refer to the GitHub Security Advisory GHSA-mw8f-w6p8-xrf4 for the corrected commit and release guidance. Upgrade to the patched branch or backport the helper into the three vulnerable views in self-managed forks.

Workarounds

  • Restrict gym.manage_gym permission assignment through Django admin until the patch is applied.
  • Enforce a database constraint or application middleware that rejects user-management requests where actor gym_id is NULL.
  • Place the vulnerable endpoints behind an authenticated reverse-proxy rule that blocks requests from staff accounts without a gym affiliation.

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

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.