Jump to content
Nytro

Decrypt stored passwords from Firefox, Chrome and Internet Explorer with C#

Recommended Posts

Posted

Decrypt stored passwords from Firefox, Chrome and Internet Explorer with C# and .NET

Mladen Stanisic,

28 Dec 2014 CPOL

How to retrieve passwords stored in Firefox, Chrome and Internet Explorer programmatically in C# Introduction

Modern internet browsers offer user to save user name/password while logging to sites. Some third party applications can be used to extract this information. These applications are written in C++, and the source code I ran into while searching for answer on how it's done is mostly in C++. Although I searched thoroughly for the answer on how to do this in C#, I didn't find complete solution for all the three. This article explains how to do this programmaticaly in C# with .NET 2.0 and for all three most popular browsers: Firefox, Chrome and IE.

Background

The way passwords are stored in browsers is thoroughly explained by Jordan Wright's excellent article in his blog on Raider Security which can be found here. His article also provide many usefull links for further insights. Especially useful are articles on password security SecurityXploaded site. For the purpose of this article it is enough to say that Google Chrome stores passwords in a file Login Data in %LOCALAPPDATA%\Google\Chrome\User Data\Default\ folder which is basically an SQLite database; Firefox stores login data in %APPDATA%\Mozilla\Firefox\Profiles\{profile name}\ folder in logins.json file, and older versions used to store data in signons.sqlite file, which is SQLite database and is not used anymore. Internet Explorer store its autocomplete passwords in Registry, in HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\IntelliForms\Storage2 key.

Articol complet: Decrypt stored passwords from Firefox, Chrome and Internet Explorer with C# and .NET - CodeProject

Posted (edited)

Tare: https://web.archive.org/web/20150109073357/http://www.codeproject.com/Articles/857320/Decrypt-Stored-Passwords-from-Firefox-Chrome-and-I

 

Si pt posteritate:

  
 
 
DEC JAN JUN
Previous capture 09 Next capture
2014 2015 2016
15 captures
31 Dec 2014 - 26 Jul 2017

Click here to Skip to main content

  Home
11,138,654 members (69,146 online)
 
 
 
 
 
     
Click here to Skip to main content
 
 
 
 

Decrypt Stored Passwords from Firefox, Chrome and Internet Explorer with C# and .NET

%7B5ac0d075-c758-48e0-8a8c-e6cd15022974%7D.jpg
Mladen Stanisic, 27 Dec 2014 CPOL
stars-fill-lg.png
stars-empty-lg.png
   4.98 (33 votes)
 
Rate this:  
 
 
How to retrieve passwords stored in Firefox, Chrome and Internet Explorer programmatically in C#

Introduction

Modern internet browsers offer user to save user name/password while logging to sites. Some third party applications can be used to extract this information. These applications are written in C++, and the source code I ran into while searching for an answer on how it's done is mostly in C++. Although I searched thoroughly for the answer on how to do this in C#, I didn't find a complete solution for all the three. This article explains how to do this programmaticaly in C# with .NET 2.0 and for all three most popular browsers: Firefox, Chrome and Internet Explorer.

Background

The way passwords are stored in browsers is thoroughly explained by Jordan Wright in his excellent article on RaiderSecurity blog which can be found here. His article also provides many useful links for further insights. Especially useful are articles on password security SecurityXploaded site. For the purpose of this article, it is enough to say that Google Chrome stores passwords in a file Login Data in %LOCALAPPDATA%\Google\Chrome\User Data\Default\ folder which is basically an SQLite database; Firefox stores login data in %APPDATA%\Mozilla\Firefox\Profiles\{profile name}\ folder in JSON logins.json file, and older versions used to store data in signons.sqlite file, which is SQLite database and is not used anymore. Internet Explorer stores its autocomplete passwords in Registry, in HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\IntelliForms\Storage2 key.

Using the Code

Single user login consists of the following information: site/url visited, user name and password. This information is retrieved from browser database (registry in the case of Internet Explorer) by static LoginReader class. Encrypted information about user name and password are decrypted by methods in large CryptoAPI class, and finally stored in LoginInfo class, where they can be accessed though properties UserName, Password, and Url.

minus.gif Collapse | Copy Code
public class LoginInfo
{
    public string Url { get; set }
    public string Password { get; set; }
    public string UserName { get; set; }    
    public string Browser { get; set; }
}

