Talos Vulnerability Report

TALOS-2021-1237

Apple macOS SMB server signature verification information disclosure vulnerability

May 19, 2021
CVE Number

CVE-2021-1878

Summary

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.

Tested Versions

Apple macOS 11.1

Product URLs

https://apple.com

CVSSv3 Score

7.1 - CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:H/A:H

CWE

CWE-190 - Integer Overflow or Wraparound

Details

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.

SMB3 compound packet signature verification heap overflow

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.

SMB2 compound packet signature verification heap overflow

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.

Timeline

2021-02-01 - Vendor Disclosure
2021-05-06 - Vendor Patched
2021-05-19 - Public Release

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.