Every CVE Search/ Get API key

CVE-2026-27801

Vaultwarden: 2FA Bypass on Protected Actions due to Faulty Rate Limit Enforcement

6.0 MediumFix available

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.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
Low
Integrity
High
Availability
None

Exploit likelihood

0.29%Up 0.2 points since Mar 5, 2026
90%50%10%1%

What each source reports

CVE List Score used 6.0

Vaultwarden: 2FA Bypass on Protected Actions due to Faulty Rate Limit Enforcement

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.

Record
CVE-2026-27801
CVSS 4.0
6.0 MediumCVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-307
Updated
2026-11-02
EUVDEUVD-2026-9501 6.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.

Record
EUVD-2026-9501
CVSS 4.0
6.0 MediumCVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-v6pg-v89r-w8wr 6.0

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:

pub async fn validate_protected_action_otp(
    otp: &str,
    user_id: &UserId,
    delete_if_valid: bool,
    conn: &mut DbConn,
) -> EmptyResult {
    let pa = TwoFactor::find_by_user_and_type(user_id, TwoFactorType::ProtectedActions as i32, conn)
        .await
        .map_res("Protected action token not found, try sending the code again or restart the process")?;
    let mut pa_data = ProtectedActionData::from_json(&pa.data)?;

    pa_data.add_attempt();
    // Delete the token after x attempts if it has been used too many times
    // We use the 6, which should be more then enough for invalid attempts and multiple valid checks
    if pa_data.attempts > 6 {
        pa.delete(conn).await?;
        err!("Token has expired")
    }

    // Check if the token has expired (Using the email 2fa expiration time)
    let date =
        DateTime::from_timestamp(pa_data.token_sent, 0).expect("Protected Action token timestamp invalid.").naive_utc();
    let max_time = CONFIG.email_expiration_time() as i64;
    if date + TimeDelta::try_seconds(max_time).unwrap() < Utc::now().naive_utc() {
        pa.delete(conn).await?;
        err!("Token has expired")
    }

    if !crypto::ct_eq(&pa_data.token, otp) {
        pa.save(conn).await?;
        err!("Token is invalid")
    }

    if delete_if_valid {
        pa.delete(conn).await?;
    }

    Ok(())
}

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.

let mut pa_data = ProtectedActionData::from_json(&pa.data)?;

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.

pub fn add_attempt(&mut self) {
    self.attempts += 1;
}

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…

Record
GHSA-v6pg-v89r-w8wr
CVSS 4.0
6.0 MediumCVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-307
Updated
2026-11-02
NVD 6.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.

Record
CVE-2026-27801
CVSS 4.0
6.0 MediumCVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:H/A:N
Weakness
CWE-307
Updated
2026-11-02
OSV 6.0

Vaultwarden: 2FA Bypass on Protected Actions due to Faulty Rate Limit Enforcement

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.

Record
CVE-2026-27801
CVSS 4.0
6.0 MediumCVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-307
Updated
2026-11-02
OSVGHSA-v6pg-v89r-w8wr 6.0

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:

pub async fn validate_protected_action_otp(
    otp: &str,
    user_id: &UserId,
    delete_if_valid: bool,
    conn: &mut DbConn,
) -> EmptyResult {
    let pa = TwoFactor::find_by_user_and_type(user_id, TwoFactorType::ProtectedActions as i32, conn)
        .await
        .map_res("Protected action token not found, try sending the code again or restart the process")?;
    let mut pa_data = ProtectedActionData::from_json(&pa.data)?;

    pa_data.add_attempt();
    // Delete the token after x attempts if it has been used too many times
    // We use the 6, which should be more then enough for invalid attempts and multiple valid checks
    if pa_data.attempts > 6 {
        pa.delete(conn).await?;
        err!("Token has expired")
    }

    // Check if the token has expired (Using the email 2fa expiration time)
    let date =
        DateTime::from_timestamp(pa_data.token_sent, 0).expect("Protected Action token timestamp invalid.").naive_utc();
    let max_time = CONFIG.email_expiration_time() as i64;
    if date + TimeDelta::try_seconds(max_time).unwrap() < Utc::now().naive_utc() {
        pa.delete(conn).await?;
        err!("Token has expired")
    }

    if !crypto::ct_eq(&pa_data.token, otp) {
        pa.save(conn).await?;
        err!("Token is invalid")
    }

    if delete_if_valid {
        pa.delete(conn).await?;
    }

    Ok(())
}

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.

let mut pa_data = ProtectedActionData::from_json(&pa.data)?;

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.

pub fn add_attempt(&mut self) {
    self.attempts += 1;
}

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…

Record
GHSA-v6pg-v89r-w8wr
CVSS 4.0
6.0 MediumCVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-307
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
vaultwarden
crates.io
through 1.34.3
1.35.0
vaultwarden
crates.io
before 1.35.0
1.35.0

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-27801 is a medium vulnerability (CVSS 6.0) in Dani-garcia Vaultwarden, published March 4, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.29%, the 21st percentile. Fixed in vaultwarden 1.35.0.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open