首页
社区
课程
招聘
[原创]CVE-2020-1350漏洞深入剖析
发表于: 2020-7-16 02:18 12366

[原创]CVE-2020-1350漏洞深入剖析

2020-7-16 02:18
12366

CVE-2020-1350 一个 DNS 协议相关的高危漏洞,攻击者利用该漏洞可以远程执行代码,其严重性堪比 SMB/RDP 漏洞,建议所有使用 Windows Server 服务器的企业或机构尽快打上补丁。

此文参考了 CheckPoint 发布的漏洞细节,如果你熟悉 DNS 协议,建议直接肝 CheckPoint 那篇文章,文章链接我文末会贴出。

简言之就是整数溢出后导致堆溢出,将该漏洞抽象为以下模型:

问题就出在 len_p 的计算上,假设 len_p1 = 0xFFD0,len_p2 = 0x40,相加后高位数据被丢弃,那么 0xFFD0 + 0x40 = 0x10010 = 0x10,所以实际分配的内存是 0x10 而不是 0x10010,而之后往 0x10 的空间内拷贝 0xFFD0 长度的数据,自然造成了内存溢出。

碰巧的是在 dns.exe 的 SigWireRead 函数内,也存在这种模型的溢出漏洞,如图:

dns.exe 为一个 SYSTEN 权限的系统服务,该文件只存在于开启 DNS 功能的 Windows Server 服务器上。据 CheckPoint 的描述只有当 Windows Server 上的 DNS 服务接收到类型为 SIG/RRSIG 的 DNS 响应包时,才能进入到可以触发此漏洞的 SigWireRead 函数内。

DNS 服务器收到响应包?DNS 服务器不应该是回复客户端的查询而发送响应包嘛,话虽如此,但如果将 DNS 服务器也当成一个客户端,不就可以发送查询请求并接收响应数据了呀,而 DNS 转发 恰好能够让 DNS 服务器充当客户端,向上级的 DNS 服务器发起查询。

所以该漏洞触发的前提条件是:通过 DNS 转发接收一条 SIG/RRSIG 类型的 DNS 响应包

DNS 整体结构包括头部区域、查询区域、响应区域、认证区域和附加区域,后面两个暂时用不上,不赘述。结构图如下:

DNS 头部区域固定 12 字节,同一个查询和响应的 ID 是一样的,QR 指明当前是查询还是响应,OPCODE 指明是用标准查询还是反向查询,TC 表示 UDP 长度大于 512 时截断并用 TCP 再次请求(漏洞关键因素),QDCOUNT 表示查询的数量,ANCOUNT 表示响应的数量,结构图如下:

DNS 查询区域主要由欲查询的域名、查询类型和查询类组成,其中最最最重要的是域名不是普通的字符串,而是多个 length + string 的形式,结尾以 length=0 为标志,结构图如下:

DNS 响应区域由资源记录的域名,RDATA 的类型、RDATA 类、TTL 值、RDATA 长度和 RDATA 组成,结构图如下:

必须强调下 DNS 中域名的表示方式:(length + string) * n,这种表示方式是该漏洞成因的关键因素之一:

资源记录(RR)有多种类型,比如说最常用的 A 类型返回的是 IPv4 地址记录。而 dns!SigWireRead 只有在读取 SIG 类型响应包时才有机会触发 CVE-2020-1350 漏洞。SIG 资源记录的结构图如下:

简单说下各个字段的含义:

域名压缩是 DNS 协议节省空间的一种方式,因为请求中的域名字符会频繁出现在响应包的内,故采用了一种标志+偏移的格式来压缩域名。压缩方式很简单:1 + 1 + offset,即最高 2bit 位为 1 时代表了将采用域名压缩,剩余的组合为偏移值(基地址为 DNS 头),如下图:

响应区域中的 Name 字段为 0xc00c,明显是被压缩过的,最高 2bit 位是标志,剩余的 bit 位组合得到的值为 0xc,所以当前 Name 表示的字符串在 DNS 头部向后偏移 0xc 的位置,也就是图中 0x3 开始(0x50 表示 DNS 头起始位置)的域名 bbs.pediy.com

