Описание
Vaultwarden has 2FA Bypass on Protected Actions due to Faulty Rate Limit Enforcement
Summary
Vaultwarden v1.34.3 and prior are susceptible to a 2FA bypass when performing protected actions. An attacker who gains authenticated access to a user’s account can exploit this bypass to perform protected actions such as accessing the user's API key or deleting the user's vault and organisations the user is an admin/owner of.
Note that
Details
Within Vaultwarden, the PasswordOrOtpData struct is used to gate certain protected actions such as account deletion behind a 2FA validation. This validation requires the user to either re-enter their master password, or to enter a one-time passcode sent to their email address.
By default, the one-time passcode is comprised of six digits, and the expiry time for each token is ten minutes. The validation of this one-time passcode is performed by the following function:
Since the one-time passcode is only six-digits long, it has significantly less entropy than a typical password or secret key. Hence, Vaultwarden attempts to prevent brute-force attacks against this passcode by enforcing a rate limit of 6 attempts per code. However, the number of attempts made by the user is not persisted correctly.
In the validate_protected_action_top function, Vaultwarden first reads the OTP data from a JSON blob stored in pa.data. The resulting ProtectedActionData structure is then a deserialised copy of the underlying JSON value.
Next, Vaultwarden calls pa_data.add_attempt() in order to increment the number of attempts made by one. This increments the attempt count on the local structure, but does not modify the value of the pa.data.
Finally, if the OTP validation fails, Vaultwarden attempts to persist the updated attempt count by calling pa.save(conn). However since we only modified a copy of pa.data, the value of pa.data.attempts remains at zero.
The probability of a successful brute force depends on the OTP token length, the OTP expiry duration, and the request throughput. Since each request issued by the attacker does not depend on any previous requests, network latency is not a factor. The bottleneck then, will likely be either the attacker’s network bandwidth or Vaultwarden’s request processing throughput. From local testing, rates of up to 2500 requests per second were achievable, which successfuly bruteforced the OTP in 3 minutes.
If the attacker’s request throughput is low, they can also make repeated requests to /api/accounts/request-otp to generate new tokens. Their probability of success is then
where $R$ is the number of requests per second, $T$ is the token expiry time in seconds, $L$ is the number of digits in the OTP code, and $n$ is the number of OTP tokens requested.
Proof of Concept
The easiest method of demonstrating this vulnerability is by making an (authenticated) request to the /api/accounts/request-otp endpoint to generate an OTP, and then repeatedly sending invalid guesses to /api/accounts/verify-otp. After six guesses, Vaultwarden will still reply "Token is invalid" in response to an incorrect guess, rather than "Token has expired" as expected when the rate limit is exceeded. Upon entering the correct OTP, the code will still validate despite more than six guesses being made.
For a more practical example, the following Go script will brute force the OTP in order to read the user’s API key.
Impact
An attacker who gains access to a user’s account can exploit this bypass to perform protected actions such as accessing the user’s API key or deleting the user’s accounts and organisations.
Remediation
The simplest fix is to ensure the updated number of attempts is persisted by calling pa.data = pa_data.to_json() before calling pa.save(conn). However this still leaves open the possibility of an attacker requesting an OTP code, exhausting their six attempts and then requesting a new code to try. This attack succeeds with probability
which becomes non-neglible as $n$ increases.
Therefore the best approach might be to enforce a delay like this, to ensure that all rate limits are ultimately tied back to time:
Пакеты
vaultwarden
<= 1.34.3
1.35.0
Связанные уязвимости
Vaultwarden is an unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs. Vaultwarden versions 1.34.3 and prior are susceptible to a 2FA bypass when performing protected actions. An attacker who gains authenticated access to a user’s account can exploit this bypass to perform protected actions such as accessing the user’s API key or deleting the user’s vault and organisations the user is an admin/owner of . This issue has been patched in version 1.35.0.
Vaultwarden is an unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs. Vaultwarden versions 1.34.3 and prior are susceptible to a 2FA bypass when performing protected actions. An attacker who gains authenticated access to a user’s account can exploit this bypass to perform protected actions such as accessing the user’s API key or deleting the user’s vault and organisations the user is an admin/owner of . This issue has been patched in version 1.35.0.
Vaultwarden is an unofficial Bitwarden compatible server written in Ru ...