Windows DNS Server远程代码执行漏洞分析
- CNNVD编号:未知
- 危害等级: 高危

- CVE编号:CVE-2020-1350
- 漏洞类型: 远程代码执行
- 威胁类型:远程
- 厂 商:未知
- 漏洞来源:深信服
- 发布时间:2021-01-06
- 更新时间:2021-01-13
漏洞简介
Microsoft Windows Server操作系统自带了一个DNS server,Windows自Windows 2000后提供了一些DNS API给应用程序开发者,允许他们通过这些API使用一些DNS方法(例如创建DNS查询,比较记录,查找name等)。
一种最简单的用户和DNS Server的交互过程如下图所示:

DNS查询和响应均通过DNS消息进行传输,消息的结构如下所示:
+---------------------+
| Header |
+---------------------+
| Question / Zone | the question / zone for the name server
+---------------------+
| Answer / Prereq | RRs answering the question / prereq
+---------------------+
| Authority / Updates | RRs pointing toward an authority / updates
+---------------------+
| Additional | RRs holding additional information
+---------------------+
此外,DNS还包含Dynamic Update消息类型,在结构上类似于query/response,但是在Dynamic Update消息中的section的名称有一定变化,如上面结构所示,Question称为Zone,Answer称为Prerequisites,Authority称为Updates。
DNS Header部分的结构如下所示:
Field | Length(Bits) | Description |
ID | 16 | 匹配query和response消息的ID |
QR | 1 | 指明为Query(QR=0)还是Response(QR=1)的flag标志 |
OPCODE | 4 | 指明query的类型,0表示为标准类型,5表示为Dynamic Update |
AA | 1 | Authoritative Answer,表明answer是否来自于authority |
TC | 1 | Truncation,指明是否因为传输限制进行truncate |
RD | 1 | Recursion Desired,指明resolver是否递归执行query |
RA | 1 | Recurision Available,指明resolver是否递归处理query |
Z | 3 | 未使用 |
RCODE | 4 | 指明错误状态,0表示没有错误 |
QDCOUNT | 16 | Question/Zone Section中的entity数量 |
ANCOUNT | 16 | Answer/Prereq Section中的entity数量 |
NSCOUNT | 16 | Authority/Updates Section中的entity数量 |
ARCOUNT | 16 | Additional Section中的entity数量 |
所有多字节数据均以大端序进行传输,上面提到的各个section都包含不定数量的Resource Records(RR),这些RR指定了DNS 资源和entity的详细信息。RR的结构如下:Field | Length(bytes) | Description |
Name | var | Owner Name,该domain属于的node |
Type | 2 | 资源记录type码 |
Class | 2 | 资源记录class码 |
TTL | 4 | 记录缓存的秒数 |
RDLENGTH | 2 | RDATA字段的长度 |
RDATA | var | 记录数据,格式取决于type和class |
对于Zone section来说,包含如上格式的RR,但是省略TTL,RDLENGTH,RDATA。
Name字段编码了0个或多个DNS标签,标签以空字符结尾。每个标签为一个1字节固定长度的字节字符串。例如,一个域名为www.example.com,会切分成3个标签,www,example以及com,然后进行编码组合到一起\x03www\x07example\x03com\x00。或者,不使用按照长度编码为字节字符串,而是一个标签包含一个2字节的指针,前2个最重要的位设置为1,其他位存储从DNS Header部分的未压缩标签出现的位置的偏移(字节数),例如Zone包含一个Name为\x03www\x07example\x03com\x00的RR,然后其他的RR可以使用2字节的指针\xc0\xc0指向该name,0xc0是距Zone RR的Name字段出现的DNS数据包起点的偏移。
Dynamic Update是可以远程更新DNS记录的一种功能,该功能允许经过授权的更新者区域中的权威名称server上增加和删除资源记录。通常情况下,Dynamic Update使用SIG或者TSIG对更新进行签名。
漏洞公示
在Windows系统中,DNS Client和DNS Server通过两个不同的模块进行实现:
- DNS Client:dnsapi.dll,负责处理DNS 解析- DNS Server:dns.exe,负责响应DNS查询在dns.exe中,实现针对各种支持的响应类型的解析函数:
dns!RR_DispatchFunctionForType()通过RRWireReadTable来确定对应的处理功能。RRWireReadTable中包含的支持的响应类型如下:
对于SIG 查询的响应类型也在其中。而处理 SIG 查询的响应类型的函数为dns!SigWireRead(),其汇编代码如下:

也就是在接收到包含SIG RRd的Dynamic Update查询后,dns!SigWireRead()函数被调用来进行RR解析,并存储到一个堆缓冲区中。在调用DNS!RR_AllocateEx()函数进行堆区分配之前,首先调用dns!Name_PacketNameToCountNameEx()函数统计Signer's Name的长度,然后按照如下公式进行计算需要分配的缓冲区的大小:bsize (buffer size) = Length of uncompressed Signer's Name + 0x14 + Length of Signature
上面公式的计算过程使用的均为16位寄存器,然后在RR_AllocateEx()函数中进行扩展:

