highCVE-2026-52827Jul 14, 2026

CVE-2026-52827: Kimai 2FA TOTP Bypass via Pre-Verification Session Cookie

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

Kimai's REST API accepts the session cookie issued right after password entry, before the TOTP step is completed, letting an attacker with only a password skip two-factor authentication entirely.

Packagekimai/kimai
Ecosystemcomposer
Affected< 2.59.0
Fixed in2.59.0

The problem

Every /api/* endpoint in Kimai accepted the KIMAI_SESSION cookie produced at the end of the password-check step, before the user ever sees the TOTP prompt.

The Symfony security firewall guarded the API with IS_AUTHENTICATED, and the Scheb TwoFactorToken in the session satisfies that check because it is a real, non-anonymous token. ApiRequestMatcher also short-circuits the stateless firewall when a session is present, routing the request through the session-aware main firewall where the TOTP gate is never enforced.

The net result: any account with 2FA enabled can be fully accessed via the API with just the account password.

Proof of concept

A working proof-of-concept for CVE-2026-52827 in kimai/kimai, with the exact payload below.

bash
# Step 1: log in with password only, capture the session cookie
curl -i -c cookies.txt -X POST https://kimai.example.com/en/login \
  -d 'email=victim@example.com&password=s3cr3t&_csrf_token='
# The response sets KIMAI_SESSION even though the browser is held at /en/auth/2fa

# Step 2: replay the pre-2FA session cookie against any API endpoint
# TOTP code is never required
curl -s \
  -b cookies.txt \
  https://kimai.example.com/api/users/me

The root cause (CWE-287 Improper Authentication) is that IS_AUTHENTICATED in Symfony is satisfied by a TwoFactorToken, which the Scheb bundle places in the session immediately after password verification and before TOTP confirmation. The API firewall used this flag, so a mid-flow session cookie was treated as fully authenticated.

The patch tightens both layers. The /api/ firewall now requires IS_AUTHENTICATED_REMEMBERED, which Scheb only assigns after 2FA completes. ApiVoter additionally checks $token instanceof TwoFactorTokenInterface and isGranted('IS_AUTHENTICATED_2FA_IN_PROGRESS'), refusing the API grant to any token still in the 2FA flow.

Regression tests were added to lock in the fix.

The fix

Upgrade to kimai/kimai 2.59.0 or later. No configuration change is required; the fix is in the firewall definition (config/packages/security.yaml) and in App\Voter\ApiVoter.

Reported by shafiqaimanx.

References: [1][2]

Related research