Has the user logged into this account, or not? (Google Chrome’s Login Data-Part 1)

Published Date : September 15, 2020 , atropos4n6

One of the most ubiquitous questions in DFIR, is the one that goes like “Has the user logged into this account with administrative privileges?”. This question has two parts. We will focus on the first one. One of the first places that I would normally dive into to determine if the user has logged into any account, would be Google Chrome’s “Login Data” database (if Google Chrome was present of course :P). I am sure that many DFIR forensicators out there, would do exactly the same. “Login Data” database, was my sure victory. I thought it was trivial to answer that question. I couldn’t be more wrong.

As you may have already experienced as a user, when you use Google Chrome to login to your account (any account you have), it prompts you with a message box, like the aforementioned one:

Image 1 “To remember me, or not?”

As you can see, you have three options, namely: 1) “Save” (a.k.a. Remember Me), 2) “Never” and 3) Click on the “x” button. Each one of them, informs Google Chrome to do something different. But also, each one of them, leaves a different trace inside “Login Data” database. We will explore all of these options in this research. The so-called “Login” feature of Google Chrome is extremely useful, as it offers to “remember” the user’s credentials, saving him the trouble of re-typing them, each time he wants to login to his account. But what happens, when the user enters invalid credentials? This question is the core of our today’s quest! Let’s start with our setup and our scenario.

Setup

All of my tests were run on a VM with Window 10. As far as the software I used, it is listed here:

  • VM OS: Microsoft Windows 10 Enterprise Evaluation 10.0.17763 N/A Build 17763 (I have downloaded Microsoft’s official VM for testing Microsoft Edge and IE. It is free and you have 90 days to use it how you want. Make a snapshot before activating Windows and then revert to it after the deadline). [Here is the link: https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/]
  • VM Software: I used Virtual Box 6.1.14.
  • I powered up the VM and then installed Google Chrome (Version 85.0.4183.102 (Official Build) (64-bit)).
  • After installing Google Chrome, I performed a serious of actions (Described in the Scenario section).
  • I then used DB Browser for SQLite (version 3.12.0) to view the contents of the “Login Data” database.

Scenario

As always the scenario was pretty basic. This time I tried:

  • To login successfully into my accounts (several well-known webpages were tested) and choose Options 1, 2 or 3 (See Image 1).
  • To login unsuccessfully into my accounts (several well-known webpages were tested) and choose Options 1, 2 or 3 (See Image 1).
  • A variation of the above.

Let’s see what did we find.

Findings-Table Stats

To start with, if you are trying to locate where Google Chrome store its files (files of forensic value), try looking at this path “C:\Users\\AppData\Local\Google\Chrome\User Data\Default“. In this directory, you will find (among other super important artifacts) a database (SQLite 3 format) named “Login Data”. This is the database we will analyze here.

This database has 2 tables that hold information which a DFIR forensicator might use (it has more tables but not all of them are forensically useful). The 1st one we will examine is named “stats“. The “stats” table has 4 columns as you can see below:

Image 2 database: Login Data table: stats

This table is populated when a user choose Option 3 at the Message-Box (See Image 1). Let’s see an example. I have put in my correct credentials and logged into my Netflix account. When the Message-Box appeared, I clicked on the “x” button. A entry created in the “stats” table like this:

Image 3 database: Login Data table: stats

In the “origin_domain” field, you can see Netflix domain. In the “username_value”, my username (the username associated with my account) is now populating this field. As it is the first time I clicked on the “x” button, “dismissal_count” is now 1. Lastly, the “update_time” holds the timestamp of my login action. Hmm, easy peasy so far. But, what do these columns store exactly and how (if at all) they get updated/increased? After some stress-testing, I was able to answer that question:

