As part of the time that my company offers for research, my good friend and talented hacker Alberto Illera (@algillera) and me decided to “checkout” LastPass.

Many of you may already know (or even use) LastPass. It is a pretty well known password manager that stores all your passwords in a “vault” and keeps them secure. Additionally, it can automatically populate the credentials for you when you visit a website in which your are registered making it easy to use more secure, random and unique passwords. You will just have to remember the master password that decrypts the vault and that’s all.

LastPass comes in many forms. As a browser plugin, as a mobile app or even as Webapp.

As you may agree, a service that stores all your passwords sounds like a cool target so we decided to have “A look into LastPass”, understand how it works, check if it really keeps our passwords secure and why not? Try to find vulnerabilities.

How does LastPass work?

One of the advantages of LastPass is that your master password and vault are never sent in cleartext to their servers. That means that not even LastPass knows your passwords which in the NSA era is something to consider. LastPass encrypts and decrypts all your data locally in your machine using AES256. Data sent to LastPass is hashed with SHA256 and hardened with PBKDF2. You can find more information in their website.

We decided to put our efforts into their API, the website and specially into reverse engineering the Chrome plugin.

API and Website

We found several issues that we reported to them and while they did not consider some of our findings, they did acknowledge some of them.

Weak password policy

I was surprised to find out the the master password policy is that it needs to be at least 8 characters long. That’s it! No mandatory uppercase, numbers, special characters, etc. Again, this is the policy for your master password, the one that protects all your other passwords. I think there is no reason to allow master passwords like ‘aaaaaaaa’ or ‘qwertyui’.

Password reminder

It is part of best practices that the password reminder cannot be the

actual password not to have password reminders. 2 factor authentication should be the way to go but LastPass has this option. While indeed, the reminder cannot be the password itself, we found out that it can “contain” the password. If your password is say, ‘qwertyui’, your reminder cannot be ‘qwertyui’ BUT it can be ‘my password is qwertyui’. This might be because they hash the password so there is no way for them to tell that the sentence contains the password unless they hash the words separately to compare first.

While the reminder gets send to your email rather than being simply displayed in the website, if the email account gets hacked, one of the first things an attacker would do is to look for keywords like “password” to find emails regarding password resets, reminders, etc.

The number of PBKDF2 iterations is public

As mentioned above, the master password gets hashed with SHA256 + PBKDF2 with a certain number of iterations. While security by obscurity is not a good thing, I believe that keeping the number of rounds secret adds extra protection. Anyone can check how many iterations where used for any user.

It is worth noting that older accounts have the default rounds set to 500. With the power of the current desktop computer this is not enough nowadays. Steve Thomas points it out in this great post. Also, as explained in his post, you can see that LastPass “fixed” the problem with the first request being iterated just once by making it rounds publicly accessible which is what we reported.

Newer accounts default to 5000 now which is better but I recommend you to check and update the number of iterations accordingly in the settings panel. (I had it to 500 and my account is only 3 years old).

CSRF

The API call to CRUD new data such as new sites, secure notes, etc. is not CSRF protected. Actually it is, but you can simply remove the parameter containing the CSRF token and it will go through. While this sounds pretty devastating (imagine deleting all credentials of a user), it is not that simple to exploit. As mentioned above, all data is encrypted so you would need to know how to correctly encrypt the data (aka knowing the master password) in order to effectively perform the attack. Funny story, while doing tests I did probably broke something because I got an email from the CEO wondering what the hell I was doing…

Bypassing 2 factor authentication

LastPass allows 2 factor authentication in several ways. It supports Yubikey, Google Authenticator, Keypass and others. We just focused on Google Authenticator as we believe it is the most popular and it is free.

We realized that it was possible to obtain the QR Code to setup the Google Authenticator by just stealing the session id. This means that retrieving the 2 factor authentication QR code was actually bypassing 2 factor authentication!

The main problem was that the request was not protected by prompting for the master password again. Sensitive actions like changing passwords, personal information and, in this case, activating/deactivating 2 factor auth, should always prompt for the password again.

Best thing was that even if the victim would not have 2 factor auth enabled, you could still retrieve the QR Code. That would help in case the victim realizes that the account was hacked and activates 2 factor auth as counter measure. It doesn’t matter, the attacker has that already!

Browser plugin

Once we decided that we had enough material on the API, we moved on to the browser plugin. We worked specifically on the Chrome plugin but we are confident that all other browser work on the same code base.

The plugin is written in javascript so no fancy assembly here. Still, it was minimized and optimized so we where looking at function names and variables such as ‘a’, ‘ab’, etc which made it all more difficult to reverse engineer.

Alberto wrote a small script to help us understand what the plugin was doing. This script would add as the first line of every function some logic to print information about the method itself and the value of the parameters passed. With that, we had a chronological stack of function calls and their parameter values. These was a big help and helped us identify the interesting parts of the plugin logic.

What caught our eye was what was happening when the user would click the option “store password”. This functionality provides the commodity of never having to enter the master password again as it will be populated to the plugin and you will be automatically logged in.

We found that the username and master password was stored in a sqlite3 database in the system. This database is not encrypted and there is no need to be root to access it. The username is stored in cleartext but the master password is encrypted.

Our next target was to figure out how it was encrypted and see if we could decrypt it. With the help of our script, we understood that the database was encrypted using 2 different encryption modes: AES256 CBC and ECB. I am not sure why they support ECB and we did not bother finding out or how the choice to use on or another was made.

Once we knew how the password was encrypted, we had to find out how the IV and key was calculated and which was the data to be decrypted. It took us a while but finally we figured it all out.

The encrypted data stored in the database is base64 encoded and has two different formats depending if CBC or ECB was used for encryption:

CBC: It includes ! at the beginning and | as the 25th character, e.g. !L5b/dOyu4EMdmWCYkASQaw==|cHTFJDy1DQi8dPY0AJL/1B== ECB: It is a simple base64 encoded string, e.g. u7W1PsEYsWrtAS1Ca7lOOH==

For CBC, the different parts correspond to:

KEY: The key is the email hashed with SHA256 and converted to binary

IV: The IV is the 24 first characters of the encrypted data stored in the password field (ignoring !)

DATA: The data to be decrypted is the portion of the stored encrypted string left, starting at the 26th character (ignoring |)

For ECB, the key is the email hashed to SHA256 and converted to binary as well and the data to be decrypted is the stored encrypted string.

Metasploit Module

With all this information, we where finally able to obtain master passwords in cleartext. Woo hoo! Our attack only covers users that click the “Store my password” option though so, don’t store your master password!

We decided to write a metasploit plugin so our research would be actually helpful to the community. This work is still in progress but we are close to send the pull request to metasploit. If you want to have a better understanding of everything explained here, checkout the source code of our module in my github repository, I tried to add comments and write the code as clear as possible.

Responsible disclosure

As always, we made a responsible disclosure to LastPass. I want to stress how easy it was to work with the security team. The where very responsive and worked on fixing the issues we reported immediately. They also followed up with us from time to time and asked for our thoughts on every fix. It was a real pleasure to work with team!

This is a list of some of the things they fixed based on our disclosure to them:

Retrieving the QR Code for 2 factor authentication does prompt for the master password now It also sends an email to the user informing of this action including information of the IP where the request was originated from The plugin warns that it is insecure to store the password locally when the option is checked They are working on detecting if the password hint contains the master password

Also, they showed their appreciation by adding us to their hall of fame and they also sent us some swag.