TCP/IP和谈栈系列缝隙(NAME:WRECK)分析

颁布功夫 2021-04-25

缝隙概述


2021年04月13日 ,Fourscore钻研尝试室与JSOF合作 ,披露了一组新的DNS缝隙 ,被称为NAME:WRECK 。这些缝隙影响了四种盛行的TCP/IP仓库--即FreeBSD、IPnet、Nucleus NET和NetX ,它们普遍存在于驰名的IT软件和盛行的IOT/OT固件中 ,并有可能影响全球数百万的物联网设备 。攻击者能够利用这些缝隙使受影响的设备脱机或对设备进行节造 。


有关介绍


1、DNS和谈之压缩指针


在之前的文章中 ,我们介绍了基础的DNS和谈 ,其中域名是由一连串的label组成的 ,如下图所示:


1.png


其中红框所示为每个label的长度 ,每个label最长为63字节 ,并且处置的时辰 ,除了第一个长度字节 ,将每个长度字节代替为”.” ,最后遇到null字节实现 ,从而组成了最后的域名 。


不外在一些回复包中 ,会蕴含屡次A纪录或CNAME纪录 ,这就造成了DNS数据过于冗长 ,因而DNS和谈的设计者设计出了压缩指针 。在压缩指针的机造中 ,通过指针指向之前出现过的一连串label从而达到压缩的主张 。这个指针由两个字节组成 ,第一个字节的前两个bits为11 ,后面14个bits为偏移地址 。

 

2.png


下面举一个具体的例子 ,如下图数据包所示 ,红框内为压缩指针 ,指向偏移0x0c的地位 ,也就是www.example.com的开头处 。


3.png


2、DHCP和谈


 DHCP动态主机配置和谈 ,前身是BOOTP和谈 ,是一个局域网的网络和谈 ,使用UDP和谈工作 ,通常被用于局域网环境 ,重要作用是集中地治理、分配IP地址 ,使客户端动态的获得IP地址、Gateway地址、DNS服务器地址等信息 ,并可能提升地址的使用率 。


DHCP报文共有8种 ,别离如下所示:

DHCPDISCOVER :客户端起头DHCP过程发送的报文 ,是DHCP和谈的起头 。

DHCPOFFER:服务器接管到DHCPDISCOVER之后做出的响应 ,蕴含了赐与客户端的IP(yiaddr)、客户端的MAC地址、租约过期功夫、服务器的鉴别符以及其他信息 。

DHCPREQUEST:客户端对于服务器发出的DHCPOFFER所做出的响应 。在续约租期的时辰同样会使用 。

DHCPACK:服务器在接管到客户端发来的DHCPREQUEST之后发出的成功确认的报文 。在成立衔接的时辰 ,客户端在接管到这个报文之后才会确认分配给它的IP和其他信息能够被允许使用 。

DHCPNAK:DHCPACK的相反的报文 ,暗示服务器回绝了客户端的要求 。

DHCPRELEASE:通常呈此刻客户端关机、下线等情况 。这个报文将会使DHCP服务器开释发出此报文的客户端的IP地址 。

DHCPINFORM:客户端发出向服务器要求一些信息的报文 。

DHCPDECLINE:当客户端发现服务器分配的IP地址无法使用(如IP地址矛盾时) ,将发出此报文 ,通知服务器不容使用该IP地址 。


DHCP数据包发送过程 ,如下图所示:


4.png


DHCP报文体式如下图所示:


5.png


部门数据域界说 ,如下所示:


xid:随机天生的一段字符串 ,两个数据包占有一样的xid注明他们属于统一次会话 。

ciaddr:客户端会在发送要求时将自己的IP放在此处 。

yiaddr:服务器会将想要分配给客户端的IP放在此处 。

siaddr:疏导法式中使用的下一个服务器的IP地址;由服务器在DHCPOFFER ,DHCPACK中返回 。

chaddr:客户端的MAC地址 。

giaddr:若是必要跨子网进行DHCP地址发放 ,则在此处填入经过的路由器的IP地址 。

sname:服务器主域名 。

options:能够自由增长的部门 ,用于存放客户端向服务器要求信息和服务器的应答信息 。


DHCP域搜索选项 ,该选项从DHCP服务器传递到DHCP客户端 ,以指定在使用DNS解析主机名时使用的域搜索列表 。该选项的代码为119 ,体式如下图所示:


6.png


 举个例子 ,下图是“eng.apple.com”和“marketing.apple.com”组成的搜索列表的示例编码:


7.png