To retrieve login information for the specified browser, we call static methods in LoginReader class. LoginReader class has three static methods (for each of the supported browsers); each of them will return a collection of LoginInfo objects which can be added to ListView, or displayed in console (like in this demo project, it offers some handy overloads of ToString() method for simplicity).

Demo project comes with precompiled Decryptor.dll. To use classes in your project, you can add reference to this DLL, and include Newtonsoft.Json.dll and sqlite_x86.dll which are located in \bin folder of the demo to your project. Then, to retrieve login data, you would write:

minus.gif Collapse | Copy Code
using PassDecrypt;

...

List<LoginInfo> logins = new List<LoginInfo>();

logins.AddRange(LoginReader.GetLoginsFirefox());
logins.AddRange(LoginReader.GetLoginsChrome());
logins.AddRange(LoginReader.GetLoginsIE());

The rest is just iterating through a list and displaying the contents of each LoginInfo object:

minus.gif Collapse | Copy Code
foreach (LoginInfo login in logins)
{
    Console.WriteLine(login);

    // or access individual properties like this:
    //
    //Console.WriteLine("Browser: \"{0}\":: URL: {1}, UserName: {2}, Password: {3}.",
    //    login.Browser, login.Url, login.UserName, login.Password);
}

Helper Classes

For the demo project, I used a number of helper classes to retrieve entries from SQLite databases, Explorer history and JSON database of Firefox.

For reading SQLite databases, I used heavily modified SQLWrapper class which I found here. The class still holds the name SQLiteWrapper, but now includes support for sequential reading by DataReader class, reading of BLOB objects, and support for 64-bit sqlite.dll. Basically, this is a native wrapper around sqlite.dll which can be downloaded here. I modified this simple wrapper for the code in the demo project to be 100 % compatible with the code you would write for the ADO.NET SQLite wrapper. So you may also reference System.Data.SQLite and use it instead if you prefer so. But this wrapper is light-weight and can be compiled for AnyCPU or x64 target without concern about which dlls to include in project and distribute to end users. All that needs to be included in the project are two sqlite.dlls, one compiled for 32 bit platform which has to be renamed to sqlite_x86.dll and the other compiled for 64 bit platform which has to be renamed to sqlite_x64.dll both are available for download from SQLite download page. Demo project is compiled for x86 platform, otherwise calling decryption functions would not be possible through delegates obtained by GetProcAddress() (64-bit code is not compatible with 32-bit libraries Firefox uses) so sqlite_x64.dll is not included in the demo.

For reading JSON database stored in logins.json, I used Newtonsoft.Json wrapper for .NET. It is available as NuGet package. To read json into C# object, I have written FFLogins class.

Finally, for reading Internet Explorer history, I used slightly modified version of The Tiny Wrapper Class for URL History Interface in C#.

Decrypting the Passwords

Google Chrome

This one is the most simple to acquire. Because Google Chrome store passwords as BLOB objects encrypted in the context of current user using the Win32 API CryptProtectData() function without additional entropy, it can be easily decrypted using CryptUnprotectData(). Both functions have a handy .NET wrapper in static ProtectData class of System.Security.Cryptography namespace. When the password blob object is retrieved from database as byte[] the rest is just a call to ProtectData.Unprotect() and converting resulting decrypted byte[] to string:

minus.gif Collapse | Copy Code
using System.Security.Cryptography;
public static string Decrypt(byte[] blob)
{
    byte[] decryptedBytes = ProtectedData.Unprotect(blob, null, DataProtectionScope.CurrentUser);
    return Encoding.UTF8.GetString(decryptedBytes);
}

Mozilla Firefox

For this, I used a solution readily available in C# which I found here, but I have updated it to work with the most recent version of Firefox and repacked into FFDecryptor class. This is wrapper around functions which Firefox use to store and retrieve passwords, and which are exported from nss3.dll. There are no wrappers for these in .NET so interop with P/Invoke has to be utilised. Functions have the following signatures:

minus.gif Collapse | Copy Code
long NSS_Init(string configdir);
int PK11SDR_Decrypt(ref TSECItem data, ref TSECItem result, int cx);
long PK11_GetInternalKeySlot();
long PK11_Authenticate(long slot, bool loadCerts, long wincx);
int NSSBase64_DecodeBuffer(IntPtr arenaOpt, IntPtr outItemOpt, StringBuilder inStr, int inLen);

and structure TSECItem referenced in PK11SDR_Decrypt has the following declaration:

minus.gif Collapse | Copy Code
private struct TSECItem
{
    public int SECItemType;
    public int SECItemData;
    public int SECItemLen;
}