Column NameType of Information it Stores
origin_domainThe domain where the user entered his credentials and logged in (or more correctly, “tried” to login). If another user (different username) tries to login to the same domain, a separate entry will be created in the stats table.
username_valueThe username that the user entered, when he tried to login to the domain. Again, if another user (different username) tries to login to the same domain, a separate entry will be created in the stats table.
dismissal_countHow many times the user has clicked on the “x” (Option 3), when trying to login with the same username. If there is a 2nd time that the user tries to login with the same username and clicks on the “x” afterwards, the dismissal_count increases by one. However, it will not go higher than 3. If a user tries to login with the same username and clicks on the “x”, more than 3 times, the browser will no longer show the Message-Box and as a consequence this number, along with update_time do not get increased/updated.
update_timeThe last time the user has clicked on the “x” (Option 3), when logging with the same username. The time here is in “Google Chrome format” (You can use DCODE an open source tool to perform any conversions Link: https://www.digital-detective.net/dcode/). If the user tries to login again with the same username and clicks on the “x”, the update_time gets updated to the timestamp of that action. However, it will stop updating after the 3rd time the user tries to login with the same username and click on the “x”.

Some points to take into consideration when dealing with this table are:

  • An entry in the username_value field, doesn’t necessarily mean that the user has successfully logged into his account. He may have used wrong password and then click on the “x” (Option 3) and there would still be an entry with his username in this table (Trust me I have tried it!). What is more, he could have used a rogue username (imaginary or even worse another user’s username) along with false password and there could still be an entry with the username he used in this table.
  • As stated before, after the 3rd time a user enters the same username when trying to login to his account, and choose Option 3 (clicks on the “x”), Chrome will no longer show him the Message-Box and will no longer increase/update dismissal_count and update_time values, for the given username.

And with that being said, let’s move to the second table named “logins”.

Findings-Table Logins

With one option completely analyzed, there are 2 more to go. As you may know (or suspect), the other two options the user has when he tries to login to his account, are related to the table “logins“. This table has many columns. I didn’t manage to determine how each one of them operates, but I will try my best. Some of these columns are: “origin_url”,”action_url”,”username _element”,”username_value”,”password_element”, “password_value”,”signon_realm”,”date_created”,”blacklisted_by_user” ,”times_used”,”id”,”date_last_used”.

I will start with analyzing artifacts related to Option 2 and leave artifacts of Option 1 for last. If a user choose Option 2 “Never”, then a specific entry will populate “logins” table. Let’s see an example:

I logon successfully to my account in Outlook and when the Message-Box appeared, I chose Option 2 “Never”. Now if we open “Login Data” database, table “logins” will show:

As we can see, no password or at least username (as in “stats” table) populates the database. Instead, the only fields that get populated (protobufs are excluded here) are columns “signon_realm”,”date_created” and “blacklisted_by_user”, as seen below:

Well, you can easily tell what these columns store, but I will write it down anyways:

Column NameType of Information it Stores
origin_urlThe domain where the user tries to login.
signon_realmThe domain (or a webpage related to the login feature of that domain),
date_createdThe exact timestamp (also in Google Chrome format) when the user chose Option 2 “Never” at the Message-Box.
blacklisted_by_userTrue or False (1 or 0). Of course, if user choose Option 2, this value will be 1.

A couple of points to keep in mind here:

  • As before, If we find an entry in this table for a particular domain, it doesn’t necessarily means that a user successfully logon to this domain. It means that he tried to login at least (either successfully or not) and then hit the “Never” button.
  • If a user hits “Never” to the Message-Box, the domain gets blocklisted for the “Login” feature and from then on, each time the user tries to login (either successfully or not) to this domain (either with the same account or with any other), this Message-Box doesn’t appear again and as a result, his logins are never saved to “Login Data” database. What is more, no timestamp related to his logins is saved either. He may have tried to login (either successfully or not) several times, but none of them will populate the table “logins”. The only timestamp that is saved regarding this option, is the time of when the user first chose to blocklist the domain (the time when he hit “Never” button at the Message-Box).

Now that Option 2 is analyzed too, it’s time to get our hands on Option 1 “Save”. Better see how this works with an example. I logon to my Instagram account and chose Option 1 when the Message-Box appeared. As a result, this entry populated “logins” table and here is a glance in its contents:

Much more details than option 2, obviously. There are some columns that also store values in this scenario, but are not visible in the above screenshot. In general, when a user tries to login in his account and choose Option 1 “Save”, these details populate “logins” table:

Column NameType of Information it Stores
origin_urlThe domain where the user tries to login.
action_urlThe domain (or a webpage related to the login feature of that domain),
username_elementThis value is domain-specific and depends on how the domain has named “user input” field in its code. So we can see various values like “username”, or “email”, or “identifier”.
username_valueThe username of the user who tried to login to his account will be stored. Different usernames in the same domain, will create separate entries.
password_elementThis value is domain-specific and depends on how the domain has named “password input” field in its code. So we can see various values like “password”, or “pass”, or “session[password]”.
password_valueHere the password of the user who tried to login to his account will be stored. The password will not be in plain text but in blob format. You can use an open source tool to extract the plain text value of the password, name “Chrome Pass View” (Link:https://www.nirsoft.net/ utils/chromepass.html).
signon_realmThe domain (or a webpage related to the login feature of that domain),
date_createdThe exact timestamp (also in Google Chrome format) when the user chose Option 1 “Save” at the Message-Box.
blacklisted_by_userTrue or False (1 or 0). Of course, if user choose Option 1, this value will be 0.
idThe identifier of the specific entry in the “logins” table. Each increases the id by one (also starting from one).
date_last_usedThe last time when the user tried to auto-login (as his credentials are remembered) with this username to his account. Each time the tries to login with the same username and password, this timestamp gets updated.
times_usedI believed that this field would get updated each time the user tries to auto-login with a stored username and password. However, this is not the case. Sometimes, when I tried to login (either successfully or not) to my account (where I already had my credentials stored in this table), it would get updated, but some other times it wouldn’t. I suspect that it is domain specific somehow.

Some logical assumptions and key points to remember:

  • As before, If we find a username and password in this table for a particular domain, it doesn’t necessarily means that a user successfully logon to this domain. It means that he tried to login at least (either successfully or not) and then hit the Option 1 “Save” button.
  • If a user tries to login with the same username he previously used and a different password (e.g. a user tries to login with the correct username, but mistypes the password and accidentally hits “Save”, then goes back and retypes the correct password to gain access to his account), a new Message-Box appears, which prompts the user to “Update Password” if he wants to. If he choose to update his password, then date_created field will hold the timestamp of the first login attempt, whereas date_last_used will hold the timestamp he chose to update the password. On the contrary, if he chose either “No thanks” or clicks on the “x”, none of those timestamps will change (also, as far as clicking on the “x”, no entry will be created inside “stats” table).
  • If date_created is after date_last_used (usually some seconds apart) then we can make the assumption that the user has only tried once to login to his account. You may ask yourself how is this possible? Well, when you try to login to your account the Message-Box appears, but you do not press Option 1 “Save” instantly. So what is happening in the background is that date_last_used time gets updated, as soon as the user hits the Login button, whereas date_created time gets created when the user hits “Save” button. You first login and the hit the “Save” button. That’s why we can make the above-mentioned assumption. If date_created is before date_last_used, then we can tell for sure, that the user has tried again to auto-login to his account on this specific domain.

We finished analyzing all three Options. We have proven that these entries in “Login Data” do not mean necessarily that the user has successfully logon to his account. The user may have used illicit or false credentials and he wouldn’t have gotten access to this account, but still entries would be found inside this database. This may lead investigators to make wrong assumptions, as to if the user has successfully logon. However, everything is not lost and we can still prove in some cases that the user has successfully logon to his account, using the “Login Data” database. Let’s see when this happens.

Findings– Everything is not lost!

During my tests, I visited well-known webpages and tried to login to my accounts (both successfully and not), in order to check whether or not “Login Data” database can help determine that the user has logon to his account. What I found out is that entries inside “Login Data” database can still prove that a user has logged in to his account. It basically depends solely on the webpage’s code implementation. Using false credentials, some webpages returned a response from the server that triggered Message-Box to appear and therefore leaded entries to be created and false assumptions to be made, while others didn’t returned that response and prevented Message-Box from appearing.

To sum up, I will write down which domains do not trigger Message-Box when trying to login with false credentials. Therefore, when you find entries inside the “logins” table or even “stats” table for these domains, you can safely make the assumption that the user logged in successfully to his account. On the other hand, I will write which domains still trigger Message-Box even when a user tries to login with false credentials to his account. Therefore, if you see these domains in the following tables, you should avoid make the assumption that the user logged in to his account successfully.

Domains that are safe to make assumptions for user’s successful logon (do not trigger Message-Box when using false creds)
https://www.facebook.com/
https://www.instagram.com/
https://www.login.live.com/
Domains that are NOT safe to make assumptions for user’s successful logon (trigger Message-Box when using false creds)
https://twitter.com/login
https://accounts.google.com/signin/v2/challenge/pwd

Keep in mind that not only “Login Data” database can help you with determining user successful logon to his account. Cookies play a big role too. For example, I logged in my Dominos Pizza account (got hungry after this research) and chose Option 2 “Never” at Message-Box. I closed the browser and then re-opened it and visited dominos.com again. Guess what? I was already logged in and ready to go. How can that be? Well, when I entered my credentials, I forgot checked the check-box “Remember Me” underneath. This let dominos create a beautiful cookie, which would have me authenticated for some time. But demystifying Chrome Cookies, will have to wait for some time.

To close this huge post, I would like to underline what takeouts should someone take from this post, about Chrome Forensics and more particularly “Login Data” database:

  • First of all, entries inside table “logins” and table “stats” do not necessarily mean that a user has successfully logon to his account. It may indicate that, but it could not as well (as we said it depends on the domain).
  • Secondly, if you find yourself in a dilemma when trying to determine if a specific domain (not listed above) triggers or not the Message-Box when false credentials are received, then just visit that domain with Google Chrome and try false credentials and see for yourself how this domain reacts to that.
  • Even the most trivial question, can have its own peculiarities.

I hope you liked this post. I enjoyed myself during this research. I learned a lot. Even if forensic analysis for this browser, is not something new for the DFIR community. If you liked this post, do not forget to share it with others who might find it useful. We covered a lot in this post. But there is more there of course, as always. For example, what happens to the “Login Data” database, when a user is logged in Google Chrome? Stay tuned to find out! DFIRing is fun!

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.