如果最后bsize的计算结果大于0xFFFF(16位寄存器能表示的最大值),那么在传入RR_AllocateEx()函数进行内存分配操作时,就会分配一个很小的缓冲区:
从代码中可以看到,在进行内存分配时,实际分配的内存大小为bsize + 0x4a,而且在进行copy操作时,将SIG RR的Signature字段copy到了偏移为Length of uncompressed Signer's Name+0x4c处的缓冲区中,这主要是为了确保Signature可以被copy到缓冲区的末尾位置。
正常情况下,bsize的计算结果会比0xFFFF小,原因如下:- DNS通过TCP传输的packet的最大数据量为0xFFFF- DNS的packet一定包含一个12字节的Header- 对于root zone来说,一个最小的Zone RR大小为5字节- Signer's Name字段前的SIG RR的长度为18个字节
因此,在正常情况下,Signer's Name和Signature长度的和一定小于0xFFFF - 12 - 5 - 18 = 0xFFDC,那么也就说明bsize的长度不会大于0xFFF0(0xFFDC + 0x14)。
但是,仍然有可能构造一个恶意的Signer's Name来使得bsize的大小超过0xFFFF来造成溢出。因为SIG RR的前18个字节在调用memcpy()函数之前都不会被处理,从而可以任意使用这些字节来存储一个DNS name,而且,构造的DNS name的最后2个字节可以指向Zone RR的Name字段。例如,Name的前18个字节如下:\x0FAAAAAAAAAAAAAAA\xc0\x0c
使用嵌套指针在SIG RR的18个字节中构造一个Name,如下所示:
Label1 = <Label1_Length> <Label1_Name> \xc0\x0c
Crafted_Name = <Label2_Length> <Label1> <Pointer1>
Pointer1指向Label1_Length处的字节。请注意,Crafted_Name包含一个指向Label1的指针Pointer1,Label1本身包含在Crafted_Name中。由于Label1的指向\xc0\x0c后缀(即Zone RR的Name字段),因此不会导致无限递归,而是通过添加作为名称子集的后缀来延长Crafted_Name 。以一个例子来说明该种方案:Label1 = \x0cAAAAAAAAAAAA\xc0\x0c
Crafted_Name = \x11\x0cAAAAAAAAAAAA\xc0\x0c\xc0XX
其中,Zone假设为example.com,XX指向Label1。Crafted_Name就变成\x0CAAAAAAAAAAAA\xC0\x0C.AAAAAAAAAAAA.example.com,长度由原来的18字节变为40字节。通过嵌套指针,使得长度变为了原来的2倍。如果Signer's Name指向Crafted_Name,那么就可能实现bsize大于0xFFFF,从而触发漏洞。
补丁
1、微软官方补丁
微软官方目前已发布针对此漏洞的安全更新补丁,千里目实验室建议广大用户及时确认所用Windows版本,并下载对应版本安全补丁进行更新:https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-1350
微软官方对于无法及时安装安全更新的用户提供了临时的缓解措施:
通过注册表编辑器,限制tcp包的长度
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DNS\Parameters DWORD = TcpReceivePacketSize
Value = 0xFF00
注意:需要重新启动DNS服务才能生效。
移除此临时缓解措施:
管理员可以移除值 TcpReceivePacketSize 及其数据,使注册表项 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DNS\Parameters 下的所有其他内容恢复原来的状态。
【深信服下一代防火墙】
可轻松防御此漏洞,建议部署深信服下一代防火墙的用户更新至最新的安全防护规则,可轻松抵御此高危风险。【深信服EDR】
深信服EDR已完成漏洞规则库更新,支持该漏洞的检测及补丁分发。EDR3.2.10及以上版本需要升级相应的SP包,更新漏洞补丁规则库版本到20200722103111及以上版本,即可检测漏洞并分发补丁进行漏洞修复。联网用户可直接在线更新,离线升级包及漏洞补丁规则库已上传至深信服社区,有需要的用户请到深信服社区下载。【深信服云盾】
已第一时间从云端自动更新防护规则,云盾用户无需操作,即可轻松、快速防御此高危风险。【深信服安全感知平台】
可检测利用该漏洞的攻击,实时告警,并可联动【深信服下一代防火墙等产品】实现对攻击者ip的封堵。【深信服安全运营服务】
深信服云端安全专家提供7*24小时持续的安全运营服务。在漏洞爆发之初,对存在漏洞的用户,检查并更新了客户防护设备的策略,确保客户防护设备可以防御此漏洞风险。https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-1350