zoukankan      html  css  js  c++  java
  • CVE-2020-1350--SigRedDNS漏洞详解与复现

    漏洞简介


    在Windows上,DNS服务器是域控制器,其管理员是Domain Admins组的一部分。默认情况下,Domain Admins组是已加入域的所有计算机上Administrators组的成员,包括域控制器[8]。如果利用得当,攻击者可以在易受攻击的系统上远程执行代码,并获得Domain Admin权限,从而有效地损害了整个公司的基础架构。

    DNS服务器-dns.exe负责在安装了DNS角色的Windows服务器上回答DNS查询。


    CVE-2020-1350是DNS.exe在处理畸形DNS Sig消息时,由于对数据包的字段校验不严格,导致了整型溢出。DNS SIG 消息中包含对DNS记录集合的数字签名,DNS记录集合就是一种名字相同,或者类型相同的DNS消息集合。


    漏洞类型:整数溢出导致基于堆的缓冲区溢出


    攻击流程


    aa91b3_adb7b85135e44f38bcca549851f38d68_mv2


    攻击者可以配置一个恶意的域名evildomain.com,该域名的DNS指向的DNS服务器作为本次攻击的目标。


    1. 当客户端查询evildomain.com的DNS记录;
    2. 目标DNS服务器向根域名服务器查询evildomain.com的NS记录;
    3. 根域名服务器告诉目标DNS,evildomain.com的权威DNS服务器是3.3.3.3,并该记录缓存起来;
    4. 客户端向目标服务器查询evildomain.com的Sig记录;
    5. 目标服务器将请求转发给权威DNS服务器;
    6. 权威DNS服务器返回畸形Sig查询结果;

    目标DNS服务器处理SigQuery的畸形消息时,将此消息缓存到自己的记录中,触发漏洞。


    漏洞函数

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

    void Examples(char *in){  
        unsigned short len_p1 = len ( in.p1);  unsigned short len_p2  =len ( in.p2);
        unsigned short len_p  = len_p1 + len_p2;  
        char * buf = (char *)malloc(len_p);  
        memcpy(buf, in.p1, len_p1); }
    

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


    而在dns.exe!SigWireRead函数里面也有类似的溢出漏洞,如图所示第11行:

    aa91b3_ffb865d47c0b4f54925b8c2acb34b036_mv2

    漏洞成因如下:ushort类型,为无符号16位类型。RR_AllocateEx 分配的大小只有16个bit,大小为0~65535,所以只要构造size 大于65535,造成整数溢出,而系统就会分配一个比实际数据量小的堆块,进而造成堆溢出,将整数溢出转化成堆溢出漏洞。


    漏洞利用:

    因此,触发此漏洞所需的全部操作就是让受攻击的DNS服务器向我们查询SIG记录,并使用长签名(长度&>=64KB)对其作出SIG响应。但是,UDP上的DNS的大小限制为512字节(如果服务器支持EDNS0,则为4096字节)。在任何情况下,这都不足以触发漏洞。


    CheckPoint 找到一种方法可以突破上述限制:UDP 响应时将 DNS 头部的 TC 标志置 1,表示被截断,之后客户端便会尝试用 TCP 协议和服务器建立连接,并通过 TCP 协议传递数据。由于消息的前两个字节表示其长度,因此TCP上的DNS中消息的最大大小表示为16位,因此限制为64KB。TCP 协议在 53 端口上可传输长度最大为 65535 的 DNS 数据,这无疑给该漏洞创造了先决条件。如下图:红框中的表示TC置位字段:

    image-20210524201342778


    但是,即使是长度为65,535的消息也不足以触发漏洞,因为消息长度包括报头和原始查询。计算传递给RR_AllocateEx的大小时不考虑此开销。那么,该漏洞如何触发呢?继续往下看,首先了解一下域名编码和域名压缩。


    域名编码

    eg:对于完整的一个域名www.baidu.com,需要14个字节:

    0 1 2 3 4 5 6 7 8 9 10 11 12 13
    3 w w w 5 b a i d u 3 c o m

    img

    可以看到第一个字节 是3,表示之后会有3个字节的字符。


    域名压缩

    域名压缩是 DNS 协议节省空间的一种方式,因为请求中的域名字符会频繁出现在响应包的内,故采用了一种标志+偏移的格式来压缩域名。压缩方式很简单:1 + 1 + offset,即最高 2bit 位为 1 时代表了将采用域名压缩,剩余的组合为偏移值(基地址为 DNS 头),如下图:响应区域中的 Name 字段为 0xc00c,明显是被压缩过的,最高 2bit 位是标志,剩余的 bit 位组合得到的值为 0xc,所以当前 Name 表示的字符串在 DNS 头部向后偏移 0xc 的位置,也就是图中红框中 0x3 开始(0x50 表示 DNS 头起始位置)的域名 bbs.pediy.com

    879291_BK62PXN5P34V87F (1)


    因此,可以将域名压缩的偏移值指向一片可控的区域,而不是指向查询时域名,就完全有可能使得域名的长度大大增加,比如在这里修改为c0 0d,那么表示域名从0x62开始,说明后面有0x62个字节的字符,然后依次。这样就使得源代码第11行中的 sigName.Length 的大小变大。


    注意:DNS name长度最大为0XFF


    所以,可以在不改变原来数据包大小的情况下,将sigLength+sigName.length+0x16 的大小,大于65535,造成整数溢出。分配一个小的空间缓存,然后导致堆溢出。


    POC分析和漏洞复现


    部分poc代码,大体就是构造sig响应消息。具体POC,在这里:https://github.com/maxpl0it/CVE-2020-1350-DoS

    image-20210524204424343


    实验环境:

    Windows Server 2012 R2 192.168.29.138

    KaLi Linux 192.168.29.135

    复现过程:

    1.在windows server 2008 安装dns服务器,并配置静态ip

    2.在kali 中运行脚本,开启ibrokethe.net 域名的恶意服务器,这里的ibrokethe.net 表示恶意域名,也可以代替为其他域名。

    sudo python sigred_dos.py ibrokethe.net 
    

    3.在受害者服务器配置转发器,转发器ip为kali 机的ip。

    image-20210525165703551

    4.在受害者服务器使用nslookup命令查询9.ibrokethe.net:

    nslookup -type=sig 9.ibrokethe.net 127.0.0.1
    

    5.查看kali,发现会发现收到了一个UDP连接,又收到了一个TCP连接。

    image-20210525165901274

    最后要说的是,复现过程采用了Windows DNS服务器的条件转发器,略过了向权威NS查询的步骤,直接向恶意DNS服务器发出查询。

    复现结果:

    抓包结果如下:

    image-20210524205603806

    由于本地无法解析“ibrokethe.net”,因此会以UDP协议向恶意服务器kali发送DNS查询,也就是kali收到的第一个UDP连接。恶意服务器查到了该域名,就向受害者发送响应包,设置TC位,通知受害者采用TCP重发原来的查询请求,并允许返回的响应报文超过512个字节。这是第二个DNS包。然后进行三次握手,受害者再次以TCP协议发送请求,这就是kali收到的TCP连接。之后,恶意服务器就可以利用TCP传输大于64KB的响应包发给受害者,触发漏洞,造成堆溢出。

    然后,就可以进行远程代码执行等操作了。


    可以看到在复现的过程,产生堆溢出,dns.exe进程发生崩溃,从而产生DOS攻击。如下图:

    879291_P2ZBQGC4C59M8U6


    漏洞修复

    临时修复方案

    修改注册表,限制tcp包的长度,TcpReceivePacketSize的值为0xFF00

     HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesDNSParameters 
      DWORD = TcpReceivePacketSize 
      Value = 0xFF00
    

    注意:必须重新启动 DNS 服务才能生效。


    补丁更新

    17

    可看到右图24行,在前面对整数溢出做了个判断,即sigLength+sigName.Length的值要大于等于sigLength

    总结:

    协议方面多多少少都会有我们所疏忽的问题,对于dns.exe开发者,最开始并未考虑到最大可以超过65535,造成溢出,代码并没有问题,造成该漏洞的关键点,主要是因为域名压缩。往往很多个疏忽点,凑到了一起就成了攻击者利用的漏洞。



    参考链接:

    CVE-2020-1350 Windows Server DNS漏洞复现

    漏洞利用剖析:带有CVE-2020-1350 SIGRed的RCE

    CVE-2020-1350: Windows DNS Server蠕虫级远程代码执行漏洞分析
    Windows DNS server从cve-2020-1350到内存泄露

    today is not tomorrow
  • 相关阅读:
    代码整洁之道
    PHP并发IO编程之路
    U盘启动盘的制作--用U盘硬装Windows系统、或是重装Windows系统
    Navicat for MySQL11--使用经验
    Eclipse字体颜色的设置方法
    5.5树和森林
    5.2二叉树
    3.3队列
    3.1栈
    2.3线性表的链式存储结构
  • 原文地址:https://www.cnblogs.com/PsgQ/p/14806195.html
Copyright © 2011-2022 走看看