该示例编码已分为三个“域搜索选项” 。在客户端解析之前 ,所有域搜索选项在逻辑上都串联到一个数据块中 。以第一个“域搜索选项”为例 ,第一个字节为119 ,第二个字节为9 ,暗示后面Searchstring的长度 ,剩下的数据均为Searchstring 。这三个“域搜索选项”的Searchstring组合成一个齐全聚合块 ,可暗示为:

|3|’e’|’n’|’g’|5|’a|’p’|’p|’l’|’e’|3|’c’|’o’|’m’|0|9|’m’|’a’|’r’|’k’|’e’|’t’|’i’|’n’|’g’|0xC0|0x04| 。


“eng.apple.com”的编码以零结尾 ,以象征名称的结尾 。“marketing”(针对marketing.apple.com)的编码以两个八位字节的压缩指针C004(十六进造)结尾 ,该指针指向DomainSearchOption数据的齐全聚合块(从第一个“域搜索选项”中的Searchstring起头)中的偏移量4 ,其中另一个有效编码能够找到齐全的域名(“apple.com”) 。如下图所示:

 

8.png


每个搜索域名都必须以零或两个八位位组压缩指针结尾 。若是接管器达到搜索列表选项数据的齐全汇总块的末尾使佚在通过搜索域名进行解码 ,而没有找到零或有效的两个八位位组压缩指针 ,那么必须将部门读取的域名视为无效域名 。


缝隙分析


1、Nucleus NET系列缝隙


9.png


上图为Nucleus NET和谈栈中的DNS_Unpack_Domain_Name()函数 ,这个函数用来处置DNS应答纪录 。第一个参数dst是指向一个buffer ,用于拷贝解析的域名 。第二个参数src指向域名的第一个字节 ,第三个参数指向DNS Header的第一个字节 。


代码通过while循环去解析域名(第7行) ,直到src为null字节 ,也就是域名的结尾 。之后将第一个label的长度赋值给size(第9行) 。下面 ,也就是最沉要的一步就是查抄该字节是否为压缩指针 。若是不是 ,src指针前移一个字节 ,而后将src拷贝到dst 。而后每个label之间加”.” 。为正常的解析域名人程 。若是是压缩指针并且是第一个压缩指针 ,retval加两个字节(第10 ,11行) ,而后凭据偏移推算label肇始地位 ,而后将长度赋值给size ,之后正常处置 。


这段代码看起来没有问题现实上蕴含4个极度严沉的问题 ,其造成的缝隙编号别离为CVE-2020-27736、CVE-2020-27738、 CVE-2020-15795、CVE-2020-27009 。


(1)对label长度没有做验证

凭据上文讲述的内容 ,每个label的第一个字节代表长度(第8 ,16行) ,但是法式没有查抄这个长度是否代表真实数据包中label的长度 ,这可导致读取超过已分配结构体的buffer ,造成回绝服务 。


(2)对压缩指针的偏移没有做验证

凭据上文讲述的内容 ,法式判断为压缩指针后 ,便会通过给出的偏移去寻找解析label(第14,15,16行) ,但是法式没有验证偏移的领域 ,这导致偏移值能够肆意给定 ,这导致能够越界读写 ,从而RCE 。


(3)短缺NULL终止判定

凭据RFC1035的表述 ,NULL字节(0x00)暗示name的结尾(第7行) 。但是在好多DNS解析法式中不足对NULL字节的验证 ,这导致攻击者能够通过节造NULL字节在特定的地位 ,通过和前几个问题相结合 ,同样能够实现可控的内存读写 。


(4)对域名的长度没有做限度

凭据RFC1035的陈述 ,从DNS纪录里提取的域名不应该超过255字节 ,只管每个label限度了不超过63个字节(第15行) ,但是这只是一次性拷贝的长度 ,并没有限度现实拷贝的长度 。如下图所示 ,通过NU_Allocate_Memory()函数分配给name 255个字节个空间(第50行) ,即DNS_MAX_NAME_SIZE 。后面挪用DNS_Unpack_Domain_Name的过程中 ,显然能够通过机关恶意的数据包溢出这255字节 。

 

10.png


所以凭据以上内容 ,PoC的编写的思路有好多步骤 ,好比能够让法式始终无法退出循环 ,类似下图:

 

11.png


c0为压缩指针 ,1e为偏移量 ,而偏移的地位正好沉新指向c0 ,造成无限循环 。


2、FreeBSD缝隙(CVE-2020-7461)


