当Windows的1月更新发布时,公众均对DHCP客户端中的CVE-2019-0547漏洞的消息所震惊。极高的CVSS评分伴随着微软没有立即发布可利用性指数评估这一事实使得用户一时难以决定是否需要立刻更新其系统。一些出版物甚至推测,可利用性指数的缺失表明在不久的将来会出现危害极大的漏洞利用。

MaxPatrol等解决方案可以识别网络上的哪些计算机容易受到某些攻击。其他解决方案若要检测此类攻击是否能起作用,需要描述识产品中的漏洞规则并检测这些产品的攻击规则。反过来,对于漏洞我们都会找出利用的方法和条件。换句话说,与简单介绍漏洞的网站活CVE中的描述中找到的内容相比,完美利用此漏洞需要我们对其更加深入和全面的理解,例如:

此漏洞的原因是操作系统错误地处理内存中的对象。

因此,要使用针对DHCP中新发现的漏洞的攻击检测规则来更新我们的产品,我们需要深入了解漏洞所有细节。 对于二进制漏洞,我们通常可以通过使用patch-diff来解决位于其根目录的错误,patch-diff比较并识别由特定补丁或更新对应用程序、库或操作系统内核的二进制代码所做的更改。 然而第一步永远是进行初步尝试。

漏洞初探

我们首先需要测试搜索引擎,查询当前已知的有关此漏洞的所有内容。 其中大部分是从MSRC网站上发表的第一手信息。 这种情况是Microsoft漏洞处于内部处理时的典型情况。

从互联网的搜索资料中,我们发现我们正在处理在Windows 10版本1803上运行的客户端和服务器系统中包含内存损坏漏洞,并且当攻击者向DHCP客户端发送特制响应时它会显示。 几天后,该页面出现了指数评级:

我们可以看到,MSRC的评级为2。这意味着错误很可能是不可利用的,或者利用它是十分困难并且需要花费许多精力。不可否认,微软很少出现如此低分的习惯。因此,我们假设如果此评分表明利用不太可能,那可能就是真的。我们可以仅仅完成分析工作。然而仔细检查漏洞并查看漏洞如何利用总会有收获。

在同一站点上,我们下载以.msu存档提供的补丁,将其解压缩并查找最有可能与客户端DHCP响应处理相关的文件。现在提供的更新不是作为修复特定错误的单独包,而是作为包含所有每月修复的单个包。这使得我们必须处理许多无关变量以找到所需数据。

在过多的文件中,我们的搜索出现了几个与过滤器匹配的库,我们将它们与未修补系统上的版本进行比较。 dhcpcore.dll库看起来我们所需要的。同时BinDiff显示出最小的变化:

实际上,此处只对一个函数DodedDomainSearchListData进行了更改。 如果用户熟悉DHCP协议及其很少使用的函数,则已经知道该函数处理的列表。

DHCP及其选项

DHCP(RFC 2131|wiki)是一种可扩展的协议,其可扩展性通过options字段实现。 每个选项由唯一标记(编号,标识符),选项中包含了数据大小以及数据本身描述。 这是典型的网络协议,并且在协议中“插入”域搜索选项,其在RFC 3397中进行描述。它允许DHCP服务器在客户端上设置标准域名结尾。 这些将用作以这种方式设置的连接的DNS后缀。

例如,假设在我们的客户端上我们设置了以下名称结尾:

.microsoft.com
.wikipedia.org

然后,在按域名确定地址的尝试中,这些结尾将逐个插入DNS请求,直到找到匹配为止。 例如,如果用户在浏览器地址栏中键入ru,则首先为ru.microsoft.com形成DNS请求,然后为ru.wikipedia.org形成DNS请求:

实际上,现代浏览器是非常智能的,因此它们通过重定向到搜索引擎来对类似于FQDN的名称作出反应。 因此,我们稍后将提供较少实用程序的输出:

读者可能会认为这是漏洞的本质。 就其本身而言,当网络上的任何设备都可以识别时,使用DHCP服务器更改DNS后缀的能力对使用DHCP请求任何网络参数的客户端构成威胁。 但那还不是全部。 从RFC中可以明显看出,这被认为是非常合法且记录在案的行为。 实际上,DHCP服务器是一个可信组件,能够影响连接到它的设备。

域搜索选项

域搜索选项号为0x77。 与所有选项一样,它带有选项号的单字节标记编码。 和大多数选项一样,标签后面跟着大小的单字节大小的数据。 DHCP消息可以包含多个选项副本。 在这种情况下,来自所有这些部分的数据以与消息中相同的顺序连接。

在取自RFC 3397的示例中,数据被分成三个部分,每个部分包含9个字节。 从图中可以看出,完整域名中的子域名使用单字节名称长度编码,后跟名称本身。 完整的域名代码以空字节结尾(子域名的空值大小)。

此外,该选项使用最简单的数据压缩方法:重新分析点。 该字段可能包含0xc0,而不是域名大小。 然后,下一个字节将建立相对于用于搜索域名结尾的选项的数据开头的偏移量。
因此,在我们的示例中,我们有一个包含两个域后缀的编码列表:

.eng.apple.com
.marketing.apple.com

DecodeDomainSearchListData函数

数字为0x77的DHCP选项允许服务器在客户端上设置DNS后缀。但不适用于装有Windows操作系统的计算机。传统上,Microsoft系统忽略了此选项,因此在必要时,使用组策略在历史上结束了DNS名称。但最近,当Windows 10版本1803的新版本引入域搜索选项处理时,情况发生了变化。如下所示,dhcpcore.dll中的函数名称已更改,它是包含错误的添加的处理程序本身。