dns!SigWireRead 函数内溢出点的阈值为 65535(寄存器低 2 字节),且在计算 Buffer 长度的加法中,另外的两个值不会很大,所以只有当发送的数据长度接近 65535 时才有机会造成溢出。可是 DNS 协议默认使用 53 号 UDP 端口,此时发送的数据不能超过 512 字节,超过后会被截断。

CheckPoint 找到一种方法可以突破上述限制:UDP 响应时将 DNS 头部的 TC 标志置 1,之后客户端便会尝试用 TCP 协议和服务器建立连接,并通过 TCP 协议传递数据。TCP 协议在 53 端口上可传输长度最大为 65535 的 DNS 数据,这无疑给该漏洞创造了先决条件。

万事俱备,只欠东风,如何构造 DNS 响应包的数据就是最后一道坎了。

图一中可以看到长度计算的公式为:v10 + v13 + 0x14,v13 为 SIG RR 中的 signature 长度,v10 为 SIG RR 中 signer's name 需要分配的空间。加法的结果不超过 0xffff,如果使得 v10 + v13 + 0x14 > 0xffff,即可造成整数溢出并导致堆溢出。可是正常情况下是不可能会溢出的,因为 TCP 传输时所有 DNS 数据最长为 0xffff,那么 v13 = 0xffff - 12 - len(queries) - 20,且 v10 的值应该为 0(因为 0xc00c 表当前域名与查询时的域名相同,而 v10 会减去查询时域名的长度),若指定查询的域名为 a.cn,计算得到 len(queries) 的最小值为 10,代入公式死活都不可能溢出。

正常的 DNS 响应数据自然是不可能溢出的,但精心构造的就不一定了哦。v10 的值主要是由 Name_PacketNameToCountNameEx 函数决定,该函数计算的方式是将 (length + string) * nlength * n 取出并返回(实际可能有所差别,大概是这样子),如果将域名压缩的偏移值指向一片可控的区域,而不是指向查询时域名,就完全有可能使得 v10 > 0

比如将默认域名压缩 0xc00c 改为 0xc00d 后,你再算算之前的公式呢,v10 取个最小值 0x62 - 0xb = 0x57,len(queries) 取个稍大的值 0x20,代入公式化简得到 v10 + v13 + v14 = 0x57 + 0xffff - 12 - 0x20 - 20 + 0x14 = 0x1002A,此时的整数溢出将导致堆溢出,并最终造成 DNS 服务 Crash,如果配合内存地址泄露和任意地址读写,就可以 RCE 了。下图是修改域名压缩后计算过程的对比:

鄙人虽然没复现(实在是没空折腾了),但是理论上可行的数据包还是有的,熬夜肝出这篇文章主要是想混个助攻(万一有人看了就写出 POC 了呢)。截个代码片段,以免被喷,如图:

以下是我构造 POC 过程的一些关键点:

 
 
 
 
 
 
 
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2020-7-17 00:27 被年少无知编辑 ,原因:
收藏
免费 7
支持
分享
最新回复 (10)
雪    币: 26245
活跃值: (63297)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
2
感谢分享~
2020-7-16 09:42
0
雪    币: 3116
活跃值: (1269)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
3
赶早
2020-7-16 11:07
0
雪    币: 1285
活跃值: (231)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
4
头都秃了, 肝不动了
2020-7-16 15:12
0
雪    币: 70
活跃值: (2031)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
2020-7-16 23:43
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
求楼主复现一下写个exp开开眼
2020-7-18 16:26
0
雪    币: 1428
活跃值: (536)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
wx_四海闲人 求楼主复现一下写个exp开开眼

只有打崩dns服务的poc,要利用的话没那简单

上传的附件:
2020-7-18 19:27
0
雪    币: 1428
活跃值: (536)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
朝闻道 头都秃了, 肝不动了
秃即强(/狗头)
2020-7-18 19:28
0
雪    币: 202
活跃值: (206)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
秃即神(/狗头)
2020-7-20 15:20
0
雪    币: 1
活跃值: (213)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
有没有一起搞1350利用的?
2020-7-24 08:10
1
雪    币: 198
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
Flyour 有没有一起搞1350利用的?
1  怎么联系你
2020-7-27 10:46
0
游客
登录 | 注册 方可回帖
返回
//