Vikunja Vulnerable to Account Takeover via Password Reset Token Reuse
Description
Vikunja is an open-source self-hosted task management platform. Versions prior to 2.1.0 have a business logic vulnerability exists in the password reset mechanism of vikunja/api that allows password reset tokens to be reused indefinitely. Due to a failure to invalidate tokens upon use and a critical logic bug in the token cleanup cron job, reset tokens remain valid forever. This allows an attacker who intercepts a single reset token (via logs, browser history, or phishing) to perform a complete, persistent account takeover at any point in the future, bypassing standard authentication controls. Version 2.1.0 contains a patch for the issue.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A password reset token reuse vulnerability in Vikunja prior to 2.1.0 allows persistent account takeover by reusing a single intercepted token.
Vikunja versions prior to 2.1.0 contain a business logic vulnerability in the password reset mechanism that allows password reset tokens to be reused indefinitely. The root cause involves two distinct logic errors. First, the ResetPassword function fails to invalidate the password reset token after use, instead deleting a different token type (TokenEmailConfirm) [2]. Second, the background cron job responsible for token cleanup contains an inverted comparison, deleting tokens newer than 24 hours instead of those older, so expiration is never enforced [2]. The combination means once a token is issued, it remains valid in the database forever.
Exploitation requires an attacker to obtain a single valid password reset token. This can occur through intercepting logs, browser history, or phishing [1][2]. No additional authentication is needed once the token is acquired. The attacker can use the token at any future time to submit a password reset request and change the victim's password. The vulnerability can be triggered remotely over the network, as the reset endpoint is accessible without prior authentication.
An attacker who successfully reuses a reset token gains complete and persistent control over the victim's account. This includes the ability to log in, view and modify all tasks and project data, and change further account settings. Since the account takeover persists until the legitimate owner manually reclaims it, it bypasses standard authentication controls and could be used for data exfiltration or privilege escalation within the application.
The issue has existed since version 0.18.0 (September 2021) and is fixed in Vikunja 2.1.0 [3]. Users should upgrade as soon as possible, especially those relying on Vikunja's own authentication rather than OAuth or LDAP. No workaround other than upgrading is available, and the advisory strongly encourages updating [3].
AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
code.vikunja.io/apiGo | <= 0.24.6 | — |
Affected products
2- go-vikunja/vikunjav5Range: < 2.1.0
Patches
15c2195f9fca9fix(auth): remove password reset token after use
2 files changed · +23 −1
pkg/user/user_password_reset.go+1 −1 modified@@ -65,7 +65,7 @@ func ResetPassword(s *xorm.Session, reset *PasswordReset) (userID int64, err err return } - err = removeTokens(s, user, TokenEmailConfirm) + err = removeTokens(s, user, TokenPasswordReset) if err != nil { return }
pkg/user/user_test.go+22 −0 modified@@ -558,6 +558,28 @@ func TestUserPasswordReset(t *testing.T) { _, err := ResetPassword(s, reset) require.NoError(t, err) }) + t.Run("removes password reset token after use", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + s := db.NewSession() + defer s.Close() + + token := "passwordresettesttoken" + + reset := &PasswordReset{ + Token: token, + NewPassword: "12345", + } + _, err := ResetPassword(s, reset) + require.NoError(t, err) + + err = s.Commit() + require.NoError(t, err) + + db.AssertMissing(t, "user_tokens", map[string]interface{}{ + "token": token, + "kind": TokenPasswordReset, + }) + }) t.Run("without password", func(t *testing.T) { db.LoadAndAssertFixtures(t) s := db.NewSession()
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-rfjg-6m84-crj2ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-28268ghsaADVISORY
- github.com/go-vikunja/vikunja/commit/5c2195f9fca9ad208477e865e6009c37889f87b2ghsax_refsource_MISCWEB
- github.com/go-vikunja/vikunja/security/advisories/GHSA-rfjg-6m84-crj2ghsax_refsource_CONFIRMWEB
- vikunja.io/changelog/vikunja-v2.1.0-was-releasedghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.