Password Algorithms: Internet Explorer 7, 8, 9

PasswordAlgorithms: Internet Explorer 7, 8, 9

IE7、8、9中的密码算法

                                               原文链接:Password Algorithms: Internet Explorer 7, 8, 9


一,介绍

在Windows8系统下,IE10 采用了不同于之前的加密算法和存放方案,所以我可能会在以后单独分析它。现在我将分析Windows7下9.0.9版本的IE

这里的一切都与传统的IE7,8完全一致。

考虑到客户可能会避免将系统升级到Windows8,所以我认为这种保护措施还是值得细细讨论。


二,存储

用户所有的自动完成表单都存放在NTUSER.DAT文件中,并且这个文件是由SHA-1散列和DPAPI的大二进制对象(BLOB)组成。

下面是我系统的一些hash散列

C:\>regquery "HKCU\Software\Microsoft\InternetExplorer\IntelliForms\Storage2"

HKEY_CURRENT_USER\Software\Microsoft\InternetExplorer\IntelliForms\Storage2

    6FBD22A243E7F5A0D660199683F52543E80CEB99EC    REG_BINARY    01000000D08C9DDF0115D1118. . .

   DF11F9BE8F0049A2FBFF29C6D49FE77383C2A6783A    REG_BINARY    01000000D08C9DDF0115D1118. . .

   E4CE6B2B79515319A7360D97E3B217F2FC843CC019    REG_BINARY    01000000D08C9DDF0115D1118. . .


为了防止离线破解,这些BLOB已经被截断。当IE连接到一个网站时,它需要凭据(指账户)来自动登录,它将

1,  导出URL的SHA-1的校验和

2,  在自动完成表单中搜索这个校验和

3,  如果发现校验和,就使用URL解密DPAPI保护的BLOB,并自动填充登录字段。


三,生成

先拿到第二个哈希值(上面展示的三个哈希值中的第二个,老外应该是要对那个hash散列开始分析)

DF11F9BE8F0049A2FBFF29C6D49FE77383C2A678 3A

这个是采用Unicode编码的字符串https://accounts.google.com/servicelogin 使用SHA-1产生的校验和

最后一个字节0x3A 是每个字节采用SHA-1散列后的校验和

以下函数使用Windowscrypto API演示了这个过程。


bool GetUrlHash(std::wstring url, std::wstring &result) {
  HCRYPTPROV hProv;
  HCRYPTHASH hHash;
  bool bResult = false;
  std::transform(url.begin(), url.end(), url.begin(), ::tolower); 
  if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 
      CRYPT_VERIFYCONTEXT)) {
    if (CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) {
      if (CryptHashData(hHash, (PBYTE)url.c_str(), 
          url.length() * sizeof(wchar_t) + 2, 0)) {

        BYTE bHash[20];
        DWORD dwHashLen = sizeof(bHash);
        if ((bResult = CryptGetHashParam(hHash, HP_HASHVAL, bHash, 
            &dwHashLen, 0))) {
          BYTE chksum = 0;
          wchar_t ch[4];
          for (size_t i = 0;i < 20 + 1;i++) {
            BYTE x;

            if (i < 20) {
              x = bHash[i];
              chksum += x;
            } else {
              x = chksum;
            }
            wsprintf(ch, L"%02X", x);
            result.push_back(ch[0]);
            result.push_back(ch[1]);
          }
        }
      }
      CryptDestroyHash(hHash);      
    }
    CryptReleaseContext(hProv, 0);
  }
  return bResult;
}


每一个用户名和密码都采用Unicode格式存储

如果存在相同的URL的表单凭据,新的账户信息会被添加在现有的表单凭据中

 

问题在于,并没有正式的文档准确的描述了凭据表单的结构,幸而,网上有一个文档描叙了它的旧版结构,这对我帮助很大。

enum { MAX_STRINGS = 200 };   
enum { INDEX_SIGNATURE=0x4B434957 };
enum { INIT_BUF_SIZE=1024 };
enum { LIST_DATA_PASSWORD = 1 };