To aquire these functions, we load nss3.dll (which has the following dependencies: msvcr100.dll, msvcp100.dll, mozglue.dll) by using LoadLibrary from Win32 API (kernel32.dll), and initialize decrypter by calling NSS_Init. All of this is done in Init() function of FFDecryptor class. Folder which contains the database, %APPDATA% profile folder, is passed as a parameter.

minus.gif Collapse | Copy Code
public static void Init(string configDir)
{
    string mozillaPath = Environment.GetEnvironmentVariable("ProgramFiles(x86)") + @"\Mozilla Firefox\";

    if (!Directory.Exists(mozillaPath))
        mozillaPath = Environment.GetFolderPath
        (Environment.SpecialFolder.ProgramFiles) + @"\Mozilla Firefox\";

    if (!Directory.Exists(mozillaPath))
        throw new ArgumentException
        ("Mozilla Firefox install folder cannot be found.");
    LoadLibrary(mozillaPath + "msvcr100.dll");
    LoadLibrary(mozillaPath + "msvcp100.dll");
    LoadLibrary(mozillaPath + "mozglue.dll");
    _nss3DllPtr = LoadLibrary(mozillaPath + "nss3.dll");
    IntPtr pProc = GetProcAddress(_nss3DllPtr, "NSS_Init");
    NSS_InitPtr NSS_Init = 
    (NSS_InitPtr)Marshal.GetDelegateForFunctionPointer(pProc, typeof(NSS_InitPtr));
    NSS_Init(configDir);

    long keySlot = PK11_GetInternalKeySlot();
    PK11_Authenticate(keySlot, true, 0);
}

User name and password are stored as an encrypted string. To decrypt it, the following function from static FFDecryptor class is used:

minus.gif Collapse | Copy Code
public static string Decrypt(string cypherText)
{
    StringBuilder sb = new StringBuilder(cypherText);
    int hi2 = NSSBase64_DecodeBuffer(IntPtr.Zero, IntPtr.Zero, sb, sb.Length);
    TSECItem tSecDec = new TSECItem();
    TSECItem item = (TSECItem)Marshal.PtrToStructure(new IntPtr(hi2), typeof(TSECItem));
    if (PK11SDR_Decrypt(ref item, ref tSecDec, 0) == 0)
    {
        if (tSecDec.SECItemLen != 0)
        {
            byte[] bvRet = new byte[tSecDec.SECItemLen];
            Marshal.Copy(new IntPtr(tSecDec.SECItemData), bvRet, 0, tSecDec.SECItemLen);
            return Encoding.ASCII.GetString(bvRet);
        }
    }
    return null;
}   

Internet Explorer

The most difficult to obtain are passwords from Internet Explorer, and not just because of the lack of decent interop routines. User name and password in Internet Explorer are stored in a single string. This string is packed into a byte array along with a couple of headers which contain information about size and offsets of each "secret" (username or password is consider a "secret" in a context of Internet Explorer depcryption). This byte array is then encrypted by using a CryptProtectData() with URL string (including trailing'\0' character!) as an additional entrophy parameter. It is then stored in registry as a value under the name which is constructed from the same URL used for encryption. This URL is hashed with SHA1 encryption to 21 byte array, and converted to string of 42 hexadecimal digits. As mentioned above, the value is stored in HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\IntelliForms\Storage2 key. To retrieve user name and password, first we iterate through all items in history, converting URL to hash and comparing it with the value name in registry key. If the match is found, than we decrypt the value (byte array) by using URL as additional entropy in function call CryptUnprotectData(). This is easy since the wrapper is available through ProtectData.Unprotect() method. The hard part is extracting information from resulting byte[]. Layout of structures used to store information is given in this article, but the provided source code is written in C++ and heavily uses pointer arithmetics to reconstruct structures from data buffer. Since using of unsafe code in C# is discouraged, I have used System.Buffer class (System.IO.MemoryStream can also be used successfully, but with Buffer the same effect can be accomplished with less lines of code) and a very handy byte[] to struct converter I have found here. The code is given below:

minus.gif Collapse | Copy Code
static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    T stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    handle.Free();
    return stuff;
}

To convert URL from history to SHA1 hash, I had to write an interop for the existing code in C++ taken from SecurityXploaded site. Here is the function in C#:

