GHSA-M469-88XX-8RX2

Vulnerability from github – Published: 2026-10-09 20:52 – Updated: 2026-10-09 20:52
VLAI
Summary
Vikunja: CalDAV and feeds BasicAuth endpoints have no rate limit, bypassing the anti-brute-force floor on account passwords
Details

Summary

The /dav, /.well-known, and /feeds groups are registered on the root Echo instance with only BasicAuth and no rate limiter. CalDAV BasicAuth accepts the plain account password, so password guessing over /dav is unbounded and never returns 429, while /api/v1/login is throttled from the tenth attempt. The only anti-brute-force control on the instance is therefore bypassable.

Details

pkg/routes/routes.go (~lines 238-249) registers /.well-known, /dav, and /feeds with middleware.BasicAuth(...) and nothing else; registerCalDavRoutes adds no limiter. pkg/routes/caldav/auth.go (~lines 88-93) falls through to user.CheckUserCredentials with the plain account password when no CalDAV token matches. In contrast, /register, /login, etc. are wrapped by unauthRateLimit() — an unconditional 10/min/IP pre-auth floor that ignores ratelimit.enabled (default false).

TOTP-enabled accounts and bot users are correctly refused, so this targets password-only accounts.

PoC (verified at runtime against v2.5.0)

POST /api/v1/login  x25 wrong passwords  -> 429 from attempt 2 (throttled)
PROPFIND /dav/principals/{user}/  x60 wrong passwords  -> 401 x60, 429 x0
GET /feeds/notifications.atom  x30 wrong passwords  -> 401 x30, 429 x0
PROPFIND /dav/... with correct password -> 207 (proves the 401s are real auth failures)

Impact

The anti-brute-force floor guarding /login is entirely absent on /dav, /feeds, and /.well-known, giving an unbounded credential-guessing surface against account passwords. (bcrypt caps throughput to a few guesses/second, but nothing caps the number of attempts.) Reported as an authentication-control bypass, not a DoS.

Fix

Apply the unconditional pre-auth rate-limit floor to the /dav, /.well-known, and /feeds groups.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.5.0"
      },
      "package": {
        "ecosystem": "Go",
        "name": "code.vikunja.io/api"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.6.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-91973"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-307"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-09T20:52:04Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\nThe `/dav`, `/.well-known`, and `/feeds` groups are registered on the root Echo instance with only BasicAuth and no rate limiter. CalDAV BasicAuth accepts the plain account password, so password guessing over `/dav` is unbounded and never returns 429, while `/api/v1/login` is throttled from the tenth attempt. The only anti-brute-force control on the instance is therefore bypassable.\n\n### Details\n`pkg/routes/routes.go` (~lines 238-249) registers `/.well-known`, `/dav`, and `/feeds` with `middleware.BasicAuth(...)` and nothing else; `registerCalDavRoutes` adds no limiter. `pkg/routes/caldav/auth.go` (~lines 88-93) falls through to `user.CheckUserCredentials` with the plain account password when no CalDAV token matches. In contrast, `/register`, `/login`, etc. are wrapped by `unauthRateLimit()` \u2014 an unconditional 10/min/IP pre-auth floor that ignores `ratelimit.enabled` (default false).\n\nTOTP-enabled accounts and bot users are correctly refused, so this targets password-only accounts.\n\n### PoC (verified at runtime against v2.5.0)\n```\nPOST /api/v1/login  x25 wrong passwords  -\u003e 429 from attempt 2 (throttled)\nPROPFIND /dav/principals/{user}/  x60 wrong passwords  -\u003e 401 x60, 429 x0\nGET /feeds/notifications.atom  x30 wrong passwords  -\u003e 401 x30, 429 x0\nPROPFIND /dav/... with correct password -\u003e 207 (proves the 401s are real auth failures)\n```\n\n### Impact\nThe anti-brute-force floor guarding `/login` is entirely absent on `/dav`, `/feeds`, and `/.well-known`, giving an unbounded credential-guessing surface against account passwords. (bcrypt caps throughput to a few guesses/second, but nothing caps the number of attempts.) Reported as an authentication-control bypass, not a DoS.\n\n### Fix\nApply the unconditional pre-auth rate-limit floor to the `/dav`, `/.well-known`, and `/feeds` groups.",
  "id": "GHSA-m469-88xx-8rx2",
  "modified": "2026-10-09T20:52:04Z",
  "published": "2026-10-09T20:52:04Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/go-vikunja/vikunja/security/advisories/GHSA-m469-88xx-8rx2"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-91973"
    },
    {
      "type": "WEB",
      "url": "https://github.com/go-vikunja/vikunja/pull/3688"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/go-vikunja/vikunja"
    },
    {
      "type": "WEB",
      "url": "https://github.com/go-vikunja/vikunja/releases/tag/v2.6.0"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/vikunja-before-2.6.0-authentication-bypass-via-caldav-basicauth"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [],
  "summary": "Vikunja: CalDAV and feeds BasicAuth endpoints have no rate limit, bypassing the anti-brute-force floor on account passwords"
}



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…

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…