struct StringIndex {
  DWORD   dwSignature;
  DWORD   cbStringSize;   // up to not including first StringEntry
  DWORD   dwNumStrings;   // Num of StringEntry present
  INT64   iData;          // Extra data for string list user

  struct tagStringEntry {
    union
    {
      DWORD_PTR   dwStringPtr;    // When written to store
      LPWSTR      pwszString;     // When loaded in memory
    };
    FILETIME    ftLastSubmitted;
    DWORD       dwStringLen;        // Length of this string
  }StringEntry[];
};


使用这个结构来解析一个已经解密的BLOB以供参考引起了一些麻烦,并且需要一些小的改动。在IEFrame.dll中,CryptProtectData()使用URL 作为凭据(密钥)来加密StringIndex和凭据表单。

接下来的问题是发现被用做密钥的原始的URL,这也是为什么IE密码算法相当不错的原因。




四,获取登录网址

有许多方法可以获取URL用于解析IE7-IE9密码。缓冲中通常有一个访问网站的列表,这个列表可以列举出我们想要的URL。

下面是使用COM的老获取URL的一种方式


void EnumCache1() {
  HRESULT hr = CoInitialize(NULL);
  if (SUCCEEDED(hr)) {
    IUrlHistoryStg2 *pHistory = NULL;
    hr = CoCreateInstance(CLSID_CUrlHistory, NULL, 
        CLSCTX_INPROC_SERVER, 
        IID_IUrlHistoryStg2,(void**)(&pHistory));
    if (SUCCEEDED(hr)) {
      IEnumSTATURL *pUrls = NULL;
      hr = pHistory->EnumUrls(&pUrls);
      if (SUCCEEDED(hr)) {
        while (TRUE) {
          STATURL st;
          ULONG result;
          hr = pUrls->Next(1, &st, &result);
          
          if (SUCCEEDED(hr) && result == 1) {
            AddUrl(st.pwcsUrl);
          } else {
            break;
          }
        }
        pUrls->Release();
      }
      pHistory->Release();
    }
    CoUninitialize();
  }  
}


另一种方法是使用WININET API

void EnumCache2()   
{   
  HANDLE hEntry;   
  DWORD dwSize;
  BYTE buffer[8192];
  LPINTERNET_CACHE_ENTRY_INFO info = (LPINTERNET_CACHE_ENTRY_INFO) buffer;
  dwSize = 8192;
  hEntry = FindFirstUrlCacheEntry(NULL, info, &dwSize);
  if (hEntry != NULL) {
    do {
      if (info->CacheEntryType != COOKIE_CACHE_ENTRY) {
        AddUrl(info->lpszSourceUrlName);
      }
      dwSize = 8192;    
    } while (FindNextUrlCacheEntry(hEntry, info, &dwSize));   
    FindCloseUrlCache(hEntry);
  }
}


从更高的角度来看,你也可以解析index.dat文件,但我这里并不打算那么做,因为它是在取证的高度。

 

一个更好的方法可能是阅读从互联网上收集的URL列表。

五,恢复账户

恢复账户,即IE7-IE9的解密表单过程,它迫使我们使用URL表来解密,以下过程可以收集到自动完成表单的列表

#define MAX_URL_HASH 255

#define MAX_URL_DATA 8192

 

typedef struct _IE_STORAGE_ENTRY {

  std::wstring UrlHash;

  DWORD cbData;

  BYTE pbData[MAX_URL_DATA];

} IE_STORAGE_ENTRY, *PIE_STORAGE_ENTRY;


