漏洞描述
3月11日,微软发布了针对115个CVE漏洞的补丁更新,其中最引人关注的是一个针对Smb服务器/客户端的一个内存破坏漏洞,CVE编号为CVE-2020-0796,成功利用此漏洞的攻击者可以在目标SMB服务器或SMB客户端上执行任意代码,此漏洞属于严重危害的零接触蠕虫级远程代码执行漏洞,此漏洞也被称为 SMBGhost或“Coronablue。
3月12日,微软正式发布漏洞通告和相关补丁:
https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0796。
客户端漏洞发生在mrxsmb.sys中,服务端漏洞发生在srv2.sys中,由于Smb没有正确处理压缩的数据包,在解压数据包的时候使用客户端传过来的长度进行解压时,并没有检查长度是否合法导致出现整数溢出。
Smb 协议就是2017年 WannaCry 和 NotPetya 勒索软件蠕虫传播的协议,利用针对SMB服务器的漏洞,未经身份验证的攻击者可以将特制数据包发送到目标SMBv3服务器;若要利用针对SMB客户端的漏洞,未经身份验证的攻击者将需要配置恶意的SMBv3服务器,并诱使用户连接到该服务器上。
静态分析
负责Smb服务器传输的文件位于C:\Windows\System32\drivers\srv2.sys,srv2.sys存在漏洞的版本是18362或18363,可以直接在windows10客户机中提取,此漏洞影响的是SMB v3.1.1,这是Smb协议的最新版本。
用IDA加载srv2.sys后分析,通过MS符号服务器加载srv2.sys符号文件,解析出一些函数:
Srv2DecompressMessageAsync
Srv2DecompressData
Smb2GetHonorCompressionAlgOrder
Smb2SelectCompressionAlgorithm
Smb2ValidateCompressionCapabilities
SMB v3中支持数据压缩,如果SMB Header中的ProtocolId是0xFC, 'S', 'M', 'B',说明数据是压缩的,这时smb会调用解压函数来处理,smb会调用srv2!Srv2ReceiveHandler函数接收smb数据包,并根据ProtocoII调用对应的处理函数。
__int64 __fastcall Srv2ReceiveHandler(__int64 a1, void *Src, __int64 a3, unsigned int a4, unsigned int *a5, char *Srca, struct _SLIST_ENTRY *a7, _QWORD *a8){
......
Size = a4;
v8 = a4;
v73 = 0;
v9 = (char *)Src;
v10 = 2;
v11 = -1i64;
*a8 = 0i64;
if ( WPP_GLOBAL_Control != (PDEVICE_OBJECT)&WPP_GLOBAL_Control
&& HIDWORD(WPP_GLOBAL_Control->Timer) & 0x200
&& BYTE1(WPP_GLOBAL_Control->Timer) >= 2u )
{
v70 = Src;
WPP_SF_DqDq(WPP_GLOBAL_Control->AttachedDevice);
}
if ( v8 4 )
goto LABEL_15;
switch ( *(_DWORD *)Srca )
{
case 0x424D53FE:
if ( v8 0x40 || *((_WORD *)Srca + 6) >= 0x13u )
goto LABEL_15;
goto LABEL_21;
case 0x424D53FC:
v12 = v8 0x10;
break;
case 0x424D53FD:
v12 = v8 0x34;
break;
default:
if ( *(_DWORD *)Srca != 1112364031 || v8 0x21 || Srca[4] != 114 )
goto LABEL_15;
LABEL_21:
v15 = __readgsdword(0x1A4u);
v16 = *(_DWORD *)qword_1C0046110 - 1;
if ( (unsigned int)v15 + 1 v16 = v15 + 1;
v17 = v16;
v18 = *((_QWORD *)qword_1C0046110 + 4);
v19 = *(_QWORD *)(v18 + 8 * v17);
if ( !*(_BYTE *)(v19 + 112) )
PplpLazyInitializeLookasideList(qword_1C0046110, *(_QWORD *)(v18 + 8 * v17));
++*(_DWORD *)(v19 + 20);
v20 = ExpInterlockedPopEntrySList((PSLIST_HEADER)v19);
}
产生整数溢出漏洞的代码在srv2!Srv2DecompressData函数中:
__int64 __fastcall Srv2DecompressData(__int64 a1)
{
__int64 v1; // rdi
__int64 v2; // rax
__m128i v3; // xmm0
__m128i v4; // xmm0
unsigned int v5; // ebp
__int64 v7; // rax
__int64 v8; // rbx
int v9; // eax
__m128i MaxCount; // [rsp+30h] [rbp-28h]
int v11; // [rsp+60h] [rbp+8h]
v11 = 0;
v1 = a1;
v2 = *(_QWORD *)(a1 + 240);
if ( *(_DWORD *)(v2 + 36) 0x10u )
return 0xC000009B;
v3 = *(__m128i *)*(_QWORD *)(v2 + 24);
MaxCount = v3;
v4 = _mm_srli_si128(v3, 8);
v5 = *(_DWORD *)(*(_QWORD *)(*(_QWORD *)(a1 + 80) + 496i64) + 140i64);
if ( v5 != v4.m128i_u16[0] )
return 0xC00000BB;
v7 = SrvNetAllocateBuffer((unsigned int)(MaxCount.m128i_i32[1] + v4.m128i_i32[1]), 0i64);
v8 = v7;
if ( !v7 )
return 0xC000009A;
if ( (int)SmbCompressionDecompress(
v5,
*(_QWORD *)(*(_QWORD *)(v1 + 240) + 24i64) + MaxCount.m128i_u32[3] + 16i64,
(unsigned int)(*(_DWORD *)(*(_QWORD *)(v1 + 240) + 36i64) - MaxCount.m128i_i32[3] - 16),
MaxCount.m128i_u32[3] + *(_QWORD *)(v7 + 24),
MaxCount.m128i_i32[1],
&v11) 0
|| (v9 = v11, v11 != MaxCount.m128i_i32[1]) )
{
SrvNetFreeBuffer(v8);
return 0xC000090B;
}
if ( MaxCount.m128i_i32[3] )
{
memmove(
*(void **)(v8 + 24),
(const void *)(*(_QWORD *)(*(_QWORD *)(v1 &