7.1 High
CVSS3
Attack Vector
LOCAL
Attack Complexity
LOW
Privileges Required
LOW
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
NONE
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
3.2 Low
CVSS2
Access Vector
LOCAL
Access Complexity
LOW
Authentication
SINGLE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
NONE
AV:L/AC:L/Au:S/C:P/I:P/A:N
0.0004 Low
EPSS
Percentile
5.7%
Bitwarden Desktop on Windows allows the user to enable vault unlock through Windows Hello (under File > Settings > Unlock with Windows Hello). When this is done, a “Biometric master key” is generated and stored locally inside the Windows’ user credential set. This is done through the “wincred” API, in particular through the functions CredWrite and CredRead that are called from the native module written in Rust (here: apps/desktop/desktop_native/src/password/windows.rs
). The item created in the user credential set has a name of the form Bitwarden_biometric/<account_uuid>_masterkey_biometric
.
When unlocking the vault through Windows Hello, the unlock operation prompts the user for authentication through Windows Hello (here: apps/desktop/src/main/biometric/biometric.windows.main.ts
line 45). If the authentication succeeds, the vault is unlocked and the items are decrypted after decrypting the master keys through the keys derived from the biometric master key.
However, the authentication through Windows Hello is unneeded. Commenting the line linked above, the vault still unlocks with no problem. The biometric master key can in fact be retrieved with a simple call to the CredRead
windows API function, and then used to decrypt the locally saved data present in %appdata%\Bitwarden\data.json
. The Windows Hello authentication prompt therefore gives a false sense of security to the user, making it seem as if authentication is needed to decrypt vault data, when in reality it is not.
The local data in %appdata%\Bitwarden\data.json
, as well as the master biometric key, persist after the vault is locked and the Bitwarden desktop allication is closed. Both of them are easily readable by any program running in the current user session, without the need to elevate privileges. Furthermore, both are accessible to any administrator account on the same machine.
Given the above, a potentially malicious program running locally on the machine can decrypt the entirety of the user vault, i.e. all ciphers (logins, cards, notes, identities). Furthermore, since the Desktop application stores locally (still in %appdata%\Bitwarden\data.json
) a refresh token and a bearer token used for authentication with the Bitwarden server, user data can easily be modified, encrypted and updated on the server on behalf of the user.
The attached proof of concept (of which a recorded video demo is also attached) consists of a Python 3 script that uses the ctypes
module to extract the biometric master key (if any) from the current user’s credential set, decrypts all cipher names stored locally, and also modifies and updates the content of a secret note with a specific (if present), sending the new data to the server through standard bitwarden API.
The setup needed before running the proof of concept script is as follows:
Super Secret Note
and arbitrary content.Now to run the proof of concept, follow these steps:
Install Python 3 (recommended Python) on the machine if not already present.
Install the following libraries through Pip: pycryptodome
Cryptography
and requests
.
python -m pip install pycryptodome Cryptography requests
Launch the Python 3 script from a CMD or Powershell prompt (running as administrator is not needed):
python poc.py
The proof of concept script output should be similar to the following:
Account: <uuid-of-the-account>
-> Biometric master key: <bio-master-key-in-hex>
-> Encryption key: <master-encryption-key-in-hex>
-> HMAC key: <master-hmac-key-in-hex>
-> Decrypted entry: Super Secret Login
-> Decrypted entry: Super Secret Card
-> Decrypted entry: Super Secret Note
-> Modifying item: Super Secret Note
-> Old content: b'This is a super secret note!'
-> New content: b'Pwned!'
-> Sending API request...
-> Response: 200
NOTE: Python 3 was my choice for its simplicity and ease of use. The same operations could be performed by other means as well (for example through a native application).
An attacker that is able to briefly obtain local access to a machine with Bitwarden desktop installed, is able to decrypt the contents of all vaults that have unlock through Windows Hello enabled. On top of that, an attacker would also be able to alter their content through simple API requests to the bitwarden server, since they have the symmetric encryption and HMAC keys used to encrypt the fields of any item in the vault.
Furthermore, on a multi-user Windows machine, any administrator account has the ability to perform the same operations for any other users on the same machine that is using Bitwarden desktop with Windows Hello unlock enabled. Although not implemented in the attached proof of concept, this would be possible by simply enumerating local users and accessing each user’s credential set, enumerating the entries and retrieving any Bitwarden biometric master key that is present.
In conclusion, Bitwarden Desktop’s biometric authentication through Windows Hello gives the user a false sense of security. While normally the vault data is locally stored encrypted without the master key, in case of Windows Hello authentication the biometric master key is locally stored in plaintext as well. This contradicts the Bitwarden Security Whitepaper, which states:
> We do not keep the Master Password stored locally or in memory on the Bitwarden Client. Your encryption key (Symmetric Key) is kept in memory while the app is unlocked. This is needed to decrypt data in your Vault. When the Vault is locked, this data is purged from memory. After a certain time frame of inactivity on lock screen, we reload the application processes to make sure that any leftover managed memory addresses are also purged. We do our best to ensure that any data that may be in memory for the application to function is only held in memory for as long as you need it and that memory is cleaned up whenever the application is locked. We consider the application to be completely safe while in a locked state.
7.1 High
CVSS3
Attack Vector
LOCAL
Attack Complexity
LOW
Privileges Required
LOW
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
NONE
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
3.2 Low
CVSS2
Access Vector
LOCAL
Access Complexity
LOW
Authentication
SINGLE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
NONE
AV:L/AC:L/Au:S/C:P/I:P/A:N
0.0004 Low
EPSS
Percentile
5.7%