Thursday, June 20, 2013

How Browsers Store Your Passwords (and Why You Shouldn't Let Them)


In a previous post, I introduced a Twitter bot called dumpmon which monitors paste sites for account dumps, configuration files, and other information. Since then, I've been monitoring the information that is detected. While you can expect a follow-up post with more dumpmon-filled data soon, this post is about how browsers store passwords.

I mention dumpmon because I have started to run across quite a few pastes like this that appear to be credential logs from malware on infected computers. It got me thinking - I've always considered it best to not have browsers store passwords directly, but why? How easy can it be for malware to pull these passwords off of infected computers? Since sources are a bit tough to find in one place, I've decided to post the results here, as well as show some simple code to extract passwords from each browser's password manager.

The Browsers

For this post, I'll be analyzing the following browsers on a Windows 8 machine. Here's a table of contents for this post to help you skip to whatever browser you're interested in:

Logos by Paul Irish
Difficulty to obtain passwords: Easy

Let's start with Chrome. Disappointingly, I found Chrome to be the easiest browser to extract passwords from. The encrypted passwords are stored in a sqlite database located at "%APPDATA%\..\Local\Google\Chrome\User Data\Default\Login Data". But how do they get there? And how is it encrypted? I got a majority of information about how passwords are stored in Chrome from this article written over 4 years ago. Since a bit has changed since then, I'll follow the same steps to show you how passwords are handled using snippets from the current Chromium source (or you just skip straight to the decryption).

Encryption and Storing Passwords
When you attempt to log into a website, Chrome first checks to see if it was a successful login:

We can see that if it's a successful login, and you used a new set of credentials that the browser didn't generate, Chrome will display a bar asking if you want your password to be remembered:

To save space, I'm omitting the code that creates the Save Password bar. However, if we click "Save password", the Accept function is called, which in turn calls the "Save" function of Chrome's password manager:

Easy enough. If it's a new login, we need to save it as such:

Again to save space, I've snipped a bit out of this (a check is performed to see if the credentials go to a Google website, etc.). After this function is called, a task is scheduled to perform the AddLoginImpl() function. This is to help keep the UI snappy:

This function attempts to call the AddLogin() function of the login database object, checking to see if it was successful. Here's the function (we're about to see how passwords are stored, I promise!):

Now we're getting somewhere. We create an encrypted string out of our password. I've snipped it out, but below the "sql::Statement" line, a SQL query is performed to store the encrypted data in the Login Data file. The EncryptedString function simply calls the EncryptString16 function on an Encryptor object (this just calls the following function below):

Finally! We can finally see that the password given is encrypted using a call to the Windows API function CryptProtectData. This means that the password is likely to only be recovered by a user with the same logon credential that encrypted the data. This is no problem, since malware is usually executed within the context of a user.

Decrypting the Passwords

Before talking about how to decrypt the passwords stored above, let's first take a look at the Login Data file using a sqlite browser.