minus.gif Collapse | Copy Code
static string GetURLHashString(string wstrURL)
{
    IntPtr hProv = IntPtr.Zero;
    IntPtr hHash = IntPtr.Zero;

    CryptAcquireContext
    (out hProv, String.Empty, string.Empty, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);

    if (!CryptCreateHash(hProv, ALG_ID.CALG_SHA1, IntPtr.Zero, 0, ref hHash))
        throw new Win32Exception(Marshal.GetLastWin32Error());

    byte[] bytesToCrypt = Encoding.Unicode.GetBytes(wstrURL);

    StringBuilder urlHash = new StringBuilder(42);
    if (CryptHashData(hHash, bytesToCrypt, (wstrURL.Length + 1) * 2, 0))
    {
        // retrieve 20 bytes of hash value
        uint dwHashLen = 20;
        byte[] buffer = new byte[dwHashLen];

        //Get the hash value now...
        if (!CryptGetHashParam(hHash, HashParameters.HP_HASHVAL, buffer, ref dwHashLen, 0))
            throw new Win32Exception(Marshal.GetLastWin32Error());

        //Convert the 20 byte hash value to hexadecimal string format...
        byte tail = 0; // used to calculate value for the last 2 bytes
        urlHash.Length = 0;
        for (int i = 0; i < dwHashLen; ++i)
        {
            byte c = buffer[i];
            tail += c;
            urlHash.AppendFormat("{0:X2}", c);
        }
        urlHash.AppendFormat("{0:X2}", tail);

        CryptDestroyHash(hHash);
    }
    CryptReleaseContext(hProv, 0);

    return urlHash.ToString();
}

And finally, the function to retrieve user name and password from Internet Explorer goes as follows:

minus.gif Collapse | Copy Code
public static bool DecryptIePassword(string url, List<string[]> dataList)
{
    //Get the hash for the passed URL
    string urlHash = GetURLHashString(url);

    //Check if this hash matches with stored hash in registry
    if (!DoesURLMatchWithHash(urlHash))
        return false;

    //Now retrieve the encrypted credentials for this registry hash entry....
    RegistryKey key = Registry.CurrentUser.OpenSubKey(KeyStr);
    if (key == null)
        return false;

    //Retrieve encrypted data for this website hash...
    //First get the value...
    byte[] cypherBytes = (byte[])key.GetValue(urlHash);
    key.Close();

    // to use URL as optional entropy we must include trailing null character
    byte[] optionalEntropy = new byte[2 * (url.Length + 1)];
    Buffer.BlockCopy(url.ToCharArray(), 0, optionalEntropy, 0, url.Length * 2);

    //Now decrypt the Autocomplete credentials....
    byte[] decryptedBytes = ProtectedData.Unprotect(cypherBytes, optionalEntropy, DataProtectionScope.CurrentUser);

    var ieAutoHeader = ByteArrayToStructure<IEAutoComplteSecretHeader>(decryptedBytes);

    //check if the data contains enough length....
    if (decryptedBytes.Length >= (ieAutoHeader.dwSize + ieAutoHeader.dwSecretInfoSize + ieAutoHeader.dwSecretSize))
    {
        //Get the total number of secret entries (username & password) for the site...
        // user name and passwords are accounted as separate secrets, but will be threated in pairs here.
        uint dwTotalSecrets = ieAutoHeader.IESecretHeader.dwTotalSecrets / 2;

        int sizeOfSecretEntry = Marshal.SizeOf(typeof(SecretEntry));
        byte[] secretsBuffer = new byte[ieAutoHeader.dwSecretSize];
        int offset = (int) (ieAutoHeader.dwSize + ieAutoHeader.dwSecretInfoSize);
        Buffer.BlockCopy(decryptedBytes, offset, secretsBuffer, 0, secretsBuffer.Length);

        if (dataList == null)
            dataList = new List<string[]>();
        else
            dataList.Clear();

        offset = Marshal.SizeOf(ieAutoHeader);
        // Each time process 2 secret entries for username & password
        for (int i = 0; i < dwTotalSecrets; i++)
        {
            byte[] secEntryBuffer = new byte[sizeOfSecretEntry];
            Buffer.BlockCopy(decryptedBytes, offset, secEntryBuffer, 0, secEntryBuffer.Length);

            SecretEntry secEntry = ByteArrayToStructure<SecretEntry>(secEntryBuffer);

            string[] dataTriplet = new string[3]; // store data such as url, username & password for each secret

            byte[] secret1 = new byte[secEntry.dwLength * 2];
            Buffer.BlockCopy(secretsBuffer, (int)secEntry.dwOffset, secret1, 0, secret1.Length);

            dataTriplet[0] = Encoding.Unicode.GetString(secret1);

            // read another secret entry
            offset += sizeOfSecretEntry;
            Buffer.BlockCopy(decryptedBytes, offset, secEntryBuffer, 0, secEntryBuffer.Length);
            secEntry = ByteArrayToStructure<SecretEntry>(secEntryBuffer);

            byte[] secret2 = new byte[secEntry.dwLength * 2]; //Get the next secret's offset i.e password
            Buffer.BlockCopy(secretsBuffer, (int) secEntry.dwOffset, secret2, 0, secret2.Length);

            dataTriplet[1] = Encoding.Unicode.GetString(secret2);

            dataTriplet[2] = urlHash;
            //move to next entry
            dataList.Add(dataTriplet);
            offset += sizeOfSecretEntry; // advanced offset pointer to next secret
        }
    }
    return true;
} //End of function

