Please help generating master password hash in server db to retrieve wife's lost password

I posted to Reddit but thought I’d post here too.

Hi, please direct me somewhere else if this isn’t the place to ask.

My wife had to change phones and can’t get into vaultwarden as her master password is wrong.
The hint verifies she has the correct password but she must’ve substituted a numerical / alpha swap differently and can’t work it out due to rate limiting.
I understand the importance of this password and she shouldn’t have forgot it or at least have it saved somewhere but here we are.

Anyway my question is seeing as I’m the administrator and have full access to the DB can I try to brute force her password against whatever value in the DB directly to avoid rate limits as I know the letters numbers and length used for the password just not the correct substitutions?

If so to save me reading the source code to find out what is the correct format to generate the password hash and which value in the DB do I compare it to to confirm its correct.

I am fine with writing my own script to do this I just need the finer details of what exactly I need to do
My server at the time of password creations / last change is Version 2022.10.1.

Thank you.

UPDATE:
So I tried a few things and they either didn’t work or were too slow but I’m close to a solution. (I have to brutes force around 500,000 password attempts to get back in so I want to compute directly against the master_password_hash)

If you go to Bitwarden Crypto and leave the default values then run the following python 3 script you will see the hashes match therefore I am computing them correctly as per the local / Bitwarden client.

The problem I have is I exported my ‘db.sqlite3’ from vaultwarden and extracted the ‘salt’ and ‘password_hash’ for my account but when I enter my correct login details and the db data into my python script the first two hashes (local / bitwarden client) match but the third hash (server / vaultwarden) doesn’t match.

My iterations all match my settings (100,000) so I cannot see where I am going wrong.

Anyone know what I should be doing for the server side hash calculation so it matches the db?

The official flow for all of this is on the bitwarden GitHub under help/master/images/security-white-paper/bitwarden-password-hashing-key-derivation-encryption.png
(Sorry as a new user, I couldn’t post the link)

Python Code here as couldn’t get it to inline: import hmacimport hashlibimport structimport base64def pbkdf2(digestmo - Pastebin.com

If you use Vaultwarden, then i suggest to increase the rate limiting threshold and brute force instead of trying to generate the hash stored in the database.

The hash generated by the Bitwarden clients isn’t the same as what is stored in the database. If you are using the latest stable Vaultwarden, then it runs a PBKDF of 600_000 over the received hash before it stores it into the database.

But, as mentioned, i suggest to increase the rate limiting threshold.

Thank you for the reply.

I understand that the server runs another hash on the master_password_hash before storing it in the database using a salt randomly generated and also stored in the database as per this image:
https://raw.githubusercontent.com/bitwarden/help/master/images/security-white-paper/bitwarden-password-hashing-key-derivation-encryption.png

I have already increased my rate limits to allow me to brute force that way but it is slow due to networks latency and the API requests and all the overheads involved.

Due to the sheer number of attempts I may need I can match a hash much quicker and also push it to my GPU so would prefer that way if possible, worst case I can leave it running via the API for however long it takes.

In my python script I do 100,000 PBKDF then another single PBKDF before then switching to server mode and performing another 100,000 PBKDF which should give me the hash in the db as per the image linked but it doesn’t, is this not possible or where have I gone wrong?

I assume the salt I am using from the db maybe isn’t correct?

I am generating 100,000 as that is what the vaultwarden and bitwarden client settings were when this hash was made.

Well, in the database there also is a field which states the amount of iterations the stored password has password_iterations, that should be the correct amount.

Further i suggest to first test it on your own account, not sure if you did that already, but from your story i concluded that you didn’t because you don’t know if you are doing it correctly. So, i would start there.

Also checkout Bitwarden Crypto, which might help in verifying the correct values of each step you took during the separate hashing.

Other than that, there is no magic way to match the hash except for the used password.
It would be easier useless if that was the case of course.

Thank you for the help.

Yes I used the password_iterations field to verify my 100,000 iterations is correct.

Also as linked in my first post I used the bitwarden crypto website to confirm my first two generations of the master key and master password are correct.

I used the salt value from the db to generate the final hash which should match the db hash but it doesn’t.

This value isn’t on the bitwarden crypto site so I can’t confirm it that way.

I’ve been testing all of this against a known account before I try on the lost password one.

My problem is I followed the bitwarden client and vaultwarden server source code to get my procedure yet it doesn’t work.
My only unknown is the salt and password hash db entries and why they don’t match when they should.
Unless it’s a language nuance between python and rust I can’t see the problem?

What you are looking for is the following:

Take a good look at the password, as it is converted to bytes first, so it isn’t the string sent by the clients, but a bytes version of it which is sent to the hashing function.

See: str - Rust for what that function does.
Now, I’,m not sure what the correct Python version would be but this might help.

password = "StringFromClient"
password_bytes = bytes(password,'UTF-8')
# Or
password_bytes = password.encode(encoding = 'UTF-8')

I think that should do the trick, but, I’m also not sure how the Python code you use is working on this.

Also, make sure you use the right Algorithm, see here:

@BlackDex Thank you so much you amazing human! :innocent:

By saying I needed to convert the string from the client to bytes made me realise that I didn’t have a string from the client.

I missed the return in the hashPassword() function of the client crypto service which is
return Utils.fromBufferToB64(hash);

The key being that the client sends the MasterPasswordHash to the server as a base64 encoded string.

I now am generating a matching hash to the db for my known login, now the brute forcing can finally begin :smile:

You have saved me and hopefully I can retrieve all my wife’s passwords and 2 factor keys and this time I will back them up safely.

Thank you again so much.

Your welcome!

As a tip, you can add eachother as an emergency contact, that way you can always login into the others account.

UPDATE:

After a few attempts of increasing complexity and generating a password list of over 7 million passwords based on the parts we knew I got a match and my wife now has all her passwords back.

Thanks very much to all involved especially @BlackDex :grinning:

I will be updating my vaultwarden server asap and adding myself as an emergency contact too.

3 Likes

@nozza87 hey sir can you share the entire script to decrypt the password you have shared the file at the top but i dont think it has Utils method. I am a llitte bit confused to start recovering the entire process

1 Like

Sorry for the late reply.

Here are the scripts for anyone interested, hopefully they help others :slight_smile:

https://mega.nz/file/V8ADiDLJ#UEPM-yMrbBfXim-7WY2oair6xtGgEcFB3oPlzL13VMg

1 Like