Our goal will be to extract the action_url, username_value, and password_value (binary, so the SQLite browser can't display it) fields from this database. To decrypt the password, all we'll need to do is make a call to the Windows API CryptUnprotectData function. Fortunately for us, Python has a great library for making Windows API calls called pywin32.

Let's look at the PoC:

And, by running the code, we see we are successful!

While it was a bit involved to find out how the passwords are stored (other dynamic methods could be used, but I figured showing the code would be most thorough), we can see that not much effort was needed to actually decrypt the passwords. The only data that is protected is the password field, and that's only in the context of the current user.
Internet Explorer
Difficulty to obtain passwords: Easy/Medium/Hard (Depends on version)

Up until IE10, Internet Explorer's password manager used essentially the same technology as Chrome's, but with some interesting twists. For the sake of completeness, we'll briefly discuss where passwords are stored in IE7-IE9, then we'll discuss the change made in IE10. 

Internet Explorer 7-9

In previous versions of Internet Explorer, passwords were stored in two different places, depending on the type of password.
  • Registry (form-based authentication) - Passwords submitted to websites such as Facebook, Gmail, etc.
  • Credentials File - HTTP Authentication passwords, as well as network login credentials 
For the sake of this post, we'll discuss credentials from form-based authentication, since these are what an average attacker will likely target. These credentials are stored in the following registry key:

HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\IntelliForms\Storage2

Looking at the values using regedit, we see something similar to the following:

As was the case with Chrome, these credentials are stored using Windows API function CryptProtectData. The difference here is that additional entropy is provided to the function. This entropy, also the registry key, is the SHA1 checksum of the URL (in unicode) of the site for which the credentials are used.

This is beneficial because when a user visits a website IE can quickly determine if credentials are stored for it by hashing the URL, and then using that hash to decrypt the credentials. However, if an attacker doesn't know the URL used, they will have a much harder time decrypting the credentials.

Attackers will often be able to mitigate this protection by simply iterating through a user's Internet history, hashing each URL, and then checking to see if any credentials have been stored for it.

While I won't paste the entire code here, you can find a great example of a full PoC here. For now, let's move on to IE10.

Internet Explorer 10

Note: Please refer to the comment below by Amy Adams regarding the fact that Windows Store Apps cannot access stored credentials in the way described above. However, this method is still relevant for applications running in the context of the user.

IE10 changed the way it stores passwords. Now, all autocomplete passwords are stored in the Credential Manager in a location called the "Web Credentials". It looks something like the following:

To my knowledge (I wasn't able to find much information on this), these credential files are stored in %APPDATA%\Local\Microsoft\Vault\[random]. A reference to what these files are, and the format used could be found here.

What I do know is that it wasn't hard to obtain these passwords. In fact, it was extremely easy. Microsoft recently provided a new Windows runtime for more API access. This runtime provides access to a Windows.Security.Credentials namespace which provides all the functionality we need to enumerate the user's credentials.

In fact, here is a short PoC C# snippet which, when executed in the context of a user, will retrieve all the stored passwords:

When executing the program, the output will be similar to this:

Note: I removed some sites that I believe came from me telling IE not to record. Other than that, I'm not sure how they got there.

As you can see, it was pretty trivial to extract all the passwords in use from a given user, as long as our program is executing in the context of the user. Moving right along!
Difficulty to obtain passwords: Medium/Very Hard

Next let's take a look at Firefox, which was tricky. I primarily used these slides (among a multitude of other resources) to find information about where user data is stored.

But first, a little about the crypto behind Firefox's password manager. Mozilla developed a open-source set of libraries called "Network Security Services", or NSS, to provide developers with the ability to create applications that meet a wide variety of security standards. Firefox makes use of an API in this library called the "Secret Decoder Ring", or SDR, to facilitate the encryption and decryption of account credentials. While it may have a "cutesy name", let's see how it's used by Firefox to provide competitive crypto:

When a Firefox profile is first created, a random key called an SDR key and a salt are created and stored in a file called "key3.db". This key and salt are used in the 3DES (DES-EDE-CBC) algorithm to encrypt all usernames and passwords. These encrypted values are then base64-encoded, and stored in a sqlite database called signons.sqlite. Both the "signons.sqlite" and "key3.db" files are located at %APPDATA%/Mozilla/Firefox/Profiles/[random_profile].

So what we need to do is to get the SDR key. As explained here, this key is held in a container called a PKCS#11 software "token". This token is encapsulated inside of a PKCS#11 "slot". Therefore, to decrypt the account credentials, we need to access this slot.

But there's a catch. This SDR key itself is encrypted using the 3DES (DES-EDE-CBC) algorithm. The key to decrypt this value is the hash of what Mozilla calls a "Master Password", paired with another value found in the key3.db file called the "global salt".

Firefox users are able to set a Master Password in the browser's settings. The problem is that many users likely don't know about this feature. As we can see, the entire integrity of a user's account credentials hinges on the complexity of chosen password that's tucked away in the security settings, since this is the only value not known to the attacker. However, it can also been that if a user picks a strong Master Password, it is unlikely that an attacker will be able to recover the stored credentials.

Here's the thing - if a user doesn't set a Master Password, a null one ("") is used. This means that an attacker could extract the global salt, hash it with "", use that to decrypt the SDR key, and then use that to compromise the user's credentials.

Let's see what this might look like:

To get a better picture of what's happening, let's briefly go to the source. The primary function responsible for doing credential decryption is called PK11SDR_Decrypt. While I won't put the whole function here, the following functions are called, respectively:

  1. PK11_GetInternalKeySlot() //Gets the internal key slot
  2. PK11_Authenticate() //Authenticates to the slot using the given Master Password
  3. PK11_FindFixedKey() //Gets the SDR key from the slot
  4. pk11_Decrypt() //Decrypts the base64-decoded data using the found SDR key
As for example code to decrypt the passwords, since this process is a bit involved, I won't reinvent the wheel here. However, here are two open-source projects that can do this process for you:
  • FireMaster - Brute forces master passwords
  • ffpasscracker - I promised you Python, so here's a solution. This uses the library as a loaded DLL. To use this on Windows, you can use these cygwin DLL's.

I hope this post has helped clarify how browsers store your passwords, and why in some cases you shouldn't let them. However, it would be unfair to end the post saying that browsers are completely unreliable at storing passwords. For example, in the case of Firefox, if a strong Master Password is chosen, account details are very unlikely to be harvested.

But, if you would like an alternative password manager, LastPass, KeePass, etc. are all great suggestions. You could also implement two-factor authentication using a device such as a YubiKey.

As always, please don't hesitate to let me know if you have any questions or suggestions in the comments below.


  1. Just a note, I think you meant to say "unfair to end the post..." instead of "fair". The following sentence provides a counterexample.

    Great article, I enjoyed reading it!

    1. Hey there! Thanks for the comment (and close reading!) You're absolutely right, and it should be fixed now.

      Thanks again!

  2. JtR and hashkill are much much faster than FireMaster. Do try them ;)

    1. Of course! Great point. I completely forgot that JtR supports Firefox hashes (when looking at the code, it's almost the exact same decryption code that Firemaster uses.. not sure who made it first).

  3. You say: "the password given is encrypted using a call to the Windows API function CryptProtectData. This means that the password is likely to only be recovered by a user with the same logon credential that encrypted the data. This is no problem, since malware is usually executed within the context of a user."

    While that's true, malware running on the machine can also just log the users credentials (e.g. keylogger) or see the protected data. I don't think it's possible to create a password-storage system that's not vulnerable to local malware.

    It seems like Chrome does the Right Thing in using a system-standard library to protect credentials in a way that requires some kind of local access (via malware/etc.) to decrypt.

    1. You make a great point, and I appreciate the comment! It is important to note that these credentials are likely to not be recovered if the malware does not have some kind of local access. The goal of this post was to kind of show what "defense-in-depth" strategies browsers use for protecting these credentials.

      For example, consider how IE7-9 protects the data. It puts an interesting twist on the standard API call by using the hash of the website as added entropy. While this certainly isn't perfect (especially if the user does not clear his/her history), it's still one more layer of defense.

      Or we can consider Firefox, who allows the user to set a Master Password that must be known (and to my knowledge not stored) for decryption. This also provides another layer of defense.

      I hope this helps. You are right in saying that it might be very difficult to protect data against local malware. In Chrome's case, I simply wish they had had a more layered approach. Doesn't mean I'll stop using it as my browser, though :D

      Thanks again for the great comment.

    2. I'm not so sure it really helps all that much unless the attacker is just looking for "passwords" as a whole, rather than specific passwords. If I'm a malware author, it's not *that* hard to check out a few major financial institutions or whatever else I'm targeting and determine the main URL for their login page, then just look for the relevant hash in the registry. Many modern web sites have a standard "http://domain.tld/login" or similar which they redirect users through no matter where they're coming from, so for any targeted attack this added about as much as ROT13.

      Again though assuming the user clears their history it is pretty decent against someone just looking for any passwords at all.

    3. I also don't think that the other browsers have a much higher security level. You have to have control over you system. When this is not the case the security mechanisms would not help you much.
      You also can have a vault with a master key for the passwords. But when the key for the vault is in another vault and the key for the second vault is on the system, this would not help you much, even if it looks like great security features.
      The key is that no one has access to your system. If this is the case, then the security of chrome is good.
      If anyone has access to your system, then nothing can help you, as the atacker can even get access to the master key for firefox, by just changing some files of firefox.

  4. How hard would it be for undetected malware to target the plaintext password in the browser's memory after the user decrypts it instead of trying to decrypt it from the file system?

    1. I'm not sure - it would need to be researched a bit more. To my knowledge, these passwords are decrypted as they are needed, not just stored when the browser opens (though I could be wrong).

    2. Reasonably easy.

      Note comment 1: "If someone has the rights to see your process memory,
      can't they probably also install a keylogger on the machine?"

  5. Why would I have any expectation that my saved passwords would be secure if you had access to my system? If you are getting me to run an arbitrary binary with my user's permissions, haven't you already pwned my account and possibly my box? This smells like a case of "it rather involved being on the other side of this airtight hatchway"

    1. Good question! You're right - in these cases it is assumed malware is already present on the system and running in the context of the user. But there can simply be better protection.

      Consider Firefox's use of a Master Password. Even if an attacker is on the otherside of the airtight hatchway, he/she will not get the credentials unless they can find out the password used.

      Thanks for the great comment!

    2. Chrome's password strategy is basically "your passwords are exactly as secure as your OS". On all platforms, Chrome tries to use the system keyring -- CryptUnprotectedData, KWallet, Gnome Wallet, Keychain, etc. -- and not do anything else. Chrome's security model is to protect you from getting malware on your machine in the first place, rather than to try to mitigate damage from malware introduced via other vectors.

      While the defense-in-depth you discuss here is certainly valid, in the end, if someone has arbitrary code executing on your computer with your user credentials, you're hosed no matter what.

    3. I don't think this thought approach is any good. Various data need various levels of protection. So although it's true that compromised computer is compromised ;) and the attacker could do whatever he wants, the added level of protection of something as valuable as passwords (as Firefox does) is important and could lengthen the window between compromise and successful data theft, in which the compromise may be detected. (You can break my house's windows, but the valuables are in safe).

      Basically there is almost no difference between saving data in plaintext and the Chrome method (in case of local user account compromise).

    4. I believe the only protection encryption can give you in this case is preventing decryption in the event the files are stolen because of a misconfigured share or P2P program. Which all browsers seem to be doing correctly.

      Firefox is also trying to protect the file in case of a compromise, but it'll only work as long as the user detects the infection before actually typing the master password. So while it does add an extra layer of security, I can see why both Chrome and IE don't do this - the extra effort only protects you in a very limited scenario, and it can be damaging to the user experience.

      The IE trick is by far the most imaginative, it also only adds very little extra protection but it doesn't really cost anything from the development standpoint, and it also doesn't change the user experience at all since it's completely transparent.

  6. Do these issues apply when using the Chrome browser on Chrome OS or Android?

    1. I'm not sure - My guess is "probably". Though again, I haven't looked into it.

  7. So did these tests use elevated privileges ... would i expect to see a prompt as a user or can these be done as past of the user context?

    1. No elevated permissions were used. I don't think the user would see a prompt if the process was just started in the background.

  8. I'm confused about the Windows Store app example. Are you suggesting any Windows Store app can access all of IE10's stored credentials?

    1. This comment has been removed by the author.

    2. Jordan: Windows Store Applications cannot access IE or any other Windows Store Application credentials saved in the Credential Locker. More detail on this is described here:

      It would be good to remove the statement in your article about this as its not entirely correct and will cause some confusion. The code you wrote for the IE10 example wouldn't work in a Windows Store Application. However it can work as medium privileged application that a user would have to install on the machine (e.g. like an executable).

      Hope this helps! Thanks for the article!


    3. Hi Amy,

      Thanks for the comment and clarification! I have updated the post, and have removed my comment above.

      Best regards,

  9. On OS X and Linux, we have keychain managers that put the onus of protecting password storage on the principle of least privilege. The idea is that privilege escalation is required to interact with the APIs to retrieve information stored within the keychain. A user would have to explicitly allow local malware to access the keychain when it requests permission.

    An interest side-channel lies in extensions for a browser which already has permission to interact with the keychain; A well-designed extension system will have its own sandbox with access control levels, requiring a privilege "audit" step during installation to explicitly allow the privileges the add-on is requesting access. Chrome and Firefox handles it pretty well, as do Facebook apps, Android apps, iOS apps, etc. If you ever see "This app can access your passwords", that's an immediate red-flag.

    But there's really nothing stopping you from blindly installing malware anyway. :)

  10. One word, Roboform. A much better program then Lastpass. $10 the first year and $20 a year thereafter. Well worth the money.

  11. what about software like dashlane that has plugin for firefox chrome and IE ???

  12. Best program for passwords is Password Safe.

    Open source, free, multiple platforms supported.

  13. Couldn't a hacker simply create a program that opens the browser, goes to the site they want to hack, then copy out the data remembered in the form fields? Not only would it just require some simple JS, it would be much easier than going into the actual data itself and copying out and decrypting from local files.

    Even so, I trust that with some good antivirus and basic computer knowledge that won't happen.

  14. Or... In Firefox, just open the password manager and click show passwords!

    1. yeah they should add an option for a principal password to work only in showing passwords and not every time you want to login a site, where could we suggest a thing like this ??...

  15. Can a comparison test be done on how the native password managers compare with something such as lastpass.

  16. Thanks for your great post! As far as I know, Password Recovery Bundle 2013 can recover website passwords from all popular browsers.

  17. Really nice background, thank you!

    But locally stored passwords have never been highest concern. Like posted above, if malware is running already nothing is save anymore.

    I would be more interessted in the syncronization feature.
    I know from Firefox a public/private key method, not very user friendly but on the first look professional.
    In Chrome it seems to be the "google account token" to enable sync between devices.

    As you where already diving in source code, is there anything special/interessting in that?


  18. Did you by chance have a look at how well the master password is protected in Firefox's memory? A trojan (or a shoulder surfer who found his colleague's PC unlocked) should not have a hard time installing a Firefox addon. And since Firefox does not need to restart for installing and uninstalling most addons, if the master password is accessible via the extension API, it might be stolen that way without restarting Firefox at all.

    Yes, I am aware that Firefox can show you a list of all stored passwords if you have already entered the master password, but you cannot copy them and trying to screenshot it in parts may be a bit harder than just copying the master password and the password db might be a lot faster if the user has stored a lot of unmemorizable passwords in his password safe.

  19. This comment has been removed by the author.

  20. Could you do an analysis on less popular browsers like Safarai or Opera (12.x) as well?

  21. This is helpful, however, it doesn't seem like you considered Chrome with the Sync Passphrase in effect. Also, you recommend password managers, but most people will use those with the 'remember me' option checked--so I'm wondering if Chrome is actually a better option in those cases.

    Here's a security.stackexchange question I'm trying to get an authoritative answer on:

  22. I have been looking the World Wide Web for this information and I want to thank you for this post. link wheel services

  23. well ,here it is explained in detail how to view saved passwords in chrome-

  24. Hey, saw your article and it was really helpful in understanding how the passwords are stored. I did notice though that there is no link when you mention "To use this on Windows, you can use these cygwin DLL's." when talking about ffpasscracker. Also, my attempts at converting this to Windows as a personal experiment with the necessary dll files have resulted in "WindowsError: Exception: access violation writing 0x00000000 when any libnss calls are done. Would you know offhand what could cause such errors or should I guide this question to the creator of that proof of concept?

  25. This comment has been removed by a blog administrator.

  26. Please give me mozila password recovery python code for windows XP & others.

  27. This comment has been removed by the author.

    1. Davaa,

      The post mentions that if a master password is not set, the null value "" is used. This makes it trivial to extract the passwords.

      As the post mentions, you can use Firemaster or ffpasscracker to do this.

  28. This comment has been removed by the author.

  29. Nice info. Today I learned something new. Thanks for sharing.

    Online computer shop

  30. This comment has been removed by the author.

  31. Hi,

    I have an error when I use python script :

    password = win32crypt.CryptUnprotectData(result[2], None, None, None, 0)[1]
    pywintypes.error: (-2146893813, 'CryptUnprotectData', 'Key not valid for use in specified state'.')

    I also tried in adminstrator.

    Do you know where it can come ?


    1. Prenom Nom maybe u should insatll pywin32 same as your python version and work it. It's working no problem.

    2. What version of python works best with this script? I'm working on 3.4 and i get syntax errors on line 36 every time. Tried manipulating the spaces but no good on it. Any idea?

  32. This comment has been removed by the author.

  33. Hi all, How to work this ffpasscracker in win7? Did anyone try it ? i cant decrypt data. If someone know about this. can u give me some hint or advice

  34. Thank you so much for giving great information .

  35. It says win32 crypt is not installed. I tried to search for it on google but couldn't find any thing for windows

  36. I created a malicious browser extension for Firefox 2 years ago, it was able to grab the passwords protected by the master password - just after the user unlocked it. The code to steal it was super easy.

    Also, Citadel is targeting Keepass and Password Safe nowadays.

    Morale of the story: your passwords are not safe when your computer is infected with malware.

  37. Another product to use is ShieldToGo which has the added advantage of being a usb based password manager which keeps your passwords away from the cloud and also provides up to 8GB encrypted storage.