报告编号:B6-2020-101901 报告来源:360-CERT 报告作者:360-CERT 更新日期:2020-10-19 0x01 前言1.1 环境搭建 (1)攻击机环境:Ubuntu 20.04 安装scapy sudo apt install python-pip3
sudo pip3 install scapy
(2)受害机环境:Windows 10 1909 x64 tcpip.sys 驱动版本:10.0.18362.476 (3)双机调试Windows 驱动: https://blog.csdn.net/qq_21000273/article/details/52027708 断点: bp tcpip!Ipv6pUpdateRDNSS
bp tcpip!Ipv6pHandleRouterAdvertisement
bp tcpip!Ipv6pHandleRouterAdvertisement+0xae4cc
bp tcpip!Ipv6pHandleRouterAdvertisement+0xae4db
bp tcpip!Ipv6pUpdateRDNSS+0x99
bp tcpip!Ipv6pUpdateRDNSS+0xca
1.2 背景知识 涉及的包类型: type:24 Route Information Option
type:25 Recursive DNS Server Option
type:134 IMCPv6 Router Advertisement
涉及的结构: _MDL结构 typedef __struct_bcount (Size ) struct _MDL {
struct _MDL *Next ;
CSHORT Size ;
CSHORT MdlFlags ;
struct _EPROCESS *Process ;
PVOID MappedSystemVa ;
PVOID StartVa ;
ULONG ByteCount ;
ULONG ByteOffset ;
} MDL , *PMDL ;
_NET_BUFFER 结构 typedef struct _NET_BUFFER {
union {
struct {
PNET_BUFFER Next;
PMDL CurrentMdl;
ULONG CurrentMdlOffset;
union {
ULONG DataLength;
SIZE_T stDataLength;
};
PMDL MdlChain;
ULONG DataOffset;
};
SLIST_HEADER Link;
NET_BUFFER_HEADER NetBufferHeader;
};
USHORT ChecksumBias;
USHORT Reserved;
NDIS_HANDLE NdisPoolHandle;
PVOID NdisReserved[2];
PVOID ProtocolReserved[6];
PVOID MiniportReserved[4];
NDIS_PHYSICAL_ADDRESS DataPhysicalAddress;
union {
PNET_BUFFER_SHARED_MEMORY SharedMemoryInfo;
PSCATTER_GATHER_LIST ScatterGatherList;
};
} NET_BUFFER, *PNET_BUFFER;
相关函数: NdisGetDataBuffer 函数 PVOID NdisGetDataBuffer(
PNET_BUFFER NetBuffer,
ULONG BytesNeeded,
PVOID Storage,
UINT AlignMultiple,
UINT AlignOffset
);
NetBuffer:指向NET_BUFFER 结构的指针 BytesNeeded:请求的连续数据的字节数 Storage:指向缓冲区的指针,如果调用者未提供缓冲区,则为NULL。缓冲区的大小必须大于或等于BytesNeeded中指定的字节数。如果此值为非NULL,并且请求的数据不连续,则NDIS将请求的数据将复制到Storage指向的地址。 Windows通过Ipv6pHandleRouterAdvertisement 函数处理 IPv6 路由器通告数据,在该函数中调用 NdisGetDataBuffer 函数从 NET_BUFFER 结构中访问连续或不连续的数据,通过 NET_BUFFER ->CurrentMdlOffset 字段来记录要访问数据起始地址相对于_MDL->MappedSystemVa 的偏移。 0x02 漏洞分析2.1 漏洞背景 2020年10月14日,360CERT监测发现 Microsoft 发布了 TCP/IP远程代码执行漏洞 的风险通告,该漏洞是由于Windows TCP/IP堆栈 在处理IMCPv6 Router Advertisement(路由通告)数据包时存在漏洞,远程攻击者通过构造特制的ICMPv6 Router Advertisement(路由通告)数据包 ,并将其发送到远程Windows主机上,可造成远程BSOD,漏洞编号为CVE-2020-16898。 2.2 漏洞成因 根据rfc5006 描述,RDNSS包的length应为奇数,而当攻击者构造的RDNSS包的Length为偶数时,Windows TCP/IP 在检查包过程中会根据Length来获取每个包的偏移,遍历解析,导致对 Addresses of IPv6 Recursive DNS Servers 和下一个 RDNSS 选项的边界解析错误,从而绕过验证,将攻击者伪造的option包进行解析,造成栈溢出,从而导致系统崩溃。 RDNSS Option 数据包格式如下: 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。 漏洞点存在于tcpip.sys -> Ipv6pHandleRouterAdvertisement 函数 漏洞调用链为:Icmpv6ReceiveDatagrams -> Ipv6pHandleRouterAdvertisement -> Ipv6pUpdateRDNSS Ipv6pHandleRouterAdvertisement 函数存在两个循环,第一个循环遍历所有headers,做一些基本的验证,如length的大小,第二个循环用于处理包,并且该阶段不再验证,两个循环的伪代码如下: // 循环1
while ( 1 )
{
……
v28 = (KIRQL *)NdisGetDataBuffer(v9, 2u, v182, 1u, 0);
v27 = v9->DataLength;
actual_length_bytes = 8 * v28[1];
……
switch ( v25 )
{
case 0x18u: // case 0x18 (ICMPv6NDOptRouteInfo)
……
if ( actual_length_bytes > 0x18u
|| (v144 = *((_BYTE *)NdisGetDataBuffer(v9, actual_length_bytes, v220, 1u, 0) + 2), v144 > 0x80u)
|| v144 > 0x40u && actual_length_bytes < 0x18u // <-----【1】验证实际字节数,不能大于0x18
|| v144 && actual_length_bytes < 0x10u )
{
*a3 = 24;
goto LABEL_275;
}
break;
case 0x19u: // case 0x19 (ICMPv6NDOptRDNSS) // <-----【2】
if ( (*(_BYTE *)(v11 + 404) & 0x40) != 0 && actual_length_bytes < 0x18u )
*a3 = 25;
break;
}
……
if ( actual_length_bytes )
{
v31 = actual_length_bytes + v9->CurrentMdlOffset;
if ( v31 >= *(_DWORD *)(v9->Link.Region + 0x28) )
{
NdisAdvanceNetBufferDataStart(v9, actual_length_bytes, 0, 0i64);// <---actual_length_bytes=4*8=0x20
}
else
{
v9->DataOffset += actual_length_bytes;
v9->DataLength -= actual_length_bytes;
v9->CurrentMdlOffset = v31; // 更新CurrentMdlOffset
}
}
v21 += actual_length_bytes;
}
……
// 循环2
while ( 1 )
{
……
if ( *v75 == 0x18 ) // case 0x18 (ICMPv6NDOptRouteInfo)
{
……
v153 = (unsigned __int8 *)NdisGetDataBuffer(NetBuffer_1, actual_option, Storage_1, 1u, 0); // <--- 【3】
v225 = _mm_load_si128((const __m128i *)&_xmm);
v174 = v225.m128i_u32[((unsigned __int64)v153[3] >> 3) & 3];
……
}
if (
本文出处: https://www.toutiao.com/i6885187554979611148/
|