Vikunja Permissive CORS Policy Leading to Account Compromise
Vikunja versions 2.2.0 through 2.6.0 contain a CORS misconfiguration that implicitly trusts all localhost ports, allowing local attackers to retrieve valid bearer tokens via credentialed cross-origin requests.
What's new
Vikunja versions 2.2.0 through 2.6.0 are affected by a permissive Cross-Origin Resource Sharing (CORS) policy vulnerability. The application defaults to allowing http://127.0.0.1:* and http://localhost:* as trusted origins. Crucially, when an administrator configures the required service.publicurl for production, this value is appended to - rather than replacing - the existing localhost defaults. Because the application enables Access-Control-Allow-Credentials: true and marks refresh cookies as SameSite=None, any web page running on the victim's local machine (on any port) can send a credentialed request to the Vikunja API. An attacker can exploit this by forcing a victim's browser to perform a cross-origin POST request to the /api/v1/user/token/refresh endpoint, which returns a valid bearer JWT in the response body. The resulting token grants the attacker full administrative or user authority over the victim's account.
Attack Chain
- The victim logs into their production Vikunja instance, resulting in a persistent
vikunja_refresh_tokencookie being set withSameSite=NoneandSecureattributes. - The victim visits a malicious or compromised website running on any local port (e.g.,
http://localhost:31337). - The malicious site initiates a
fetch()request to the victim's authenticated Vikunja instance, specifically targetingPOST /api/v1/user/token/refresh. - The victim's browser, observing the
Access-Control-Allow-Credentials: trueheader returned by the Vikunja server, attaches thevikunja_refresh_tokencookie to the cross-origin request. - The Vikunja server validates the cookie, processes the request as a legitimate refresh attempt, and returns a JSON response containing a new valid bearer JWT.
- The malicious site receives the response, including the bearer token, due to the permissive
Access-Control-Allow-Originheader matching the localhost origin. - The attacker uses the intercepted bearer token to authenticate against the Vikunja API as the victim, achieving full account takeover.
Impact
Successful exploitation results in total account compromise, granting an attacker full access to the victim's data, tasks, and administrative functions within the Vikunja instance. The impact is significant for organizations relying on Vikunja for sensitive task management, particularly where administrative or privileged user accounts are targeted via local browser-based vectors.
Recommendation
Detection and mitigation should focus on identifying unauthorized access patterns and hardening the CORS configuration.
- Upgrade Vikunja to a version where
service.publicurlcorrectly overrides default localhost origins. - Audit web server logs for requests to
/api/v1/user/token/refreshoriginating from unexpectedOriginheaders, specifically those targeting loopback IP addresses or local ports. - Implement strict CORS policies on the reverse proxy or web server layer in front of Vikunja to explicitly whitelist only authorized production domains and strip unauthorized localhost origins from the
Access-Control-Allow-Originheader.