clients icon indicating copy to clipboard operation
clients copied to clipboard

Firefox browser plugin keeps master password in memory when locked

Open robdavid opened this issue 3 years ago • 15 comments

Describe the Bug

The Bitwarden FAQ states:

We do not keep the master password stored locally or in memory. Your encryption key (which is derived from the master password) 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. We also reload the application’s renderer process after 10 seconds of inactivity on the lock screen to make sure any managed memory addresses which have not yet been garbage collected 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.

However, in my testing, I have found that once you unlock your vault with the master password in the Firefox extension, and then lock it again, the master password is still recoverable from the memory of the running Firefox browser. This does not seem to be the case for the standalone application, nor for Chrome or Edge browser extensions. I haven't tested against other browsers.

Steps To Reproduce

These are the steps I used on a Linux system. I have found similar behaviour on Windows using different tools.

  1. Launch a new instance of Firefox

  2. Open the Bitwarden extension and enter the master password to access the Vault.

  3. Dump the memory from the browser and search for your master password.

    There are various ways to do this. I used a utility I wrote here to read process memory, with the following commands:

    unset HISTFILE # Don't save bash history
    pidof firefox | xargs dumpproc -o - | grep MASTER_PASSWORD
    
  4. Lock the vault in the extension Settings -> Lock now

  5. Repeat the search for your master password.

Expected Result

The master password should not be found after the vault is locked (step 5).

Actual Result

The master password was found both before and after locking, steps 3 & 5 (but not before step 2). From the output, and some trial and error, it's possible to identify the specific process ID that contains the password secret, e.g.

dumpproc -o - 9486 | grep MASTER_PASSWORD

There's is a notable difference in the way that the Firefox extension behaves compared with Chrome and Edge browser extensions and the stand alone application. In those latter cases, the process that contains the secret disappears a few seconds after the vault is locked. This seems consistent with he statement in the FAQ about the reloading of "application’s renderer process after 10 seconds of inactivity on the lock screen". This is in contrast to the Firefox extension, where the process seems to remain indefinitely, still holding the secret in memory.

I have found the same behaviour running on Windows. In this case I used HxD to manually capture and search memory data from browsers and the app.

Screenshots or Videos

Screenshot from 2021-01-02 13-42-39

Environment

  • Operating system: Ubuntu 20.04.1 LTS
  • Browser: Firefox 84.0
  • Build Version: 1.47.1

robdavid avatar Jan 02 '21 16:01 robdavid

I'm not certain, but I think this is the relevant code: from src/services/browserStorage.service.ts:

async remove(key: string): Promise<any> {
    if (this.isSafari) {
        await SafariApp.sendMessageToApp('storage_remove', key);
    } else {
        return new Promise((resolve) => {
            this.chromeStorageApi.remove(key, () => {
                resolve();
            });
        });
    }
}

Firefox, per their docs, uses the Chrome storage API "with a few differences", so assuming this finding is reproduceable the two places I would think to look are (a) making sure the extension uses the correct permissions on Firefox (first glance at manfiest.json looks like this should be ok) and (b) whether this is one of those "few differences" in Firefox that is going to require a special case to work as expected here.

apmarshall avatar Jan 05 '21 21:01 apmarshall

I need to slightly amend my original report. Running further tests under Linux it seems that it is not the case that the process holding the master password is terminated on Chrome and Edge. Rather, it is the case that the master password can no longer be found in the process's memory. Firefox, in contrast however, continues to retain a copy of the password. It may be that it was only on Windows that I was seeing the process termination behaviour. I will try to verify that later.

On Linux, I've grabbed a few screenshots of how the number of password matches in memory changes over time, from the point the vault is locked, because I think it's interesting. My shell command scans memory every 3 seconds counting the number of password matches found. Each time I locked the vault in the extension a few seconds after I started the scan.

This is the result for Chrome. dumping-chrome-memory

Edge, is perhaps unsurprisingly, very similar dumping-edge-memory

And this is Firefox dumping-firefox-memory

From this it looks like that an attempt is made to purge the password from memory a short time after locking, as with the other browsers, but unlike the other browsers, one copy still persists.

robdavid avatar Jan 06 '21 18:01 robdavid

I have managed to confirm the different behavior on Windows comapared to Linux on Edge and Chrome. On Windows this time I used Process Hacker 2 to dig into process memory. You can do this by selecting a process and following

Properties -> Memory tab -> Strings... button -> choose string length -> Filter button -> Contains...

and then search for the password. Unfortuntately you have to search manually through each browser process to identify the one where the extension is running containing the master password.

After locking the vault, I found that on Edge and Chrome, the password is still present for about 10 seconds after the vault is locked. Then the process terminates. On Firefox, the process remains indefinitely, and the password is sill present.

Maybe this provides a useful clue as to what is happening, but I don't really know.

robdavid avatar Jan 08 '21 08:01 robdavid

Thanks for the info @robdavid . Looking into this a bit more, we are handling Firefox and Opera differently than Chrome and others. On Firefox and Opera, we call window.location.reload(true); on the background and popup processes. On Chrome we call chrome.runtime.reload();.