DWORD GetAutocompleteEntries() {
  HKEY hKey;
  DWORD dwResult;
  dwResult = RegOpenKeyEx(HKEY_CURRENT_USER, 
      L"Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2",
      0, KEY_QUERY_VALUE, &hKey);
  if (dwResult == ERROR_SUCCESS) {
    DWORD dwIndex = 0;
    while (TRUE) {
      IE_STORAGE_ENTRY entry;
      DWORD cbUrl = MAX_URL_HASH;
      wchar_t UrlHash[MAX_URL_HASH];
      entry.cbData = MAX_URL_DATA;
      dwResult = RegEnumValue(hKey, dwIndex, UrlHash, &cbUrl, 
          NULL, 0, entry.pbData, &entry.cbData);
      if (dwResult == ERROR_SUCCESS) {
        entry.UrlHash = UrlHash;
        ac_entries.push_back(entry);
      } else if (dwResult == ERROR_NO_MORE_ITEMS) {
        break;
      }
      dwIndex++;
    }
    RegCloseKey(hKey);
  }  
  return ac_entries.size();
}



现在,有了URL字符串和自动完成表单,我们可以尝试使用CryptUnprotectData()去解密那些数据。然后使用StringIndex结构解析解密后的数据。

void ParseBlob(PBYTE pInfo, const wchar_t *url) {
  StringIndex* pBlob = (StringIndex*)pInfo;
  // get offset of data
  PBYTE pse = (PBYTE)&pInfo[pBlob->cbHdrSize + pBlob->cbStringSize1] ;
  // process 2 entries for each login
  for (DWORD i = 0;i < pBlob->dwNumStrings;i += 2) {
    // get username and password
    wchar_t *username = (wchar_t*)&pse[pBlob->StringEntry[i + 0].dwStringPtr];
    wchar_t *password = (wchar_t*)&pse[pBlob->StringEntry[i + 1].dwStringPtr];
    bool bTime;
    wchar_t last_logon[MAX_PATH];
    if (lstrlen(password) > 0) {
      // get last time this was used
      FILETIME ft;
      SYSTEMTIME st;
      FileTimeToLocalFileTime(&pBlob->StringEntry[i].ftLastSubmitted, &ft);
      FileTimeToSystemTime(&ft, &st);
      bTime = (GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &st, L"MM/dd/yyyy", last_logon, MAX_PATH) > 0);
    } else {
      bTime = false;
    }
    wprintf(L"\n%-30s  %-20s  %-15s %s", username, password, bTime ? last_logon : L"NEVER", url);
  }
}


六,总结

由于URL作为entropy()来使用,因为可以恢复自动完成表单的所有条目。

这仅仅可以恢复当前流行的网页的凭据表单,诸如Facebook, Gmail, Instagram, Hotmail.

但少为人知的将是问题,除非网址被存储被高速缓存中。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
"Multi-Stage Algorithms: A Comprehensive Survey"(2017)是一篇综合调查多阶段算法的文章。该文章由Y. Zhang, Y. Han, L. Liu等人撰写,并提供了对多阶段算法的定义、分类、性质、应用领域和未来研究方向的综述。 文章首先介绍了多阶段算法的基本概念和定义。它解释了多阶段算法是一种将问题分解为多个阶段并分别解决的方法,每个阶段的解决方案都依赖于前一个阶段的结果。 接下来,文章对多阶段算法进行了分类。它将多阶段算法分为序列决策问题、多层次决策问题和分布式决策问题等不同类型,针对每种类型讨论了其特点和应用。 然后,文章回顾了多阶段算法的性质。它详细探讨了多阶段算法的可行性和最优性等性质,并说明了在不同约束条件下多阶段算法的优化目标和限制条件。 文章接着讨论了多阶段算法在各个领域中的应用。它提到了多阶段算法在机器学习、数据挖掘、网络优化和资源分配等领域的应用案例,并强调了多阶段算法在处理复杂问题和大规模数据时的优势。 最后,文章总结了目前多阶段算法研究的主要趋势和未来的研究方向。它提出了一些未解决的问题,如多阶段算法的实时性、稳定性和鲁棒性等,并呼吁进一步研究多阶段算法的性能分析和优化方法。 "Multi-Stage Algorithms: A Comprehensive Survey"这篇文章提供了对多阶段算法的全面调查,涵盖了其定义、分类、性质、应用领域和未来研究方向等方面。它为研究人员和从业者提供了对多阶段算法的深入了解和指导。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值