Skip to content

Bypassing the User Presence Check in FIDO2 Authentication

TL;DR:

  • FIDO2 user presence checks can be bypassed on some services.
  • Nextcloud and other projects using webauthn-framework <3.2.9 or >3.2.12 and < 3.3.4 are affected.
  • User presence checks restrict stealthy abuse of authenticators.

We found that the popular PHP library webauthn-framework does not verify user presence and thus an attached FIDO2 authenticator can be stealthily used by an attacker controlling the user’s system to login to affected services such as Nextcloud.

FIDO2

FIDO2 is a standard for secure web authentication using public-key cryptography. The authentication scheme prevents attacks targeting weak or reused passwords, as well as phishing. Cryptographic keys are usually stored on hardware authenticators. This allows the user to attach an authenticator to multiple systems and to restrict access to the stored keys.

FIDO2 provides two mechanisms to restrict access to an authenticator, namely user presence (UP) and user verification (UV). User verification prevents an attacker from using a stolen token. The most common user verification method is a PIN, however biometric recognition is also supported by some authenticators. In addition, to test that a user is really present during authentication, many authenticators feature a hardware button that has to be touched.
Intuitively, one might think that UP and UV are verified by the authenticator only. However, this is not the case.

Inconsistent Standards

FIDO2 is not a single standard, but made up of two standards WebAuthn and CTAP2. CTAP2 defines the protocol that is used by a browser to talk to an authenticator, while WebAuthn defines the Javascript API that is used by a service to initiate registration and authentication. According to the WebAuthn standard, a test of user presence is always required. However, in CTAP2 an option can be set to disable the UP check.

This discrepancy allows an attacker with access to the user’s system to request authentication stealthily in the background without the need of physical access to the authenticator. However, this is not an issue on its own. Even though the UP check can be disabled, the authenticator indicates if UP has been checked. This is represented by a bit in the signed data returned by the authenticator. Therefore, it is the service’s responsibility to verify that a UP check happened.

Missing Check In Webauthn-framework

While taking a look at a widely used PHP library for WebAuthn, we noticed that the UP check was missing. Nextcloud is probably the most prominent application including this library by default. Furthermore, many extensions for popular frameworks such as Typo3, Drupal and Laravel rely on it.

Impact

We think that the impact of this vulnerability is very limited. WebAuthn eliminates many risks of traditional password-based authentication and prevents phishing attacks. Furthermore, an attacker with access to the user’s system also has other means to hijack an account. For example cookies can be extracted from the browser to hijack an active session.

Nonetheless, we think that the UP checks improve protection of registered accounts. This is especially true for accounts that are seldomly used. In this case, there might be no active session that can be hijacked by the attacker and establishing a new session at least requires user interaction. However, UP checks are not powerful enough to prevent malicious interaction with an attached authenticator completely.

Bypassing User Presence

Bypassing the user presence check is as simpel as setting the option up to false in the CTAP2 operation authenticatorGetAssertion. The following code requests an assertion without UP check using the python-fido2 library.

opts = {“up”: False}
assertions = self.ctap2.get_assertions(rp_id, hash, allow_creds, ext, opts)

A Known Issue

Interestingly, we found a closed issue on GitHub that reported the missing check before. As a consequence, the check was added in version 3.2.9 but removed again in version 3.3.0.

By contacting the developer, we found out that the check was removed because the official FIDO conformance test tools reject the changed behavior.
These tools verify conformance of a implementation to the FIDO standard.
However, in this case they seemingly rejected conforming behavior. As noted earlier, according to WebAuthn verifying UP is required. An issue in the repository of the conformance tools suggests that the reason for this is silent authentication. Silent authentication means that neither UP nor UV is checked.

While silent authentication is a valid use case according to the CTAP2 standard, the behavior is not supported by WebAuthn.
Thus, the conformance test tools should be adapted to accept server implementations that verify UP.
Even though the impact of bypassing UP is limited, we think that more work should be focused on auditing FIDO2 server implementations.