The reason we do this is that Firefox and Opera have sidebars, and calling chrome.runtime.reload(); makes the sidebar close and open, which is not desired. I figured just reloading the window would result in the same effect.

I wonder if we test a Firefox build with it doing chrome.runtime.reload(); would we have the same problem or not.

kspearrin avatar Jan 08 '21 19:01 kspearrin

I'm happy to try a test build if one becomes available.

robdavid avatar Jan 09 '21 22:01 robdavid

I have used dumpproc to reproduce the issue locally, and have found that the master password is maintained in memory, even when the login process hasn't been completed successfully in an account with 2FA enabled.

Prerequisites

  • Have 2FA enabled in the Bitwarden account.
  • Remove the device from 2FA trusted devices, so the extension asks for 2FA code during login process.

Steps to reproduce

  • Launch a new instance of Firefox
  • Open the Bitwarden extension and enter the master password to access the Vault.
  • When asked for the 2FA code, cancel the login process entirely.
  • Dump the memory from the browser and search for your master password.

Even deleting the Firefox extension after following these steps, doesn't remove the master password from the browser's memory.

@robdavid, feel free to test it yourself, before considering this report valid.

adamantike avatar Jan 23 '21 17:01 adamantike

After testing it further, I found that the last master password used in a login attempt is maintained in memory, even if the login wasn't successful. Just trying any credentials, and then confirming that the used password is still in memory, until a new login attempt is made.

I wonder if this is the root cause for the password still in memory, and that it's actually not related to the "Lock vault" functionality.

adamantike avatar Jan 24 '21 16:01 adamantike

I can indeed replicate this behaviour. Anything you type into the master password field is findable in the memory dump afterwards. I generated this random string to enter into the input field:

Zt+mlmQmZy8dA7uO

I entered this into the Bitwarden extensions's master password field and hit enter, and got a message telling me it was the wrong password. Then I used dumpproc to dump memory into a file, opened it with ghex, searched for my input, and found a match.

image

As well as the plain text match, there's a version just before with the '+' character encoded as %2B. Makes me think this could be form submission data.

robdavid avatar Jan 24 '21 21:01 robdavid

Can you replicate this behavior on any form you type data into with Firefox (extension or website)?

kspearrin avatar Jan 24 '21 23:01 kspearrin

I tried this with just a login to a website. I noticed some similar but not exactly the same behaviour. If I entered my password but did not submit, I could find my password in memory. After submitting, I could still find it, but after a period of time it couldn't be found any more. I did not time exactly how long, but after something in the order of a minute or two.

robdavid avatar Feb 04 '21 22:02 robdavid

I also tested this, on chromium (brave) the extension has an own process that dies after 10 seconds and the new process has no stored information about the password. On Firefox every extension uses the same process and the password is stored for a very long time.

Easy fix: Use this as your master password:"..........ååååå"

Just joking here is the real fix: If you try to login again with a wrong password, it replaces it in the memory, so type something wrong and try to unlock. That's it. Thanks Mozilla!

Tokukarin avatar Jun 24 '21 13:06 Tokukarin

This seems to be a pretty serious bug and it's been open for more than 6 months, any news about it?

andreapx avatar Jun 28 '21 10:06 andreapx

Hi all,

I’ve done some investigation into this issue, and it seems that Firefox does keep the master password in memory even when locked. Unfortunately, we haven’t identified a way to clear the memory. Even after manually refreshing the background page Firefox seems to still track the password in memory for an unknown duration. It’s most likely marked as unused but not removed by the GC for some reason.

The one thing I’ve found that does help is to manually disable and re-enable the extension, unfortunately that’s not something that can be done without user interaction.

We will continue to investigate this issue and look for potential solutions, however I would also like to highlight that we are planning a migration to Manifest V3 for our browser extensions during the second half of the year. Manifest v3 will eliminate the persistent background page which I suspect would solve this issue. Obviously postponing fixing this issue is not optimal hence why we will continue to investigate alternative approaches. (The Manifest v3 work is currently waiting for chrome.storage.session to be available in Chrome stable, and Firefox to support the full Manifest V3).

Hinton avatar Jan 11 '22 15:01 Hinton

Any news on this serious problem? When will the extension switch to Manifest V3? Question: in this page has been written that the password gets left in the memory after the vaults gets locked, but what about when it is unlocked? Does the password resides in memory in clear text?

andreapx avatar Aug 02 '22 18:08 andreapx

Hi, @andreapx, an update; the team is currently working on building a functional extension with Manifest v3, and will be ready for the browser beta release, hopefully before end of the year.

dbosompem avatar Aug 03 '22 14:08 dbosompem

Thanks for the info @robdavid . Looking into this a bit more, we are handling Firefox and Opera differently than Chrome and others. On Firefox and Opera, we call window.location.reload(true); on the background and popup processes. On Chrome we call chrome.runtime.reload();.

The reason we do this is that Firefox and Opera have sidebars, and calling chrome.runtime.reload(); makes the sidebar close and open, which is not desired. I figured just reloading the window would result in the same effect.

I wonder if we test a Firefox build with it doing chrome.runtime.reload(); would we have the same problem or not.

Instead of reloading, can't you override the value by writing something to the variable?

llitz avatar Feb 09 '23 16:02 llitz