CVE-2021-1878
An information disclosure vulnerability exists in the SMB Server Apple macOS 11.1. A specially crafted SMB packet can trigger an integer overflow, leading to information disclosure, cryptographic check bypass and denial of service. This vulnerability can be triggered by sending a malicious packet to the vulnerable server.
Apple macOS 11.1
7.1 - CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:H/A:H
CWE-190 - Integer Overflow or Wraparound
macOS is a series of proprietary operating systems developed by Apple with macOS 11.1, with Big Sur being the latest.
Server Message Block (SMB) is a network file sharing protocol widely used in Windows network environments and macOS contains a proprietary implementation of both server and client components. SMB is often used in office and enterprise environments for file and printer sharing.
Three distinct versions and multiple dialects of SMB protocol are supported by macOS’ SMB server. Since SMB2 revision of the SMB protocol, a support for compound SMB messages has existed. This means that a single packet can contain multiple, chained, messages that need to be parsed, verified and processed in sequence. This is facilitated by NextCommand
field in SMB2 header which specifies the offset from the begging of current packet to the next one in the compounded request or response. If the request/response packet isn’t compound, or is last in sequence, this field must be 0. Additionally , both SMB2 and SMB3 have mandatory message signing, facilitated by different HMAC algorithms. SMB2 dialects use SHA256 based HMAC, while SMB3 dialects use AES-128-CMAC.
There exists a vulnerability in the way macOS SMB server processes SMB3 compounded packets. If the NextCommand
field has a non-zero value, a function smb2_dispatch_compound
is invoked. Signing is required for SMB3 packets, and while processing the compound requests, following reconstructed code is executed:
smb_packet = v62 - 64;
smb_packet_len = v63 - (v62 - 64);
if ( *(v9 + 5) )
smb_packet_len = *(v9 + 5);
if ( *(v74 + 136) == 3 )
{
if ( !v68 && !signing_mechanism::smb3_verify(v36, smb_packet, smb_packet_len) )
{
if ( (platform::log::global_level(v36) & 0x1F) == 31 )
{
v36 = platform::log::logger_for_level(31LL);
(*(*v36 + 16LL))(
v36,
31LL,
"/AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/smbx/smbx-499.100.1/src/cmd/smbd/smb2_dispatch.cpp",
160LL,
"smb2_verify_message",
"%s: Verify failed for msgid: %llu\n",
"smb2_verify_message",
*(v9 + 3));
}
In the above code, method smb3_verify
is invoked with first argument pointing to beginning of compound packet and second to apparent packet length. Value for smb_packet_len
ends up being set to the value specified in NextCommand
. No validation is performed in smb3_verify
which can be seen in the following excerpt:
CCAESCmacUpdate(mac_ctx, smb_packet, 0x30LL);
CCAESCmacUpdate(mac_ctx, &v8, 0x10LL);
CCAESCmacUpdate(mac_ctx, smb_packet + 0x40, smb_packet_len - 64);
CCAESCmacFinal(mac_ctx, &v11);
LODWORD(smb_packet_len) = timingsafe_bcmp(&v9, &v11, 16LL);
Method smb3_verify
is supposed to HMAC the contents of the packet (without the signature itself) and compare it to the signature embedded in the packet. The vulnerability lies in the 3rd call to CCAESCmacUpdate
where length of 64 (standard SMB2 header size) is subtracted from supplied smb_packet_len
value. Since smb_packet_len
comes directly from NextCommand
field in the packet, it is under full attacker control which can lead to integer overflow if the value is less than 64, or directly lead to out of bounds memory access if the value is larger than the actual received packet size.
This can be observed in the debugger:
* thread #2, queue = 'com.apple.root.default-qos', stop reason = EXC_BAD_ACCESS (code=1, address=0x100685000)
frame #0: 0x00007fff71d77f81 libcorecrypto.dylib`vng_aes_encrypt_cbc_hw + 145
libcorecrypto.dylib`vng_aes_encrypt_cbc_hw:
-> 0x7fff71d77f81 <+145>: movups xmm1, xmmword ptr [rbx]
0x7fff71d77f84 <+148>: pxor xmm0, xmm2
0x7fff71d77f88 <+152>: pxor xmm0, xmm1
0x7fff71d77f8c <+156>: aesenc xmm0, xmm3
Target 0: (smbd) stopped.
(lldb) register read
General Purpose Registers:
rax = 0x00000000000000a0
rbx = 0x0000000100685000
rcx = 0x000070000de1eb70
rdx = 0x0000000000000010
rdi = 0x0000000100684f80
rsi = 0x00000001003130fc
rbp = 0x000070000de1eb20
rsp = 0x000070000de1eb00
r8 = 0x0000000100313008
r9 = 0x000070000de1ebd0
r10 = 0x00000000fffffc00
r11 = 0x0000000000000d70
r12 = 0x0000000100312fc0
r13 = 0x0000000000000008
r14 = 0x000070000de1ebf0
r15 = 0x0000000100313008
rip = 0x00007fff71d77f81 libcorecrypto.dylib`vng_aes_encrypt_cbc_hw + 145
rflags = 0x0000000000010202
cs = 0x000000000000002b
fs = 0x0000000000000000
gs = 0x0000000000000000
(lldb) bt
* thread #2, queue = 'com.apple.root.default-qos', stop reason = EXC_BAD_ACCESS (code=1, address=0x100685000)
* frame #0: 0x00007fff71d77f81 libcorecrypto.dylib`vng_aes_encrypt_cbc_hw + 145
frame #1: 0x00007fff71da6b98 libcorecrypto.dylib`cbc_wrapper_aesni + 38
frame #2: 0x00007fff71d7cd15 libcorecrypto.dylib`cccmac_update + 282
frame #3: 0x000000010001dafe smbd`signing_mechanism::smb3_verify(unsigned char*, unsigned long) + 144
frame #4: 0x0000000100024c6c smbd`smb2_dispatch_compound(smb_transport*, unsigned char*, unsigned char*) + 1788
frame #5: 0x00000001000101a1 smbd`invocation function for block in smb_transport::dispatch() + 54
frame #6: 0x00007fff71f046c4 libdispatch.dylib`_dispatch_call_block_and_release + 12
frame #7: 0x00007fff71f05658 libdispatch.dylib`_dispatch_client_callout + 8
frame #8: 0x00007fff71f13aa8 libdispatch.dylib`_dispatch_root_queue_drain + 663
frame #9: 0x00007fff71f14097 libdispatch.dylib`_dispatch_worker_thread2 + 92
frame #10: 0x00007fff7215f9f7 libsystem_pthread.dylib`_pthread_wqthread + 220
frame #11: 0x00007fff7215eb77 libsystem_pthread.dylib`start_wqthread + 15
Above crash is due to an integer overflow that results in length argument to CCAESCmacUpdate
being very large which ends up reading invalid memory.
When SMB2 protocol dialect is negotiated by the client a vulnerability of similar nature can be triggered in different part of code. When processing compound SMB2 requests in smb2_dispatch_compound
following code is eventually invoked:
else if ( !signing_mechanism::smb2_verify(v36, smb_packet, smb_packet_len) )
{
if ( (platform::log::global_level(v36) & 0x1F) == 31 )
{
v36 = platform::log::logger_for_level(31LL);
(*(*v36 + 16LL))(
v36,
31LL,
"/AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/smbx/smbx-499.100.1/src/cmd/smbd/smb2_dispatch.cpp",
166LL,
"smb2_verify_message",
"%s: Verify failed for msgid: %llu\n",
"smb2_verify_message",
*(v9 + 3));
}
Method smb2_verify
is invoked to perform SMB2 specific signature validation. Relevant excerpt from smb2_verify
is as follows:
CCHmacInit(v8, 2LL, *(this + 2), *(this + 3) - *(this + 2));
v13 = 0LL;
v12 = 0LL;
v9 = 0LL;
v4 = *(a2 + 7);
v10 = *(a2 + 6);
v11 = v4;
CCHmacUpdate(v8, a2, 48LL);
CCHmacUpdate(v8, &v9, 16LL);
v5 = a3 - 64;
CCHmacUpdate(v8, a2 + 64, v5);
CCHmacFinal(v8, &v12);
Same lack of length checks makes it possible to overflow the integer v5
which can again lead to out of bounds memory access. This time, from call to CCHmacUpdate
. This can result in the following crash:
* thread #4, queue = 'com.apple.root.default-qos', stop reason = EXC_BAD_ACCESS (code=1, address=0x100685000)
frame #0: 0x00007fff71d6a2e8 libcorecrypto.dylib`AccelerateCrypto_SHA256_compress_AVX2 + 2344
libcorecrypto.dylib`AccelerateCrypto_SHA256_compress_AVX2:
-> 0x7fff71d6a2e8 <+2344>: vmovdqu ymm0, ymmword ptr [rdx]
0x7fff71d6a2ec <+2348>: mov esi, r8d
0x7fff71d6a2ef <+2351>: rorx eax, r12d, 0x19
0x7fff71d6a2f5 <+2357>: rorx ecx, r12d, 0xb
Target 0: (smbd) stopped.
(lldb) bt
* thread #4, queue = 'com.apple.root.default-qos', stop reason = EXC_BAD_ACCESS (code=1, address=0x100685000)
* frame #0: 0x00007fff71d6a2e8 libcorecrypto.dylib`AccelerateCrypto_SHA256_compress_AVX2 + 2344
frame #1: 0x00007fff71d67522 libcorecrypto.dylib`ccdigest_update + 292
frame #2: 0x000000010001d8c4 smbd`signing_mechanism::smb2_verify(unsigned char*, unsigned long) + 168
frame #3: 0x0000000100024d22 smbd`smb2_dispatch_compound(smb_transport*, unsigned char*, unsigned char*) + 1970
frame #4: 0x00000001000101a1 smbd`invocation function for block in smb_transport::dispatch() + 54
frame #5: 0x00007fff71f046c4 libdispatch.dylib`_dispatch_call_block_and_release + 12
frame #6: 0x00007fff71f05658 libdispatch.dylib`_dispatch_client_callout + 8
frame #7: 0x00007fff71f13aa8 libdispatch.dylib`_dispatch_root_queue_drain + 663
frame #8: 0x00007fff71f14097 libdispatch.dylib`_dispatch_worker_thread2 + 92
frame #9: 0x00007fff7215f9f7 libsystem_pthread.dylib`_pthread_wqthread + 220
frame #10: 0x00007fff7215eb77 libsystem_pthread.dylib`start_wqthread + 15
(lldb) register read
General Purpose Registers:
rax = 0x00007fff71de9f40 libcorecrypto.dylib`sha256_K + 224
rbx = 0x0000000045b02fa5
rcx = 0x00000000ce06ca55
rdx = 0x0000000100684ff0
rdi = 0x0000000032800520
rsi = 0x00000000339f2762
rbp = 0x000070000f875ad0
rsp = 0x000070000f875920
r8 = 0x0000000070a4a061
r9 = 0x000000003bae7f22
r10 = 0x00000000219fa763
r11 = 0x00000000b69105fc
r12 = 0x000000006721f416
r13 = 0x00000000bf0eb75b
r14 = 0x00000000025ff0d1
r15 = 0x00000000c8df1999
rip = 0x00007fff71d6a2e8 libcorecrypto.dylib`AccelerateCrypto_SHA256_compress_AVX2 + 2344
rflags = 0x0000000000010202
cs = 0x000000000000002b
fs = 0x0000000000000000
gs = 0x0000000000000000
Above crash is due to invalid memory access stemming from a CCHmacUpdate
call with abnormally large size because of an integer overflow.
By carefully controlling the NextCommand
value, an arbitrary number of out of bounds bytes can be included in HMAC calculation which can either result in HMAC verification as valid or invalid. This can potentially be used to construct an information disclosure oracle by sending a sequence of specially crafted packets. Additionally, when value of NextCommand
is equal to 64, in both cases (SMB2 and SMB3) , the calculated length for second HMAC Update function call will be 0 meaning that no bytes from the rest of the packet would be used in signature verification. This compromises message integrity and could potentially be abused in man in the middle scenarios to inject arbitrary content into requests and replies between client and server.
2021-02-01 - Vendor Disclosure
2021-05-06 - Vendor Patched
2021-05-19 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.