Vikunja is an open-source self-hosted task management platform. Starting in version 0.8 and prior to version 2.2.0, unauthenticated users…
GitHub_M·CWE-807·Published 2026-03-20
Vikunja is an open-source self-hosted task management platform. Starting in version 0.8 and prior to version 2.2.0, unauthenticated users are able to bypass the application's built-in rate-limits by spoofing the `X-Forwarded-For` or `X-Real-IP` headers due to the rate-limit relying on the value of `(echo.Context).RealIP`. Unauthenticated users can abuse endpoints available to them for different potential impacts. The immediate concern would be brute-forcing usernames or specific accounts' passwords. This bypass allows unlimited requests against unauthenticated endpoints. Version 2.2.0 patches the issue.
Vikunja is an open-source self-hosted task management platform. Starting in version 0.8 and prior to version 2.2.0, unauthenticated users are able to bypass the application's built-in rate-limits by spoofing the `X-Forwarded-For` or `X-Real-IP` headers due to the rate-limit relying on the value of `(echo.Context).RealIP`. Unauthenticated users can abuse endpoints available to them for different potential impacts. The immediate concern would be brute-forcing usernames or specific accounts' passwords. This bypass allows unlimited requests against unauthenticated endpoints. Version 2.2.0 patches the issue.
Vikunja has a Rate-Limit Bypass for Unauthenticated Users via Spoofed Headers in code.vikunja.io/api. NOTE: The source advisory for this report contains additional versions that could not be automatically mapped to standard Go module versions. (If this is causing false-positive reports from vulnerability scanners, please suggest an edit to the report.) The additional affected modules and versions are: code.vikunja.io/api from v0.8.0 before v2.2.0.
### Summary Unauthenticated users are able to bypass the application's built-in rate-limits by spoofing the `X-Forwarded-For` or `X-Real-IP` headers due to the rate-limit relying on the value of `(echo.Context).RealIP`. ### Details In the first file below, the rate-limit for unauthenticated users can be observed being populated with the `ip` value. In the second file, it shows it using the `c.RealIP()` function for the `ip` case. Due to this, the rate-limit will rely on the value of one of the two mentioned headers (`X-Forwarded-For` or `X-Real-IP`). These can be spoofed by users client-side in order to completely bypass any unauthenticated rate-limits in place. Some reverse proxies like Traefik will overwrite this value by default, but others will not, leaving any deployment that either isn't using a reserve proxy that specifically overwrites the header's value or isn't using a reverse proxy vulnerable. **File 1:** pkg\routes\routes.go:318 ```go // This is the group with no auth // It is its own group to be able to rate limit this based on different heuristics n := a.Group("") setupRateLimit(n, "ip") // Docs n.GET("/docs.json", apiv1.DocsJSON) n.GET("/docs", apiv1.RedocUI) // Prometheus endpoint setupMetrics(n) // Separate route for unauthenticated routes to enable rate limits for it ur := a.Group("") rate := limiter.Rate{ Period: 60 * time.Second, Limit: config.RateLimitNoAuthRoutesLimit.GetInt64(), } rateLimiter := createRateLimiter(rate) ur.Use(RateLimit(rateLimiter, "ip")) ``` **File 2:** pkg\routes\rate_limit.go:41 ```go // RateLimit is the rate limit middleware func RateLimit(rateLimiter *limiter.Limiter, rateLimitKind string) echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c *echo.Context) (err error) { var rateLimitKey string switch rateLimitKind { case "ip": rateLimitKey = c.RealIP() case "user": auth, err := auth2.GetAuthFromClaims(c) if err != nil { log.Errorf("Error getting auth from jwt claims: %v", err) } rateLimitKey = "user_" + strconv.FormatInt(auth.GetID(), 10) default: log.Errorf("Unknown rate limit kind configured: %s", rateLimitKind) } ``` ### PoC 1. Download and run the default docker compose file via the instructions here: [https://vikunja.io/install/](https://vikunja.io/install/). Do not configure a proxy. 2. Once running, navigate to the application in a web browser that is using a web proxy, such as Burp Suite. 3. Attempt to authenticate to the application with an invalid username and password. 4. In the web proxy's logs, locate the request to the `/api/v1/login` endpoint. Observe that the response contains rate-limit details: ```http HTTP/1.1 403 Forbidden Cache-Control: no-store Content-Type: application/json Vary: Origin X-Ratelimit-Limit: 10 X-Ratelimit-Remaining: 9 X-Ratelimit-Reset: 1772224455 Date: Fri, 27 Feb 2026 20:33:16 GMT Content-Length: 54 {"code":1011,"message":"Wrong username or password."} ``` 5. Add the `X-Forwarded-For` header with an arbitrary value, like so: `X-Forwarded-For: FakeValue`. Send the request 10 times, or until the rate-limit is at zero. 6. Modify the `X-Forwarded-For` headers value to be different, like so: `X-Forwarded-For: NewValue`. 7. Observe that the `X-Ratelimit-Remaining` header's value has reset its countdown and is back at `9`. ### Impact Unauthenticated users can abuse endpoints available to them for different potential impacts. The immediate concern would be brute-forcing usernames or specific accounts' passwords. This bypass allows unlimited requests against unauthenticated endpoints.
Vikunja es una plataforma de gestión de tareas de código abierto y autoalojada. A partir de la versión 0.8 y antes de la versión 2.2.0, los usuarios no autenticados pueden eludir los límites de tasa incorporados de la aplicación suplantando los encabezados `X-Forwarded-For` o `X-Real-IP` debido a que el límite de tasa se basa en el valor de `(echo.Context).RealIP`. Los usuarios no autenticados pueden abusar de los puntos finales disponibles para ellos para diferentes impactos potenciales. La preocupación inmediata sería el ataque de fuerza bruta de nombres de usuario o contraseñas de cuentas específicas. Esta elusión permite solicitudes ilimitadas contra puntos finales no autenticados. La versión 2.2.0 soluciona el problema.
| Version | Type | Source | Base | Exp | Impact | Vector |
|---|---|---|---|---|---|---|
| 3.1 | Primary | cve.org | 5.3 | — | — | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L |
| 3.1 | Primary | cve.org | 5.3 | — | — | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L |
| 3.1 | Secondary | NVD | 5.3 | 3.9 | 1.4 | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L |
| 3.1 | Secondary | GHSA | 5.3 | — | — | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L |