GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration

GHSA-42CR-W2GR-M54Q

Vulnerability from github – Published: 2026-02-26 22:15 – Updated: 2026-04-15 20:41
VLAI
Summary
wger: IDOR via user-unscoped cache keys on routine API actions exposes workout data
Details

Summary

Five routine detail action endpoints check a cache before calling self.get_object(). Cache keys are scoped only by pk — no user ID is included. When a victim has previously accessed their routine via the API, an attacker can retrieve the cached response for the same PK without any ownership check.

Details

wger/manager/api/views.py — five actions follow this pattern (lines 134–201):

@action(detail=True)
def date_sequence_display_mode(self, request, pk=None):
    cache_key = make_routine_api_date_sequence_display_cache_key(pk)
    cached = cache.get(cache_key)
    if cached:
        return Response(cached)   # returned WITHOUT calling self.get_object()
    # only reaches ownership check on cache miss
    routine = self.get_object()
    ...

Cache key construction in wger/utils/cache.py:89–106:

def make_routine_api_date_sequence_display_cache_key(routine_id):
    return f"routine-api-date-sequence-display-{routine_id}"
    # No user ID in key

Cache TTL: 1 month (4 * 604800 seconds, settings_global.py:461).

Affected endpoints:

GET /api/v2/routine/{pk}/date-sequence-display/
GET /api/v2/routine/{pk}/date-sequence-gym/
GET /api/v2/routine/{pk}/structure/
GET /api/v2/routine/{pk}/logs/
GET /api/v2/routine/{pk}/stats/

PoC

1. Victim (user A) visits GET /api/v2/routine/5/structure/ → response cached under key "routine-api-structure-5"
2. Attacker (user B) visits GET /api/v2/routine/5/structure/ → cache hit → returns user A's routine structure without any ownership check

Requires the victim to have previously accessed the endpoint (cache must be populated). Once populated, the cache entry is valid for 1 month.

Impact

An attacker with a registered account can retrieve another user's routine details — workout day sequences, exercise structure, training logs, and statistics — from cache without ownership verification.

Fix: Include the user ID in the cache key:

def make_routine_api_date_sequence_display_cache_key(routine_id, user_id):
    return f"routine-api-date-sequence-display-{user_id}-{routine_id}"

Or move self.get_object() before the cache lookup so ownership is always verified first.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "wger"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "2.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-27838"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-639"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-02-26T22:15:30Z",
    "nvd_published_at": "2026-02-26T23:16:34Z",
    "severity": "LOW"
  },
  "details": "### Summary\n\nFive routine detail action endpoints check a cache before calling `self.get_object()`. Cache keys are scoped only by `pk` \u2014 no user ID is included. When a victim has previously accessed their routine via the API, an attacker can retrieve the cached response for the same PK without any ownership check.\n\n### Details\n\n`wger/manager/api/views.py` \u2014 five actions follow this pattern (lines 134\u2013201):\n\n```python\n@action(detail=True)\ndef date_sequence_display_mode(self, request, pk=None):\n    cache_key = make_routine_api_date_sequence_display_cache_key(pk)\n    cached = cache.get(cache_key)\n    if cached:\n        return Response(cached)   # returned WITHOUT calling self.get_object()\n    # only reaches ownership check on cache miss\n    routine = self.get_object()\n    ...\n```\n\nCache key construction in `wger/utils/cache.py:89\u2013106`:\n\n```python\ndef make_routine_api_date_sequence_display_cache_key(routine_id):\n    return f\"routine-api-date-sequence-display-{routine_id}\"\n    # No user ID in key\n```\n\nCache TTL: 1 month (`4 * 604800` seconds, `settings_global.py:461`).\n\nAffected endpoints:\n```\nGET /api/v2/routine/{pk}/date-sequence-display/\nGET /api/v2/routine/{pk}/date-sequence-gym/\nGET /api/v2/routine/{pk}/structure/\nGET /api/v2/routine/{pk}/logs/\nGET /api/v2/routine/{pk}/stats/\n```\n\n### PoC\n\n```\n1. Victim (user A) visits GET /api/v2/routine/5/structure/ \u2192 response cached under key \"routine-api-structure-5\"\n2. Attacker (user B) visits GET /api/v2/routine/5/structure/ \u2192 cache hit \u2192 returns user A\u0027s routine structure without any ownership check\n```\n\nRequires the victim to have previously accessed the endpoint (cache must be populated). Once populated, the cache entry is valid for 1 month.\n\n### Impact\n\nAn attacker with a registered account can retrieve another user\u0027s routine details \u2014 workout day sequences, exercise structure, training logs, and statistics \u2014 from cache without ownership verification.\n\n**Fix**: Include the user ID in the cache key:\n```python\ndef make_routine_api_date_sequence_display_cache_key(routine_id, user_id):\n    return f\"routine-api-date-sequence-display-{user_id}-{routine_id}\"\n```\n\nOr move `self.get_object()` before the cache lookup so ownership is always verified first.",
  "id": "GHSA-42cr-w2gr-m54q",
  "modified": "2026-04-15T20:41:17Z",
  "published": "2026-02-26T22:15:30Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/wger-project/wger/security/advisories/GHSA-42cr-w2gr-m54q"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-27838"
    },
    {
      "type": "WEB",
      "url": "https://github.com/wger-project/wger/commit/e964328784e2ee2830a1991d69fadbce86ac9fbf"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/wger-project/wger"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "wger: IDOR via user-unscoped cache keys on routine API actions exposes workout data"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…