65.9K
CodeProject 正在变化。 阅读更多。
Home

Google Chrome 凭据的秘密

starIconstarIconstarIconstarIconstarIcon

5.00/5 (17投票s)

2017年1月30日

CPOL

6分钟阅读

viewsIcon

39450

downloadIcon

1106

Chrome 将所有密码和其他凭证存储在一个加密的数据库中,但您知道吗:任何拥有适当知识的人都可以检索它们。本文将向您展示如何做到这一点。

引言

本文是关于获取浏览器(及其他应用程序,例如:MS Outlook)存储的(已加密)凭证的系列文章之一。第一篇文章介绍了 Wi-Fi 凭证。本文介绍 Google Chrome 以及凭证的存储方式和获取方法。

配置文件文件夹

Chrome 将用户的凭证存储在一个名为“配置文件文件夹”的特殊文件夹中。第一步是了解在哪里可以找到该文件夹。为了更好地理解,请注意 Chrome 内置了一个显示其版本和安装位置重要信息的机制。要查看此信息,只需在地址栏中输入:“chrome://version/”。

在显示的众多信息中,请参阅“配置文件路径”,敏感数据就保存在那里。

要以编程方式获取此路径,我们需要执行以下操作:

#define FORENSICS_CHROMECREDENTIALS_PATH _T("\\Google\\Chrome\\User Data\\Default\\")

bool result = false;
TCHAR szProfileFolderPath[MAX_PATH];
result = SHGetSpecialFolderPath(0, szProfileFolderPath, CSIDL_LOCAL_APPDATA, 0);
StrCat(szProfileFolderPath, FORENSICS_CHROMECREDENTIALS_PATH);

首先,我们获取用户 Windows 用户帐户的用户特定路径。这可能是c:\users\john\c:\users\myself\,由于我们不知道它,所以不使用任何硬编码值,而是在运行时获取。

第二部分是 Google 数据的相对路径,这是固定的,因此我们将其部分硬编码。当我们结合这两部分时,我们就得到了路径。

现在我们有了路径,需要重点关注一个特定的文件,该文件实际上是一个 SQLite3 数据库:Login Data

要获取此文件,我们在路径后添加"\"_T("\\"),然后是字符串_T("Login Data"),这样,szProfileFolderPath 将保存我们需要打开的数据库的完整路径。

#define FORENSICS_CHROMECREDENTIALS_DB _T("\\Login Data")
StrCat(szProfileFolderPath, FORENSICS_CHROMECREDENTIALS_DB);

在打开此数据库之前,建议将其复制到另一个文件,以免干扰任何正在运行的 Google Chrome 实例,这样,就不需要关闭 Chrome 即可进行操作。

#define TEMP_CHROME_DB    _T("temp.db")
CopyFile(szProfileFolderPath, TEMP_CHROME_DB, FALSE);

关于 SQLite3 的一些说明

在 Secured Globe, Inc. 的日常开发工作中,我们经常使用 SQLite3 。大多数取证信息都以某种方式与 SQLite3 相关。Sqlite3 可以从源代码级别使用(将 sqlite3.csqlite3.h 添加到您的程序中,或者作为静态/动态库)。

我们使用它并进行了两个额外的增强:

  • CppSqlite3 - 一个包装器,可以确保更好地处理 UNICODE 测试(没有它,sqlite3 很难处理其数据库中的国际字符)。第一个版本已发布 在此处 Code Project。
  • SEE - 一个付费库(每许可证 2,000 美元)由 Sqlite3 出售,它允许应用程序读写加密的数据库文件。它支持四种不同的加密算法:
    • RC4
    • OFB 模式下的 AES-128
    • CCM 模式下的 AES-128
    • OFB 模式下的 AES-256

以下 文章 在帮助人们在程序中使用 Sqlite3,尤其是 Windows 程序方面非常有用。

从 SQLite3 数据库中提取凭证

接下来,我们打开数据库并在所需的存储凭证的特定表上运行查询。此表称为“logins”。我们预定义要在该表上执行的查询如下:

#define CHROME_CRED_SQL_QUERY "SELECT signon_realm,username_value,
password_value,date_created FROM logins"

然后,我们可以打开数据库并运行此查询:

CppSQLite3DB CredentialsDB; // we define a CppSQLite3DB object
try
{
    CredentialsDB.open(TEMP_CHROME_DB);
}
catch (CppSQLite3Exception &e)
{
    // Handle exceptions here
    return false;
}
CppSQLite3Query SqlQuery;
CString sql;
sql = CHROME_CRED_SQL_QUERY;
try
{
    SqlQuery = CredentialsDB.execQuery(sql);
}
catch (CppSQLite3Exception &e)
{
    // Handle query execution's exceptions here
    return 0;
}
int count = 0;
int size = 0;

