Author Topic: How Mozilla saves passwords  (Read 13325 times)

0 Members and 2 Guests are viewing this topic.

Offline Deque

  • P.I.N.N.
  • Global Moderator
  • Overlord
  • *
  • Posts: 1203
  • Cookies: 518
  • Programmer, Malware Analyst
    • View Profile
How Mozilla saves passwords
« on: January 31, 2013, 04:32:20 pm »
This paper is written from the view of a programmer. It describes which algorithms are used by Mozilla to encrypt login data, i.e. saved passwords and usernames for websites in Firefox or the login data of your e-mail accounts in Thunderbird. I will provide some example code (Java) from my MozillaRecovery program.

How I got the information: (skip this, if you only want the information itself)

An information that you will find without problems is the location of your login data: It is the signons.sqlite (or signons.txt, signons3.txt in older versions), which can be found in the profile folder of your application.

First thing I did was researching about the sqlite format: http://www.sqlite.org/fileformat2.html
It is recommended to use a hex editor to compare the description with your own signons.sqlite file.
The format is well documented, so writing a program that obtains data from an sqlite file shouldn't be a problem.

Because I read that the data is encoded in Base64 and not encrypted if no master password is set, I copied a username entry and tried to decode. But it didn't work. I guess it worked with older versions. Now there is some kind of encryption too.

I searched for open-source programs that recover passwords from Firefox or Thunderbird and found this: http://securityxploded.com/thunderbirdpassdecryptor.php
Old website entries told me it was open-source, but I couldn't find any source to download. Old postings in the forum of securityxploded told me, that they changed this. Some people had used their code for writing maleware, so antivirus scanner recognized their program as a virus. It is pretty sad that the lazyness (not writing their own code, just grieving) and improvidence of some people forced the authors of ThunderbirdPassDecryptor to hide their knowledge. The further search for open-source programs was not fruitful.

In fact, signons.sqlite is useless without the key3.db file, which also resides in the profile folder of your application. This is where the trouble began. I couldn't find information about that file for a long time, so I downloaded the source code of Thunderbird, looked into it for several days and learned more about it's inner workings. I discovered that the login data in the signons.sqlite file is encrypted with TripleDES in CBC mode. The key used for the encryption is saved in key3.db and encrypted as well.

One day I stumbled on this website and it helped me a lot: http://www.drh-consultancy.demon.co.uk/key3.html
It describes how the keys in key3.db can be obtained. But not everything is correct anymore. Some changes are necessary.

First thing that made me think:

Quote
Initially you will need the database password

Where do I get that from?
I just guessed that this is the master password and was right.

I also got the idea that the entry values should follow right after the entry name (I am not sure if it is standard knowledge to do it in another way). I.e. looking at the key3.db in a hex editor you might get that picture on the plain text side:

...................password-check.Version..........

Which means the password-check entry would only have a one byte value. That couldn't be true. But the version entry which follows right after, only has a one byte value. So I tried it backwards, with the entry name following it's value (which lead to the problem to find out where the entries start). It was still not enough to get it working.

Since this website provides some test vectors (I am very grateful for that), I was able to implement and verify the decryption algorithm. Now I knew that it worked with the data on this website, but it still didn't work with my own key3.db file.
I can't really say how I got the idea, but I changed the length of the global salt entry from 16 bytes to 20 bytes. I guess it was just out of a hunch while looking at the hex values. Surprisingly this was the right thing. My test output decrypted the string "password-check" and I was happy. This is how I got the main algorithm for checking if a master password is the right one.

