This article covers Google Chrome and how credentials are stored and how they can be fetched from it.
This article is one of several articles covering the secrets of obtaining stored (and encrypted) credentials stored by browsers (and other applications, for example: MS Outlook). The first article covered Wi-Fi credentials. This article covers Google Chrome and how credentials are stored and can be fetched from it.
The Profile Folder
Chrome stores the user’s credentials inside a special folder called the “profile folder”. The first step would be understanding where to find that folder. To understand better, note that Chrome has a built in mechanism for displaying important information about its version and installation location. To view this information, just type: "chrome://version/" in the Address Bar.
Among the many pieces of information displayed, please refer to "Profile Path", which is where the sensitive data is kept.
To get this path programmatically, we need to do as follows:
#define FORENSICS_CHROMECREDENTIALS_PATH _T("\\Google\\Chrome\\User Data\\Default\\")
bool result = false;
result = SHGetSpecialFolderPath(0, szProfileFolderPath, CSIDL_LOCAL_APPDATA, 0);
First, we obtained the user specific path of his or her Windows' user account. That can be c:\users\john\ or c:\users\myself\, and since we don't know it, we don't use any hardcoded value but obtain it during runtime.
The second part is the relative path of the Google data, which is constant so we do keep that part hardcoded. When we combine the two, we get the path.
Now that we have the path, we need to focus in one specific file which is in fact a SQLite3 database: Login Data.
To get that file, we add "\" (
_T("\\") to the path and then the string
_T("Login Data") and when we do,
szProfileFolderPath will hold the full path of the database we need to open.
#define FORENSICS_CHROMECREDENTIALS_DB _T("\\Login Data")
Before we open this database, it is advised to copy it to another file so it won't intervene with any instance of Google Chrome currently opened, and by doing so, there is no need to shut down Chrome before we operate.
#define TEMP_CHROME_DB _T("temp.db")
CopyFile(szProfileFolderPath, TEMP_CHROME_DB, FALSE);
Some Notes about SQLite3
During the day to day development work at Secured Globe, Inc. we use SQLite3 a lot. Most of the forensics information out there is associated one way or another with SQLite3. Sqlite3 can be used at the source code level (adding sqlite3.c and sqlite3.h to your program, or as a static / dynamic library).
We use it with two additional enhancements:
- CppSqlite3 - a wrapper which can ensure better handling with UNICODE test (without it, sqlite3 can hardly deal with international characters within its databases). The first versions were published here at Code Project.
- SEE - a paid library ($2,000 per license) sold by Sqlite3 which allows an application to read and write encrypted database files. Four different encryption algorithms are supported:
- AES-128 in OFB mode
- AES-128 in CCM mode
- AES-256 in OFB mode
The following article is very handy when it comes to helping people using Sqlite3 in their programs, and especially Windows programs.
Fetching the Credentials from the SQLite3 Database
Next, we open the database and run a query on the specific table we need for the stored credentials. This table is called "
logins". We predefine the query to be executed on this table as follows:
#define CHROME_CRED_SQL_QUERY "SELECT signon_realm,username_value,
password_value,date_created FROM logins"
Then, we can open the database and run this query:
catch (CppSQLite3Exception &e)
sql = CHROME_CRED_SQL_QUERY;
SqlQuery = CredentialsDB.execQuery(sql);
catch (CppSQLite3Exception &e)
int count = 0;
int size = 0;
The SGBrowserCredentials Data Structure
SGBrowserCredentials (Secured Globe Browser Credentials) is used for any process of fetching browser credentials and is capable of holding the relevant fields which are common to all browsers.
We define the single element and a CSimpleArray for the purpose of collecting the results of our program. We also use CTime for the date/time stamp of each entry. We do so to be able to compare credentials from different sources (browsers), as each browser use a different method for storing date and time. The importance of date/time for the scope of our program is to be able to use it later for filtering the results. For example: being able to tell which new credentials were created since 1.1.2017 or since the last time we checked.
typedef struct _SGBrowserCredentials
Site = 0;
UserName = 0;
Password = 0;
DateCreated = NULL;
typedef CSimpleArray<SGBrowserCredentials> SGBrowserCredentialsArray;
Reading the Data
After running the SQL query, we should expect to get a number of records. Each record is a single entry such as a stored credentials. We use the following local variables:
WCHAR *Site, *User, *CreationDate;
DATA_BLOB DataIn, DataOut;
to store a single record. We read also encrypted data which we will decrypt later.
#define CHROME_DB_FIELD_SITE 0
#define CHROME_DB_FIELD_USER 1
#define CHROME_DB_ENC_FIELD_PASS 2
#define CHROME_DB_FIELD_DATE 3
Then the loop we run over the results looks like this:
Site = (WCHAR *)SqlQuery.fieldValue(CHROME_DB_FIELD_SITE);
User = (WCHAR *)SqlQuery.fieldValue(CHROME_DB_FIELD_USER);
DataIn.pbData = (LPBYTE)SqlQuery.getBlobField(CHROME_DB_ENC_FIELD_PASS, size);
DataIn.cbData = size;
CreationDate = (WCHAR *)SqlQuery.fieldValue(CHROME_DB_FIELD_DATE);
Now we need to decrypt the encrypted part and that can be done smoothly as long as you run the program from where the database was originally stored. The reason for that is that the encryption is based on the Windows logon system. If you copy the database and try to open it from another machine, you won't be able to decrypt the password but just to see the other fields.
Decrypting Encrypted Data
To decrypt the data, use CryptUnprotectData which decrypts password (in a
bool bDecrypted = CryptUnprotectData(&DataIn, 0, 0, 0, 0, 8, &DataOut);
If it has succeed, we will have the decrypted password in
Handling Chrome's Date/Time Format
Before we create the single record and add it to our dynamic array, we need to interpret the way date and time of each entry are kept by Chrome. It would be easiest to convert the date/time (fetched as
string from the database) into a FILETIME object. The
FILETIME structure contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC). First, we need to grab the first 10 digits of the long number used by Chrome and store it in a ULONGLONG variable (using _wcstoui64). Then we multiple it by
10 and assign the "
LowPart" and "
HighPart" to the
ULONGLONG lltm = _wcstoui64(ChromeTime.GetString(), NULL, 10);
uLarge.QuadPart = lltm * 10;
ftTime.dwHighDateTime = uLarge.HighPart;
ftTime.dwLowDateTime = uLarge.LowPart;
From FILETIME we can always convert to SYSTEMTIME and to CTime easily.
When you start the program, you will see all stored credentials on your machine. It should look like this:
The program and any updates will be available at www.windowscredentials.com and at Source Forge.
About the Source Code
The source code can be useful for demonstrating not only the functionality described in the article, but some GUI (Graphical User Interface) aspects. The source code project is a Dialog based MFC one, built using Visual Studio Ultimate 2013. Word of thanks should go to Marc Richarme who created EasySize. His class is used for supporting resizing the dialog while preserving the relative position of the controls it contains. I have used TabCtrlSSL (thanks Derek Lakin) with changes and enhancements we have made. I worked on displaying the browser's icon (Chrome, in this case) in the grid, while displaying it with a transparent color. All images to be shown transparent in the grid (which is in fact a CListCtrl) need to have a common background color (Green / RGB(0,255,0) in my case) and should be saved as a 24 bit BMP file. That's basically the trick. You may also learn from the source code about displaying labels and other controls whilst keeping a common background color to the entire dialog.
* The graphics are copyrighted to Secured Globe, Inc. and should not be used for other purposes / products.
- 30th January, 2017: Initial version