深入微软IIS哈希表:缓存投毒与认证绕过漏洞解析

本文详细分析了微软IIS服务器中哈希表实现的三个关键漏洞:CVE-2022-22025哈希洪水拒绝服务攻击、CVE-2022-22040缓存投毒攻击以及CVE-2022-30209认证绕过漏洞,涵盖技术原理与利用方案。

Orange: 在缓存中起舞——破坏微软IIS的哈希表!

大家好,这是我在Black Hat USA和DEFCON的第五次演讲。你可以在此获取幻灯片和视频:

作为计算机科学中最基本的数据结构,哈希表被广泛用于计算机基础设施,如操作系统、编程语言、数据库和Web服务器。由于其重要性,微软从很早阶段就设计了自己的哈希表算法,并大量应用于其Web服务器IIS。

由于IIS不公开源代码,我认为该算法的实现细节应是一个未探索的漏洞发现领域。因此,本研究主要关注哈希表的实现及其使用。我们还研究了缓存机制,因为IIS中大多数哈希表的使用都与缓存相关!

由于大多数细节都在幻灯片中,请原谅我这次只提供简要文章而非完整博客。

CVE-2022-22025 - 微软IIS哈希洪水拒绝服务攻击

很难想象在2022年我们还能在IIS中看到哈希洪水攻击这样的经典算法复杂性攻击。尽管微软配置了每30秒删除过期记录的线程来缓解攻击,但我们仍在实现中发现了一个关键分割漏洞,可将攻击威力放大10倍以上,通过零哈希击败守护者。通过此漏洞,我们可以使默认安装的IIS服务器在大约每秒30个连接时无响应!

由于此漏洞也符合Windows Insider Preview赏金计划,我们因此获得了30,000美元的赏金。这是拒绝服务类别的最高赏金! 你可以在此查看完整演示视频:[链接]

CVE-2022-22040 - 微软IIS缓存投毒攻击

与其他出色的缓存投毒研究相比,这个相对简单。该漏洞发现在输出缓存组件中,该模块负责缓存动态响应以减少Web堆栈上昂贵的数据库或文件系统访问。

输出缓存使用一个不良的查询字符串解析器,当查询字符串键重复时,只取第一次出现作为缓存键。这种行为本身并不是问题。然而,在与后端ASP.NET的整个架构视图中,这是一个麻烦。后端将所有重复键的值连接在一起,这导致了解析器行为之间的不一致。因此,经典的HTTP参数污染可以使IIS缓存错误的结果!

CVE-2022-30209 - 微软IIS认证绕过

这可能是本次演讲中最有趣的漏洞。LKRHash是微软在1997年设计并申请专利的哈希表算法。它基于线性哈希,由微软研究院的Paul Larson以及IIS团队的Murali Krishnan和George Reilly创建。

LKRHash旨在在多线程和多核环境下构建可扩展和高并发的哈希表。创建者付出了大量努力使此实现可移植、灵活和可定制,以适应微软的多个产品。应用程序可以定义自己的表相关函数,如哈希函数、键提取函数或键比较函数。这种扩展性为漏洞挖掘创造了许多机会。因此,在此背景下,我们更关注记录、键和函数之间的关系。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
CLKRHashTable::CLKRHashTable(
    this,
    "TOKEN_CACHE",   // 用于调试的标识符
    pfnExtractKey,   // 从记录中提取键
    pfnCalcKeyHash,  // 计算键的哈希签名
    pfnEqualKeys,    // 比较两个键
    pfnAddRefRecord, // 在FindKey等中添加引用
    4.0,             // 平均链长的界限
    1,               // 哈希表的初始大小
    0,               // 从属哈希表的数量
    0                // 允许多个相同键?
);

由于“登录”是一个昂贵的操作,为了提高性能,IIS默认缓存所有基于密码认证的令牌,如基本认证,而这次我们发现的漏洞位于发生碰撞时键比较函数的逻辑中。

如果登录尝试的哈希命中已在缓存中的键,LKRHash进入应用程序特定的pfnEqualKeys函数以确定键是否正确。TokenCacheModule的应用程序特定逻辑如下:

代码截图

我最喜欢的漏洞!😆原始意图是比较密码。然而,开发者复制粘贴了代码但忘记替换变量名。这导致了IIS上的认证绕过。

如逻辑所示,它比较多个部分以做出决定,奇怪的是为什么IIS比较用户名两次。

我猜原始意图是比较密码。然而,开发者复制粘贴了代码但忘记替换变量名。这导致攻击者可以使用随机密码重用另一个用户的登录令牌。

要构建最小的PoC来测试你自己的系统,你可以创建一个测试账户并在IIS上配置基本认证。

1
2
3
4
5
6
# 添加测试账户,请确保测试后删除
> net user orange test-for-CVE-2022-30209-auth-bypass /add

# 登录来源不重要,这可以在IIS外部完成。
> curl -I -su 'orange:test-for-CVE-2022-30209-auth-bypass' 'http://<iis>/protected/' | findstr HTTP
HTTP/1.1 200 OK

在攻击者终端上:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 用于完整性检查的脚本
> type test.py
def HashString(password):
    j = 0    
    for c in map(ord, password):
        j = c + (101*j)&0xffffffff
    return j

assert HashString('test-for-CVE-2022-30209-auth-bypass') == HashString('ZeeiJT')

# 成功登录前
> curl -I -su 'orange:ZeeiJT' 'http://<iis>/protected/' | findstr HTTP
HTTP/1.1 401 Unauthorized

# 成功登录后
> curl -I -su 'orange:ZeeiJT' 'http://<iis>/protected/' | findstr HTTP
HTTP/1.1 200 OK

如你所见,攻击者可以使用另一个密码登录用户orange,该密码的哈希与原始密码相同。

然而,碰撞哈希并不容易。每次尝试的概率仅为1/2^32,因为哈希是32位整数,且攻击者无法知道现有缓存键的哈希。要使利用此漏洞像抽奖一样,这是一个荒谬的数字。唯一的优点是尝试成本为零,并且你有无限次尝试!

为了使此漏洞更实用,我们提出了几种中奖的方法,例如:

  • 增加碰撞几率 - LKRHash结合LCGs来扰乱结果以使哈希更随机。然而,我们可以降低键空间,因为LCG在32位整数下不是一对一映射。必定有一些结果永远不会出现,因此我们可以预计算一个排除哈希不在结果中的密码的字典,并将成功率提高至少13%!
  • 重获主动权 - 通过理解根本原因,我们头脑风暴了几种用例,可以永久缓存令牌在内存中,不再等待用户交互,例如IIS功能“Connect As”或利用软件设计模式。

我们还证明了此攻击自然适用于Microsoft Exchange Server。通过利用默认激活的Exchange Active Monitoring服务,我们可以无需密码进入HealthMailbox的邮箱!这种无认证的账户劫持对于进一步的利用(如钓鱼或链接另一个认证后RCE)非常有用!

时间线

  • 2022年3月16日 - 我们通过MSRC门户向微软报告了IIS缓存投毒。
  • 2022年4月9日 - 我们通过MSRC门户向微软报告了IIS哈希洪水拒绝服务攻击。
  • 2022年4月10日 - 我们通过MSRC门户向微软报告了IIS认证绕过。
  • 2022年7月12日 - 微软在7月的补丁星期二修复了所有问题。

P.S. 本博客中涉及的所有漏洞均已负责任地报告给微软,并于2022年7月修复。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计