Points of Interest

Retrieval of passwords for Firefox and Internet Explorer requires use of P/Invoke. The code in the demo project for this article demontrates how to use delegates to call functions from run-time loaded dlls obtained with LoadLibrary() and GetProcAddress() API functions. It further demonstrates how to use System.Buffer and Marshal classes to perform interop between managed and native structures, and how to construct structures from byte arrays without use of unsafe code and pointer arithmetics. It also gives an insight to a bit of cryptography by demonstrating how to use data decryption function in System.Security.Cryptography.ProtectData class.

History

  • 27.12.2014: Initial post
  • 07.01.2015: Updated code to fix bugs in DecryptIePassword()
 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

  • EMAIL
  •  
  •  
  •  
  •  
  •  
  •  

About the Author

%7B5ac0d075-c758-48e0-8a8c-e6cd15022974%7D.jpg
Mladen Stanisic
Software Developer
Serbia Serbia
I was a hobbyist programmer writing in C++ for seven years. Starting from summer 2014 I am professional C# developer working on desktop applications using Windows Forms.
 
 
 
 

Comments and Discussions

 
    First Prev Next
t.gif
Question IE Problem - "Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2" does not exist Pin member Graeme_Grant 10hrs 10mins ago 
Answer Re: IE Problem - "Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2" does not exist Pin member Mladen Stanisic 8hrs 40mins ago 
General My vote of 5 Pin member raja mohan 7-Jan-15 3:50 
Question IE Error Pin member Burhan Eyimaya 6-Jan-15 4:56 
Answer Re: IE Error [modified] Pin member Mladen Stanisic 7-Jan-15 12:03 
General Re: IE Error Pin member Burhan Eyimaya 7-Jan-15 20:29 
General My vote of 5 Pin member Oshtri Deka 30-Dec-14 6:52 
Question Chrome DB Pin member angelbroz85 29-Dec-14 14:26 
Answer Re: Chrome DB Pin member Mladen Stanisic 30-Dec-14 6:49 
General Re: Chrome DB Pin member angelbroz85 30-Dec-14 6:57 
Question Is there a source code download for this article? Pin member Martin Hart Turner 29-Dec-14 2:58 
Answer Re: Is there a source code download for this article? [modified] Pin member Mladen Stanisic 29-Dec-14 9:51 
General Re: Is there a source code download for this article? Pin member Martin Hart Turner 29-Dec-14 21:40 
General My vote of 5 Pin professional PapyRef 29-Dec-14 2:17 
General My vote of 5 Pin member Humayun Kabir Mamun 29-Dec-14 1:44 
Bug Slight Error [modified] Pin member ledtech3 28-Dec-14 14:09 
General Re: Slight Error [modified] Pin member Mladen Stanisic 29-Dec-14 10:15 
General Re: Slight Error Pin member ledtech3 29-Dec-14 12:45 
General My vote of 5 Pin member m-mousavi 27-Dec-14 23:59 
General My vote of 5 Pin member Joe Gakenheimer 27-Dec-14 20:26 
t.gif
Last Visit: 31-Dec-99 19:00     Last Update: 8-Jan-15 16:33 Refresh 1

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Info
First Posted  27 Dec 2014
Views  9,737
Downloads  230
Bookmarked  44 times
 
 
Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141223.1 | Last Updated 28 Dec 2014
 
Article Copyright 2014 by Mladen Stanisic
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid

DFPAudiencePixel;ord='635563676382695886';dc_seg=33704410

Edited by gigiRoman

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...