0x05Cve-2020-16898 复现

Cve-2020-16898 复现

参考:

https://blog.csdn.net/hsj_csdn/article/details/109138162

https://www.secpulse.com/archives/143665.html

https://cert.360.cn/report/detail?id=771d8ddc2d703071d5761b6a2b139793

0x1 复现过程:

0x01 准备靶机环境

下一个win10的镜像,然后开启vmware的IPV6功能(编辑-虚拟网络编辑器-NAT设置),查看靶机IPV6地址

image-20201109173742012

0x02 攻击机运行脚本(攻击机需要能与靶机进行通信

image-20201109173451963

0x03 攻击成功,靶机蓝屏重启

image-20201109173816856

0x2 漏洞部分分析:

0x01 抓包,可以看到进行了多次DNS查询

image-20201109173845156

0x3 原理分析:

相关知识

1、Microsoft Windows中的TCP/IP功能在内核级别运行,并由驱动程序tcpip.sys提供。该驱动程序处理所有传入和传出的TCP/IP通信信息,包括解析从网络接口接收到的数据包,以及解释此数据并将其传递给更高级别的组件。

2、RDNSS Option:一个用于向IPv6主机传送RDNSS信息的IPv6的RA option

3、Recursive DNS Server (RDNSS):递归DNS服务器,提供递归DNS解析服务的服务器,用于将域名转换为IP地址或解析成RFC1034和RFC1035中定义的PTR记录。

4、IPv6 Router Advertisment (RA) options,也称为DNS RA options,允许IPv6的路由器向IPv6的主机广播DNS Recursive Server Address(DNS递归路由器地址)列表和DNS Search List(DNS搜索列表),其主要用途为在IPv6的主机上进行DNS名称解析以及域后缀的处理。该信息使用现有ND message(例如RA)作为载体。IPv6主机可以通过RA消息配置一个或多个RDNSS的IPv6地址。当主机接收到RA消息中的DNS的options时,其处理过程如下:1、首先检查Lengh字段的合法性:是否大于等于最小值3,以及是否满足(Length - 1) % 2 == 0;2、对于RDNSS option,还会检查Address字段是否为一个单播地址;3、如果以上验证通过,则主机应按顺序将选项的值复制到DNS存储库和解析器存储库中。 否则,主机必须丢弃这些选项。

5、GS机制:

GS机制分三个步骤:计算随机种子 –> canary写入栈帧 –> GS校验

1、程序启动时,读取**.data的第一个DWORD**作为基数,然后和各种元素(时间戳,进程ID,线程ID,计数器等等)进行XOR加密

2、然后将加密后的种子再次写入**.data的第一个DWORD**

3、函数在执行前,把加密后的种子取出,与当前esp进行异或计算,结果存入EBP的前面

4、函数主体正常执行。

5、函数返回前(retn前一点),把cookie取出与esp异或计算后,调用__security_check_cookie函数进行检查,与.data节里的种子进行比较,

如果校验通过,则返回原函数继续执行;如果校验失败,则程序终止。

6、通过RFC8106的标准中对RDNSS option结构的定义(https://tools.ietf.org/pdf/rfc8106.pdf)

image-20201109173914793

Type: 占8-bit,RDNSS 的类型为25

Length:8-bit无符号整数,单位长度为8个字节,所以Type, Length, Reserved, Lifetime一共占8个字节,一个单位长度,而一个IPv6地址占16个字节,两个单位长度,所以Length的最小值为3,且为奇数。

Reserved:保留字段

Lifetime:32-bit无符号整数,存活周期。

Addresses of IPv6 Recursive DNS Servers:保存RNDSS的IPv6地址,每个占16个字节,地址的数量会影响Length字段,number=(Length - 1) / 2。每增加一个地址,Length加2。

原理

由于Windows TCP/IP堆栈在处理选项类型为25(0x19,递归DNS服务器选项),当伪造长度字段值为偶数的ICMPv6的路由广播数据包发送给目标主机时,Windows TCP/IP 在检查包过程中会根据Length来获取每个包的偏移,遍历解析,导致对 Addresses of IPv6 Recursive DNS Servers 和下一个 RDNSS 选项的边界解析错误,从而绕过验证,将攻击者伪造的option包进行解析,造成栈溢出,从而导致系统崩溃。

分析

1、从靶机中导出蓝屏日志文件dmp(在windows\minidump中),使用windbg来分析

遇到的问题:不能正常加载符号表

解决:因为时效问题,搜索了很多方法都无效,最终使用科学上网自动加载符号表解决

2、kv语句查看(Kv,命令用来显示当前线程的堆栈,显示FPO和调用约定)

image-20201109173936731

可以看到GS机制的Security Cookie校验失败,也就是说该值被覆盖掉了。

分析出现的关键函数tcpip!Ipv6pHandleRouterAdvertisement()。

3、使用IDA64反编译驱动程序tcpip.sys(win10 1909版本)

找到函数tcpip!Ipv6pHandleRouterAdvertisement()

image-20201109173949807

函数一开始就写出了GS机制:

image-20201109174004821

查看调用的函数,发现Ipv6pUpdateRDNSS()

image-20201109174021022

进入漏洞函数Ipv6pUpdateRDNSS():

image-20201109174048451

通过查询微软官方文档理解出现的几个函数

我们可以理解下面的循环是,确认Address的数量,对每个Address进行处理,分配合适的内存。

而漏洞原因就在于v41处未对length奇偶进行判断

根据RFC8106的标准,Length字段的值应该满足最小为3的奇数的情况。那么当我们提供一个偶数Length值如4时,Ipv6pUpdateRDNSS()函数应该增加32字节(4*8)的缓冲区。但是此代码中,可以看到堆栈在内部以16字节为增量进行计数,当v41为1时,分配的缓冲区是v9的8字节加上v19的16字节,最终为24字节,就会导致缓冲区溢出。

注意:如果从NetBuffer请求的数据是连续的,则会将数据存放在NDIS提供的地址,这样无法造成溢出。所以需要从NetBuffer请求的数据不是连续的,才会将数据存放在Storage上。数据非连续的实现是通过fragmentation(碎片化)处理,将Router Advertisement包通过scapy的fragment6函数拆分成多个IPv6 fragments进行发送。

查看复现中抓取的Router Advertisement流量包,可以看到其中length为偶数8

image-20201109174105003