I still didn't implement a program for obtaining the login data out of signons.sqlite, once you got the key entries from key3.db. But my hunger for knowing how it works is satisfied and implementing it shouldn't be necessary at all. Reason: Thunderbird and Firefox show you the data (passwords included) in plaintext, if you know the master password. If no master password is given, the data is not secured at all, just encrypted with a hardcoded key: http://www.infond.fr/2010/04/firefox-passwords-management-leaks.html
(I didn't verify this yet, but I will)

How Mozilla saves login data:

Summary: login data is saved in signons.sqlite. It is encoded in Base64, encrypted with TripleDES in CBC mode and standard block padding. The key for the decryption is saved in key3.db. The entries in key3.db are encrypted with the master password. The decryption algorithm (of the key3.db entries) is not straight forward, but shown right after.

Sqlite Format: http://www.sqlite.org/fileformat2.html

Netscape Communicator Key Database Format: http://www.drh-consultancy.demon.co.uk/key3.html

Work through this description, but change the following:
  • the global salt value is 20 bytes (not 16 bytes) long (I think there may be a value indicating the length of the global salt somewhere)
  • the plain text entry names (i.e. Version, global salt) follow after their values
  • the database password is the master password
To verify the master password and your decryption algorithm, use the check-password entry. Its value is the encrypted string "check-password".

Java example code: extracted from MozillaRecovery

Key3.db key derivation algorithm:

The comments are in the notation of the website mentioned above.

Code: (Java) [Select]
private static String decrypt(byte[] password, byte[] es, byte[] gs, byte[] text) {
        try {
            // HP = SHA1(global-salt||password)
            byte[] hp = SHA.sha1(appendArray(gs, password));
            byte[] pes = Arrays.copyOf(es, 20);
            // CHP = SHA1(HP||ES)
            byte[] chp = SHA.sha1(appendArray(hp, es));
            // k1 = CHMAC(PES||ES)
            byte[] k1 = SHA.sha1Hmac(appendArray(pes, es), chp);
            // tk = CHMAC(PES)
            byte[] tk = SHA.sha1Hmac(pes, chp);
            // k2 = CHMAC(tk||ES)
            byte[] k2 = SHA.sha1Hmac(appendArray(tk, es), chp);
            // k = k1||k2
            byte[] k = appendArray(k1, k2);
            byte[] desKey = Arrays.copyOf(k, 24);
            byte[] desIV = Arrays.copyOfRange(k, k.length - 8, k.length);
            return new TripleDES(desKey, desIV).decrypt(text);
        } catch (NoSuchAlgorithmException e) {
            logger.fatal(e.getMessage());
            e.printStackTrace();
        } catch (BadPaddingException e) {
            logger.debug(e.getMessage() + ". Probably wrong key.");
        }
        return null;
    }


SHA-1 and HMAC-SHA1:

Code: (Java) [Select]
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class SHA {
 
    private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
    private static final String SHA1_ALGORITHM = "SHA-1";

    public static byte[] sha1Hmac(byte[] data, byte[] key) {
        try {
            SecretKeySpec signingKey = new SecretKeySpec(key,
                    HMAC_SHA1_ALGORITHM);
            Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
            mac.init(signingKey);
            return mac.doFinal(data);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;

    }
   
    public static byte[] sha1(byte[] text) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance(SHA1_ALGORITHM);
        md.update(text, 0, text.length);
        return md.digest();
    }
}}


TripleDES:

Code: (Java) [Select]
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;

public class TripleDES {
    private KeySpec keySpec;
    private SecretKey key;
    private IvParameterSpec iv;

    public TripleDES(byte[] keyBytes, byte[] ivString) {
        try {
            keySpec = new DESedeKeySpec(keyBytes);
            key = SecretKeyFactory.getInstance("DESede")
                    .generateSecret(keySpec);
            iv = new IvParameterSpec(ivString);
        } catch (InvalidKeySpecException | NoSuchAlgorithmException
                | InvalidKeyException e) {
            e.printStackTrace();
        }

    }

