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版本,并下载对应版本安全补丁进行更新:
微软官方对于无法及时安装安全更新的用户提供了临时的缓解措施:
通过注册表编辑器,限制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小时持续的安全运营服务。在漏洞爆发之初,对存在漏洞的用户,检查并更新了客户防护设备的策略,确保客户防护设备可以防御此漏洞风险。