现在让我们开始工作吧。梳理一下代码,这就是我们找到的。正如人们可能猜到的那样,DecodeDomainSearchListData过程解码来自服务器的消息的域搜索选项中的数据。作为输入,它采用如前所述打包的数据数组,并输出以空字符结尾的字符串,其中包含以逗号分隔的域名结尾列表。例如,该函数将上述示例中的数据转换为以下字符串:

eng.apple.com,marketing.apple.com

DecodeDomainSearchListData是通过UpdateDomainSearchOption函数调用的,此函数将返回的列表写入注册表项的“DhcpDomainSearchList”参数中:

HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{INTERFACE_GUID}\

它存储特定网络接口的主要参数。

DecodeDomainSearchListData函数进行两次传递。 在第一次传递时,它执行除输入缓冲区之外的所有操作。 所以第一遍用于计算保存返回数据所需的内存大小。 在第二遍中,为该数据分配存储器并填充分配的存储器。 该函数不是太大 - 大约250条指令 - 它的主要工作是处理输入流中字符的三种可能变体中的每一种:1)0x00,2)0xc0或3)所有值。 与DHCP相关的错误的修复归结为在第二遍开始时添加对结果缓冲区大小的检查。 如果大小为零,则不为缓冲区分配内存,并且该函数完成执行并返回错误:

因此,只有当目标缓冲区的大小为零时,漏洞才会显示出来。 并且在一开始该函数检查其输入,其大小不能小于两个字节。 因此,利用需要找到以输出缓冲区的大小等于零的方式形成的非空域后缀选项。

攻击过程

我们首先想到的是使用重解析来确保非空输入数据生成一个空的输出字符串:

设置为使用具有此类内容的选项进行响应的服务器确实会导致对未更新的客户端的访问冲突。当函数解析部分完整域名时,它会将该部分复制到目标缓冲区并附加句点。 在RFC的此示例中,以下数据将按以下顺序复制到缓冲区:

1). eng.

2). eng.apple.

3). eng.apple.com.

然后,当在输入数据中遇到零域大小时,该函数将目标缓冲区中的前一个字符从句点更改为逗号:

4). eng.apple.com,

进行解析操作:

5). eng.apple.com,marketing.

6). eng.apple.com,marketing.apple.

7). eng.apple.com,marketing.apple.com
8). eng.apple.com,marketing.apple.com

当输入数据结束时,剩下的就是用空字符替换最后一个逗号,这里有一个准备好写入注册表的字符串:

9). eng.apple.com,marketing.apple.com

当攻击者发送如上所述形成的缓冲区时会发生什么?从示例中我们可以看到它包含的列表由单个元素组成 - 一个空字符串。在第一次传递时,该函数计算输出数据大小。由于数据不包含任何非零域名,因此大小为零。

在第二遍中,为数据分配堆内存块并复制数据。但解析函数会立即遇到指示域名结尾的空字符,因此,如前所述,它会将前一个字符从句点更改为逗号。然后我们遇到了问题。目标缓冲区迭代器设置为零。前一个字符属于堆内存块的标头。此字符将更改为0x2c,这里是一个逗号。

但是,这仅在32位系统上发生。使用unsigned int存储目标缓冲区迭代器的当前位置会导致处理x64系统时发生更改。让我们更仔细地看一下负责将逗号写入缓冲区的代码片段:

使用32位寄存器eax从当前位置减去,但在寻址缓冲区时,代码寻址完整的64位寄存器rax。在AMD64架构上,任何具有32位寄存器的操作都会将寄存器的高半字清零。这意味着rax寄存器将在减法存储0xffffffff之后成为非-1。因此,在64位系统上,值0x2c将写入地址buf [0xffffffff],这是在为缓冲区分配的内存之外。

这些发现与Microsoft的可利用性评分密切相关,因为要利用此漏洞,攻击者必须学习如何在DHCP客户端上执行远程堆攻击,以及对堆内存分配进行充分控制以确保预设值(即,逗号和句号)被写入准备好的地址并引起可控的不利影响。

否则,将数据写入未经检查的地址将导致svchost.exe进程失败,其中包含当前可能托管的所有服务,以及操作系统随后重新启动这些服务。如果情况许可,攻击者也可以利用这一优势。

CVE-2019-0726

如果我们仔细查看导致错误的数据类型并将该数据与错误发生进行比较,我们可以看到域名列表已经发生更改,即生成的缓冲区大小不会为零, 但我们仍然会尝试将其写入缓冲区。 为此,列表的第一个元素必须是空字符串,而所有其他元素可能包含名义域名。 例如:

该选项包括两个元素。 第一个域后缀为空,它立即以空字节结束。 第二个后缀是.ru。 计算出的输出字符串大小为3个字节。同时,数据开头的零将强制函数将逗号写为结果字符串中的前一个字符,但由于迭代器在字符串中的当前位置是零,它将在分配的缓冲区之外写入。

现在我们需要通过实际测试来确认我们的理论结果。 让我们模拟一个DHCP服务器使用带有present选项的消息响应客户端请求的情况,当我尝试在为结果字符串分配的缓冲区的0xffffffff位置写一个逗号时,我们立即发现异常:

这里寄存器r8包含一个指向传入选项的指针,rdi包含分配的目标缓冲区的地址,而rax包含该缓冲区中必须写入字符的位置。这些是我们在安装了所有更新的系统上获得的结果。

本文为翻译稿件,翻译来自:[http://blog.ptsecurity.com/2019/05/dhcp-security-in-windows-10-analyzing.html](http://blog.ptsecurity.com/2019/05/dhcp-security-in-windows-10-analyzing.html)
点击收藏 | 0 关注 | 1 打赏
  • 动动手指,沙发就是你的了!
登录 后跟帖