    public byte[] encrypt(byte[] text) {
        if (text != null) {
            try {
                Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding",
                        "SunJCE");
                cipher.init(Cipher.ENCRYPT_MODE, key, iv);
                return cipher.doFinal(text);
            } catch (IllegalBlockSizeException | InvalidKeyException
                    | InvalidAlgorithmParameterException
                    | NoSuchAlgorithmException | NoSuchProviderException
                    | NoSuchPaddingException | BadPaddingException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    public String decrypt(byte[] text) throws BadPaddingException {
        if (text != null) {
            try {
                Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding",
                        "SunJCE");
                cipher.init(Cipher.DECRYPT_MODE, key, iv);
                byte[] result = cipher.doFinal(text);
                return new String(result, "UTF8");
            } catch (NoSuchAlgorithmException | NoSuchProviderException
                    | NoSuchPaddingException | IllegalBlockSizeException
                    | InvalidKeyException | InvalidAlgorithmParameterException
                    | UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

Offline relax

  • Sir
  • ***
  • Posts: 562
  • Cookies: 114
  • The one and only
    • View Profile
Re: How Mozilla saves passwords
« Reply #1 on: January 31, 2013, 04:47:41 pm »
It's posts like this that made me love EZ
+1

Offline Phage

  • VIP
  • Overlord
  • *
  • Posts: 1280
  • Cookies: 120
    • View Profile
Re: How Mozilla saves passwords
« Reply #2 on: January 31, 2013, 05:02:26 pm »
This is really high quality stuff. Please keep it up! And of cause +1.
"Ruby devs do, in fact, get all the girls. No girl wants a python, but EVERY girl wants rubies" - connection

"It always takes longer than you expect, even when you take into account Hofstadter’s Law."

Offline WirelessDesert

  • Knight
  • **
  • Posts: 356
  • Cookies: 10
  • I think...
    • View Profile
Re: How Mozilla saves passwords
« Reply #3 on: February 01, 2013, 11:53:47 am »
Wow, good research you've done here.
+1
Check out my arduino project: Moving car - School project!
"I'm like current, I always take the easiest route."

Offline parad0x

  • VIP
  • Royal Highness
  • *
  • Posts: 638
  • Cookies: 118
    • View Profile
Re: How Mozilla saves passwords
« Reply #4 on: February 01, 2013, 11:59:48 am »
You really know crytography cryptology. +1 from me. :)

Offline sn0w

  • Serf
  • *
  • Posts: 39
  • Cookies: 16
  • Do your best and prepare for the worst.
    • View Profile
Re: How Mozilla saves passwords
« Reply #5 on: February 01, 2013, 01:16:01 pm »
Nice research. Can't give +1, guess my post aren't enough for that. + 1  ;)
« Last Edit: February 16, 2013, 08:55:03 pm by sn0w »

Offline rafX

  • NULL
  • Posts: 3
  • Cookies: 0
    • View Profile
Re: How Mozilla saves passwords
« Reply #6 on: May 07, 2013, 02:08:53 pm »
Great description and thanks for source code examples. I've been trying to go one step further and try to decode entries in signons.sqlite. I've read these items are encrypted with 3DES ant next encoded to Base64 format but I suppose it a little bit more complicated... So I have a few questions.

1. If you read encrypted values from signons.sqlite and decode them from Base64 you would receive array of bytes with length is undivided by 8... As I know the length of encrypted data by 3DES (CBC) should by multiple of this value, but it's not. Probably something should be omitted, am I right?

2. I'm still wondering where private keys entries are located in key3.db file. I guess it should be from 0x00002F60 byte but it's still hard to me to clarify how the key value (and parameters like salt) should by obtained

Cheers

Offline Deque

  • P.I.N.N.
  • Global Moderator
  • Overlord
  • *
  • Posts: 1203
  • Cookies: 518
  • Programmer, Malware Analyst
    • View Profile
Re: How Mozilla saves passwords
« Reply #7 on: May 07, 2013, 03:16:17 pm »
Great description and thanks for source code examples. I've been trying to go one step further and try to decode entries in signons.sqlite. I've read these items are encrypted with 3DES ant next encoded to Base64 format but I suppose it a little bit more complicated... So I have a few questions.

1. If you read encrypted values from signons.sqlite and decode them from Base64 you would receive array of bytes with length is undivided by 8... As I know the length of encrypted data by 3DES (CBC) should by multiple of this value, but it's not. Probably something should be omitted, am I right?

2. I'm still wondering where private keys entries are located in key3.db file. I guess it should be from 0x00002F60 byte but it's still hard to me to clarify how the key value (and parameters like salt) should by obtained

Cheers

2: Read about the key3.db private keys here: http://www.drh-consultancy.demon.co.uk/key3.html
There is a section for them explaining how you can obtain them.
Or do you mean that this section doesn't explain it well enough?

1: The plaintext is padded with  1000… as far as I know
Sorry, I misread.
I don't know. I didn't try to decrypt them.
« Last Edit: May 07, 2013, 03:22:14 pm by Deque »

Offline rafX

  • NULL
  • Posts: 3
  • Cookies: 0
    • View Profile
Re: How Mozilla saves passwords
« Reply #8 on: May 07, 2013, 04:50:27 pm »
Yes, I've read this section before, but it doesn't explain me derivation key process well. My basic problem is to discover where should I started to read key's bytes from. In your case you had plain-text strings like ''password-check” or „global-salt”. In my case there is no information like that. Description provided on: drh.consultancy.demon.co.uk doesn't provide this guidance.

I've generated a few key3.db files and I noticed that starting from 0x00002F60 byte there is 16 probably constant bytes (I've no idea what does they means) and after them I obtained different bytes chain for different files – probably it's a key or its part, but I'm still not able to extract it propertly.

Offline Deque

  • P.I.N.N.
  • Global Moderator
  • Overlord
  • *
  • Posts: 1203
  • Cookies: 518
  • Programmer, Malware Analyst
    • View Profile
Re: How Mozilla saves passwords
« Reply #9 on: May 07, 2013, 05:10:22 pm »
http://www.drh-consultancy.demon.co.uk/key3.html says:

Quote
The private key entries has a database key that is part of the public key, for an RSA key it is the modulus, for a DSA key it is the public value

So what you are looking for is the public value.
I would search for this (google or in the source code of mozilla)

Edit: This should help: http://www.infond.fr/2010/04/firefox-passwords-management-leaks.html

Quote
How usernames and passwords are encrypted in Firefox?
 
 Edit following file written in javascript
 <blockquote>/toolkit/components/passwordmgr/src/storage-mozStorage.js</blockquote>The two encryption javascript functions are:
 <blockquote>_encrypt(PlainText)
 (...)
 PlainOctet = _utfConverter.ConvertFromUnicode(PlainText)
 CipherText = _decoderRing.encrytpString(PlainOctet)
 
 _decrypt(CipherText)
 (...)
 PlainOctet = _decoderRing.decryptString(CipherText)
 PlainText = _udtConverter.ConvertToUnicode(PlainOctet)</blockquote>

[...]

decrypt() calls three functions:
 <blockquote>PK11_GetInternalKeySlot()
 PK11_Authenticate()
 PK11SDR_Decrypt()</blockquote>
« Last Edit: May 07, 2013, 05:18:40 pm by Deque »

Offline rafX

  • NULL
  • Posts: 3
  • Cookies: 0
    • View Profile
Re: How Mozilla saves passwords
« Reply #10 on: May 08, 2013, 01:03:09 pm »
I've googled a lot but I can't find these information... Probably the only way to find out is to analyzing Mozilla source code . Anyway, thanks for answering :)

Offline tygahh

  • NULL
  • Posts: 3
  • Cookies: 0
    • View Profile
Re: How Mozilla saves passwords
« Reply #11 on: May 10, 2013, 12:36:06 pm »
you really did a winderful job here. keep it up. I'm a Java developer and ur coding skill is awesome

Offline agraj

  • /dev/null
  • *
  • Posts: 6
  • Cookies: 0
    • View Profile
Re: How Mozilla saves passwords
« Reply #12 on: May 14, 2013, 04:53:57 pm »
this is awesome. i respect u for the knowledge u shared. i will also share what i have.

Offline davaa

  • NULL
  • Posts: 3
  • Cookies: -9
    • View Profile
Re: How Mozilla saves passwords
« Reply #13 on: May 16, 2014, 10:51:28 am »
Nice info, Can u share some decrypting python code in win ?


Offline Kulverstukas

  • Administrator
  • Zeus
  • *
  • Posts: 6627
  • Cookies: 542
  • Fascist dictator
    • View Profile
    • My blog
Re: How Mozilla saves passwords
« Reply #14 on: May 16, 2014, 11:37:34 am »
Nice info, Can u share some decrypting python code in win ?
Java is pretty easy to port to Python, why don't you do it? and FYI python is cross-platform.