该缝隙呈此刻dhclient解析DHCP数据包中的“域搜索选项”数据时 ,由于天堑查抄谬误而导致堆溢出 。dhclinet是FreeBSD系统中用于提供DHCP服务的二进造法式 ,执行号令:dhclient em0便可进行DHCP配置 ,em0为网卡 。源码位于sbin\dhclient\options.c ,从do_packet()函数起头 ,代码清单如下图所示:


12.png


该函数用于处置DHCP客户端接管到的数据包 ,行890 ,挪用parse_options()函数解析数据包中的options ,代码清单如下图所示:


13.png


行106 ,挪用expand_domain_search()函数进一步解析DHCP域搜索选项数据 ,该函数进行两个操作 ,第一步操作是先获取所有域名标签的总长度 ,第二步操作是凭据第一步获取的长度进行内存分配 ,并拷贝所有域名标签 。

先看第一操作 ,若何获取所有域名标签的长度 ,关键代码实现如下图所示:


14.png


首先判断options是否为空 ,不为空就获取options ,行229 ,而后进入while循环 ,挪用find_search_domain_name_len()函数处置options ,该函数通过一个while循环逐个字节解析Searchsting并分类处置 ,第一种情况的关键代码实现如下图所示:

 

15.png


若是读取到data[i]为0 ,暗示域名标签的结尾 ,并返回该域名标签长度 。接着第二种情况的关键代码实现如下图所示:


16.png


若是读取到data[i]为0xC0 ,暗示为压缩指针 ,指向另一个域名标签 ,行287 ,推算pointer ,而后对该指针进行领域查抄判断是否越界(第299行) ,递归挪用find_search_domain_name_len()函数解析压缩指针指向的另一个域名标签(第301行) ,递归挪用返回后 ,进行domain_name_len += pointer_len累加 。若是既不是0结尾也不是压缩指针 ,则顺次累加并移位游标 ,实现代码如下图所示:


17.png


在第299行和第301行之间是存在问题的 ,若是递归处置压缩指针指向的另一个域名标签不合法时 ,返回的pointer_len为-1 ,这里并没有将其视为无效并进行返回 ,而是仍旧返回部门域名标签长度 。

再看第二操作 ,分配缓冲区并进行域名标签拷贝 ,关键代码如下图所示:


18.png


这里expanded_len为推算出来的域名标签的长度(第242行) ,分配一段内存(第248行) ,进入while循环 ,挪用expand_search_domain_name()函数进行域名标签拷贝 ,实现代码如下图所示:


19.png


该函数和find_search_domain_name_len()函数实现根基是一样的 ,分类处置0结尾和压缩指针 ,但是在处置压缩指针的情况时 ,并没有判断指针领域的合理性 。行366 ,挪用memcpy进行域名标签拷贝 ,拷贝长度为label_len 。


凭据前文分析 ,第一步操作在递归处置压缩指针的情况时 ,是存在问题的 ,并没有抛弃蕴含无效压缩指针的域名标签 �D芄换匾桓鎏厥獾挠蛎昵┗旌蟣ebal的递归解析 ,该域名标签暗示为:| 1 |x3F|x00| 1 |'A'|xC0|x01| 。


顺次读取到label_len为0xC0时 ,进一步递归挪用解析 ,这次读取到label_len为0x3f ,将0x3f当成了域名长度 ,但是第305行 ,判断产生越界 ,因而返回-1 。


20.png


但是这里依然推算出domain_name_lan为1 ,因而第二个域名蕴含一个0x41 。


21.png


最后推算出expanded_len为0x5 ,如下图所示:


22.png


而后起头进行第二步拷贝操作 ,先拷贝第一个domain:0x3f 。如下图所示:

 

24.png


第二次拷贝第二个domain:0x41 。如下图所示:

 

25.png


递归解析压缩指针0XC004时 ,产生了混合 ,如下图所示:


26.png


谬误地将0x3f当成label_len ,这显著是大于expanded_len的 ,直接拷贝导致溢出 。不外在现实测试中 ,并未产生内存粉碎 ,而能够机关其他的域名标签陷入无限递归 ,让dhclient过程仓库耗尽导致崩溃 ,造成回绝服务 。


27.png

 

措置建议


FreeBSD、Nucleus NET和 NetX ,建议先执行以下安全建议 ,再实时更新设备供给商颁布的安全更新 。

安全建议:


使用一些缓解信息来开发检测DNS缝隙的署名;

发现并盘点运行易受攻击仓库的设备;

执行分段节造和适当的network hygiene;

监督受影响的设备供给商颁布的补� �;

配置设备依赖内部DNS服务器;

监控所有网络流量中的恶意数据包 。