2021年2月份的微软补丁中,修复了一组Windows TCP/IP协议栈的漏洞,包括两个远程代码执行(RCE)漏洞(CVE-2021-24074、CVE-2021-24094)和一个拒绝服务(DOS)漏洞(CVE-2021-24086)。由于漏洞出现在TCP/IP协议栈中,危险较大,CVSS给出的评分分为9.8/8.4,7.5/6.5。其中,CVE-2021-24086和CVE-2021-24094出现在Windows IPv6堆栈模块中,CVE-2021-24074出现在Windows Ipv4堆栈处理模块中。本篇分析文章重点针对CVE-2021-24074进行分析。
1.初步分析
首先查找该漏洞的相关信息,发现除了微软官方漏洞公告信息之外,基本没有漏洞的相关信息,且该漏洞是微软自己的安全团队发现的。公告信息中有关于漏洞的缓解方法:
netsh int ipv4 set global sourceroutingbehavior=drop
Ipv4 源路由被认为是不安全的,并且在 Windows 中默认为阻止状态;但是,系统将处理该请求并返回拒绝该请求的 ICMP 消息。变通办法将导致系统完全丢弃这些请求,而不进行任何处理。
从这可以初步推断,漏洞应该是出现在IPv4源路由相关的处理函数中。
2.补丁对比
接下来开始施展补丁对比大法,对比漏洞修复前后TCP/IP处理模块tcpip.sys,bindiff的分析结果如下图:
从补丁对比的情况,可以看出不同的函数有一些,由于CVE-2021-24074漏洞是IPv4的问题,所以筛选出与IPv4相关的函数,首先筛出改动最大的Ipv4pRessembleDatagram函数,对比其反汇编代码,找出不同如下图:
对比可以看出,出现了这一部分的密集不同:
1月份函数:
memmove(v26 + 5, *(const void **)(a2 + 96), *(unsigned __int16 *)(a2 + 104));
Ipv4pFillPacketChecksum(v15);
*(_DWORD *)(v15 + 92) ^= ((unsigned __int8)IppReassembleEcnField(a2) ^ (unsigned __int8)*(_DWORD *)(v15 + 92)) & 3;
*(_BYTE *)(v16 + 176) |= 8u;
*(_QWORD *)(v16 + 8) = v14;
IppRemoveFromReassemblySet((PKSPIN_LOCK)(v8 + 20304));
for ( i = *(__int64 **)(a2 + 112); i; i = (__int64 *)*i )
NetioExpandNetBuffer(v15, i + 2, *((unsigned int *)i + 14));
2月份函数:
memmove(v28 + 5, *(const void **)(a2 + 96), *(unsigned __int16 *)(a2 + 104));
Ipv4pFillPacketChecksum(v16);
*(_DWORD *)(v16 + 92) ^= (*(_DWORD *)(v16 + 92) ^ IppReassembleEcnField(a2)) & 3;
*(_DWORD *)(v17 + 308) = *(_DWORD *)(a2 + 216);
v18 = *(_BYTE *)(a2 + 220);
*(_BYTE *)(v17 + 176) |= 8u;
*(_BYTE *)(v17 + 304) = v18;
*(_QWORD *)(v17 + 8) = v15;
*(_QWORD *)(v17 + 272) = v28;
if ( *(_QWORD *)(v17 + 240) != v17 + 224 )
*(_QWORD *)(v17 + 240) = v28 + 4;
IppRemoveFromReassemblySet((PKSPIN_LOCK)(v9 + 20304));
for ( i = *(__int64 **)(a2 + 112); i; i = (__int64 *)*i )
NetioExpandNetBuffer(v16, i + 2, *((unsigned int *)i + 14));
可以看出,这是一个数据结构的赋值问题,并且存在一个if判断,这个地方应该是漏洞的关键部分,Ipv4pRessembleDatagram函数是将两个分片数据包进行重组,上述区别的部分是将分片数据通过数据结构链连起来之前的初始化和判断。
目前初步判断漏洞的原因是由于分片重组的时候,正常ipv4协议分片要求所有分片option要一致,但是出漏洞的情况应该是分片的时候option不一致,导致在重组的时候出现问题。漏洞构造的恶意的分片,第一片option是130(安全选项option),第二片option是131(松散路由LSRR option)或137(严格路由SSRR option)。在重组的时候,是开辟了一段空间,然后里面存储的都是链表一样的东西,通过链表链到真正的数据上。
开辟空间之后,首先存储的是ipv4 header,之后存储ipv4 option, 之后存储data(上述for循环部分)。重组过程中,只进行一次option的分析,于是始终用的是第一个option 130,但是当option 131到来时,还是会处理该option,即源路由option,这会导致在重组链完成之后,调用到IppReceiveHeaderBatch函数,如下图:
该函数中会间接调用Ipv4pReceiveRoutingHeader函数,该函数会查找数据包的下一跳时,它将OOB读取该地址(作为DWORD)并对其进行一些检查。如果这些检查成功,则IP堆栈将通过复制原始数据包,然后在记录路由中写入其自己的IP地址,尝试将数据包路由到其下一跳。因为此写操作依赖于与以前相同的无效指针值,所以这实际上是OOB写操作(超出了新分配的数据包的末尾)。该OOB写入的内容是目标机器的IP地址,表示为DWORD(因此不受攻击者的控制)。
3.小结
微软博客中描述该漏洞很复杂,因此很难短期内实现漏洞利用。目前正进一步分析,后续会继续更新该漏洞的更多细节。
4.参考文献
1.https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-24074
2.https://msrc-blog.microsoft.com/2021/02/09/multiple-security-updates-affecting-tcp-ip/
3.https://tools.ietf.org/html/rfc791