SGBrowserCredentials 数据结构

SGBrowserCredentials(Secured Globe Browser Credentials)用于任何获取浏览器凭证的进程,并且能够保存所有浏览器通用的相关字段。

我们定义单个元素和一个 CSimpleArray,用于收集程序的结果。我们还使用 CTime 来处理每个条目的日期/时间戳。我们这样做是为了能够比较来自不同来源(浏览器)的凭证,因为每个浏览器存储日期和时间的方法都不同。日期/时间对我们程序的范围的重要性在于能够稍后使用它来过滤结果。例如:能够判断自 1.1.2017 以来或自上次检查以来创建了哪些新凭证。

typedef struct _SGBrowserCredentials
{
    int Browser; // 0 = chrome, 1 = ie, 2 = firefox, 
    TCHAR Site[256];
    TCHAR UserName[80];
    TCHAR Password[256];
    CTime DateCreated;
    _SGBrowserCredentials()
    {
        Site[0] = 0;
        UserName[0] = 0;
        Password[0] = 0;
        DateCreated = NULL;
    }
} SGBrowserCredentials;

typedef CSimpleArray<SGBrowserCredentials> SGBrowserCredentialsArray;

读取数据

运行 SQL 查询后,我们应该会得到一些记录。每条记录都是一个单独的条目,例如一个存储的凭证。我们使用以下局部变量:

WCHAR *Site, *User, *CreationDate;

DATA_BLOB DataIn, DataOut;

来存储单个记录。我们还读取加密数据,稍后将对其进行解密。

#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

然后,我们遍历结果的循环如下:

while (!SqlQuery.eof())
{
    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);
...

现在我们需要解密加密部分,只要您从数据库最初存储的位置运行程序,就可以顺利完成。原因在于加密基于 Windows 登录系统。如果您复制数据库并尝试从另一台机器打开它,您将无法解密密码,只能看到其他字段。

解密加密数据

要解密数据,请使用 CryptUnprotectData,它以DATA_BLOB结构解密密码。

bool bDecrypted = CryptUnprotectData(&DataIn, 0, 0, 0, 0, 8, &DataOut);

如果成功,我们将在DataOut中获得解密后的密码。

处理 Chrome 的日期/时间格式

在创建单个记录并将其添加到动态数组之前,我们需要解释 Chrome 如何存储每个条目的日期和时间。最简单的方法是将日期/时间(从数据库中作为string获取)转换为 FILETIME 对象。FILETIME结构包含一个 64 位值,表示自 1601 年 1 月 1 日(UTC)以来的 100 纳秒间隔数。首先,我们需要获取 Chrome 使用的长数字的前 10 位数字,并将其存储在 ULONGLONG 变量中(使用 _wcstoui64)。然后,我们将其乘以10,并将“LowPart”和“HighPart”分配给FILETIME对象。

ULONGLONG lltm = _wcstoui64(ChromeTime.GetString(), NULL, 10);
ULARGE_INTEGER uLarge;
uLarge.QuadPart = lltm * 10;
FILETIME ftTime;
ftTime.dwHighDateTime = uLarge.HighPart;
ftTime.dwLowDateTime = uLarge.LowPart;

From FILETIME we can always convert to SYSTEMTIME and to CTime easily.

程序

启动程序时,您将看到您计算机上存储的所有凭证。应该如下所示:

程序及任何更新将在 www.windowscredentials.comSource Forge 上提供。

关于源代码

源代码可以用于演示文章中描述的功能,以及一些 GUI(图形用户界面)方面。源代码项目是基于对话框的 MFC 项目,使用 Visual Studio Ultimate 2013 构建。应感谢 Marc Richarme,他创建了 EasySize。他的类用于支持在调整对话框大小时保持其中控件的相对位置。我使用了 TabCtrlSSL (感谢 Derek Lakin)以及我们所做的更改和增强。我致力于在网格中显示浏览器的图标(此处为 Chrome),同时以透明颜色显示它。在网格中(实际上是一个 CListCtrl)显示的所有图像都需要有一个共同的背景色(在本例中为绿色/RGB(0,255,0))并保存为 24 位 BMP 文件。这基本上就是技巧。您还可以从源代码中了解如何在显示标签和其他控件的同时,为整个对话框保持共同的背景色。

* 图形受 Secured Globe, Inc. 版权保护,不得用于其他目的/产品。

历史

  • 2017 年 1 月 30 日:初始版本
© . All rights reserved.