Talos Vulnerability Report

TALOS-2021-1258

Apple macOS SMB server IOCTL request uninitialized stack variable vulnerability

June 2, 2021
CVE Number

CVE-2021-30712

Summary

A memory corruption vulnerability exists in the SMB Server Apple macOS 11.1. A specially crafted SMB packet can trigger the use of an uninitialized stack variable which can lead to memory corruption and denial of service. This vulnerability can be triggered by sending a malicious packet to the vulnerable server.

Tested Versions

Apple macOS 11.2

Product URLs

https://apple.com

CVSSv3 Score

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

CWE

CWE-824 - Access of Uninitialized Pointer

Details

macOS is a series of proprietary operating systems developed by Apple with macOS 11.2, 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. This vulnerability is present in SMB2 and newer versions of the protocol, more specifically in the IOCTL_REQUEST processing.

While processing the IOCTL_REQUEST packets, function smb2_dispatch_ioctl_request is invoked with supplied packet bytes as its arguments. It in turn calls smb2::extract(uchar *&,uchar * const&,smb2::ioctl_request &,uchar * const&) which extracts different fields from the packet into IOCTL_REQUEST structure. Among other structure fields, values for inputOffset , inputLength and inputBuffer are extracted. Value inputOffset specifies an offset to IOCTL input buffer from the beginning of the packet while inputLength specifies its size. Pointer inputBuffer is supposed to point to the beginning of the buffer, as instructed by inputOffset. However, taking a closer look at the code reveals the following (pseudo-code, relevant parts):

    inputOffset = **packet_pointer | (*(*packet_pointer + 2LL) << 16) | (*(*packet_pointer + 3LL) << 24);      [1]
    lioctl_request->InputOffset = inputOffset;                                                                                                             
    inputCount = *(v12 + 4) | (*(v12 + 6) << 16) | (*(v12 + 7) << 24);                                                                       [2]
    lioctl_request->InputCount = inputCount;

    if ( inputCount <= *packet_end - inputBuffer )                                                                                                     [3]
    {
      result = 1;                                                                                                                                                                 [4]
      if ( inputCount )
      {
        result = 0;                                                                                                                                                              [5]
        if ( inputBuffer >= v4 && inputBuffer <= *packet_end )
        {
          lioctl_request->Buffer = inputBuffer;                                                                                                              [6]
          *v7 += inputCount;
          result = 1;
        }
      }
    }
  }
  return result;

First, at [1], inputOffset is extracted, followed by inputCount at [2]. At [3], a sanity check is made to make sure the buffer doesn’t end outside the packet and a success return value is set at [4]. If, however, inputCount isn’t zero, return value is set to error and another check is performed at [5]. Check at [5] ensures that the inputBuffer isn’t outside the packet and function finally ends.

Seemingly, all the necessary checks are performed to ensure data is sanitized, but there exists a path where return result is set to 1 ( SUCCESS ) and lioctl_request->Buffer pointer remains uninitialized. This pointer actually resides on the calling function’s stack, is uninitialized and contains random data. When it is subsequently used, it can lead to a crash or further memory corruption if contents can be controlled.

One way to make this pointer uninitialized while this function succeed is to set inputCount to be exactly zero. This will make the comparison at [3] to pass which sets the result to 1 (SUCCSESS), but since inputCount is 0, the check at [4] will fail and no further processing occurs, leaving lioctl_request->Buffer uninitialized.

This can be observed in the debugger. To demonstrate control over the uninitialized stack area, the attached PoC constructs a special, compound, SMB command. To be able to control the stack contents, we need to find a function that gives us control of its own stack. A prime candidate for this is smb2_dispatch_tree_connect function and it really is perfect for this task because it has a very large stack frame and directly copies packet data onto its stack. This is the same function that contains a stack buffer overflow that is described in TALOS-2021-1246. In short, the start of the smb2_dispatch_tree_connect can be abbreviated to:

__int64 __fastcall smb2_dispatch_tree_connect(smb_request *smbRequest, unsigned __int8 *treeConnectBuffer, unsigned __int8 *a3)
{
  smb_request *smbRequest_local; // r15
....
  __int16 wcSharePath[1024]; // [rsp+50h] [rbp-840h]
  void *tree_connect_request_extracted[2];



if ( !smb2::extract(&pTreeConnectBuffer, &v22, tree_connect_request_extracted, &smbRequest->smb_packet_start) ){

    return v4;
}

....

memcpy(wcSharePath, v6, (2 * v6_length));

...

In short, the above memcpy call allows us to cleanly initialize 2048 bytes on the stack with arbitrary contents. The supplied PoC does this by sending a TREE_CONNECT request with a very long share patch consisting of bytes with value 0x81.

The reason to construct a compound SMB command that first issues TREE_CONNECT followed by IOCTL_REQUEST is because parts of the compound command will be executed in the same thread, on the same stack, one right after the other. That means that stack frame for function smb2_dispatch_ioctl_request (where our uninitialized pointer resides) begins exactly at the same place as stack frame for smb2_dispatch_tree_connect which we have almost complete control over. This gives us an opportunity to control the contents of the uninitialized pointer and abuse this vulnerability. We can observe this in the debugger by placing breakpoints at important locations. First, we will observe memcpy being called inside smb2_dispatch_tree_connect:

Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = instruction step over
    frame #0: 0x00000001081b7ce0 smbd`smb2_dispatch_tree_connect(smb_request&, unsigned char*, unsigned char*) + 165
smbd`smb2_dispatch_tree_connect:
->  0x1081b7ce0 <+165>: call   0x108220f96               ; symbol stub for: memcpy
    0x1081b7ce5 <+170>: mov    word ptr [rbp + 2*rbx - 0x840], 0x0
    0x1081b7cef <+180>: lea    eax, [rbx - 0x80000000]
    0x1081b7cf5 <+186>: mov    qword ptr [rbp - 0x860], r12
Target 0: (smbd) stopped.
(lldb) register read $rdi
     rdi = 0x000000029cdc94d0
(lldb) ni
Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = instruction step over
    frame #0: 0x00000001081b7ce5 smbd`smb2_dispatch_tree_connect(smb_request&, unsigned char*, unsigned char*) + 170
smbd`smb2_dispatch_tree_connect:
->  0x1081b7ce5 <+170>: mov    word ptr [rbp + 2*rbx - 0x840], 0x0
    0x1081b7cef <+180>: lea    eax, [rbx - 0x80000000]
    0x1081b7cf5 <+186>: mov    qword ptr [rbp - 0x860], r12
    0x1081b7cfc <+193>: mov    dword ptr [rbp - 0x858], eax
Target 0: (smbd) stopped.
(lldb) mem read 0x000000029cdc94d0
0x29cdc94d0: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc94e0: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
(lldb) mem read 0x000000029cdc94d0+1000
0x29cdc98b8: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc98c8: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
(lldb) mem read 0x000000029cdc94d0+1808
0x29cdc9be0: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc9bf0: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
(lldb)

In the above we can see the stack is initialized to 0x81, especially at offset 1808 where our uninitialized pointer will be. Continuing execution till smb2_dispatch_ioctl_request is called and we can examine the stack once again:

Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = instruction step over
    frame #0: 0x00000001081d3d27 smbd`smb2_dispatch_ioctl(smb_request&, unsigned char*, unsigned char*) + 12
smbd`smb2_dispatch_ioctl:
->  0x1081d3d27 <+12>: push   rbx
    0x1081d3d28 <+13>: sub    rsp, 0x218
    0x1081d3d2f <+20>: mov    r14, rdi
    0x1081d3d32 <+23>: mov    rax, qword ptr [rip + 0x74357] ; (void *)0x00007fff92150d00: __stack_chk_guard
Target 0: (smbd) stopped.
(lldb)
Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = instruction step over
    frame #0: 0x00000001081d3d28 smbd`smb2_dispatch_ioctl(smb_request&, unsigned char*, unsigned char*) + 13
smbd`smb2_dispatch_ioctl:
->  0x1081d3d28 <+13>: sub    rsp, 0x218
    0x1081d3d2f <+20>: mov    r14, rdi
    0x1081d3d32 <+23>: mov    rax, qword ptr [rip + 0x74357] ; (void *)0x00007fff92150d00: __stack_chk_guard
    0x1081d3d39 <+30>: mov    rax, qword ptr [rax]
Target 0: (smbd) stopped.
(lldb)
Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = instruction step over
    frame #0: 0x00000001081d3d2f smbd`smb2_dispatch_ioctl(smb_request&, unsigned char*, unsigned char*) + 20
smbd`smb2_dispatch_ioctl:
->  0x1081d3d2f <+20>: mov    r14, rdi
    0x1081d3d32 <+23>: mov    rax, qword ptr [rip + 0x74357] ; (void *)0x00007fff92150d00: __stack_chk_guard
    0x1081d3d39 <+30>: mov    rax, qword ptr [rax]
    0x1081d3d3c <+33>: mov    qword ptr [rbp - 0x30], rax
(lldb) mem read $rsp -c 0x218
0x29cdc9ad0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x29cdc9ae0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x29cdc9af0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0x29cdc9b00: dc 8f 84 a1 02 00 00 00 70 9b dc 9c 02 00 00 00  �..�....p.�.....
0x29cdc9b10: 38 5f 11 92 ff 7f 00 00 06 00 00 00 00 00 00 00  8_..�...........
0x29cdc9b20: 50 9b dc 9c 02 00 00 00 98 ab 6b 6b ff 7f 00 00  P.�......�kk�...
0x29cdc9b30: f4 00 00 00 00 00 00 00 90 cf 9f a0 02 00 00 00  �........�.�....
0x29cdc9b40: 06 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00  ................
0x29cdc9b50: a0 9c dc 9c 02 00 00 00 45 0d 69 6b ff 7f 00 00  �.�.....E.ik�...
0x29cdc9b60: e8 8e 84 a1 02 00 00 00 03 00 00 00 00 00 00 00  �..�............
0x29cdc9b70: e4 06 0d 72 cc 92 0e 80 55 ab 11 28 d4 49 68 cf  �..r�...U�.(�Ih�
0x29cdc9b80: 1d c0 76 8c f5 f8 84 09 2a 03 f3 bc 0f d3 75 06  .�v.��..*.�.�u.
0x29cdc9b90: 78 1a 25 08 01 00 00 00 00 00 00 00 00 00 00 00  x.%.............
0x29cdc9ba0: d0 fe 54 9f 02 00 00 00 01 00 00 00 00 00 00 00  ��T.............
0x29cdc9bb0: f0 9b dc 9c 02 00 00 00 59 19 ba 68 ff 7f 00 00  �.�.....Y.�h�...
0x29cdc9bc0: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc9bd0: 00 00 00 00 00 00 00 00 28 1b 25 08 01 00 00 00  ........(.%.....
0x29cdc9be0: 81 81 81 81 81 81 81 81 78 1a 25 08 01 00 00 00  ........x.%.....
0x29cdc9bf0: 90 9c dc 9c 02 00 00 00 68 11 ba 68 ff 7f 00 00  ..�.....h.�h�...
0x29cdc9c00: 00 20 2b 08 01 00 00 00 78 1a 25 08 01 00 00 00  . +.....x.%.....
0x29cdc9c10: d0 fe 54 9f 02 00 00 00 10 1a 25 08 01 00 00 00  ��T.......%.....
0x29cdc9c20: 00 00 00 00 00 00 00 00 d0 fe 54 9f 02 00 00 00  ........��T.....
0x29cdc9c30: 00 00 00 00 00 00 00 00 0f 28 1f 08 01 00 00 00  .........(......
0x29cdc9c40: 14 00 00 00 0c 00 00 00 3d 00 00 00 be 02 00 00  ........=...�...
0x29cdc9c50: bd 02 00 00 bf 02 00 00 64 00 00 00 ff 7f 00 00  �...�...d...�...
0x29cdc9c60: 57 dd 1e 08 81 81 81 81 81 81 81 81 00 00 00 00  W�..............
0x29cdc9c70: 10 00 00 00 00 00 00 00 99 27 1f 08 01 00 00 00  .........'......
0x29cdc9c80: 00 00 00 00 00 00 00 00 76 00 9f 6f 1a b9 85 74  ........v..o.�.t
0x29cdc9c90: d0 fe 54 9f 02 00 00 00 50 00 00 00 00 00 00 00  ��T.....P.......
0x29cdc9ca0: 00 00 00 00 00 00 00 00 10 00 84 a1 02 00 00 00  ...........�....
0x29cdc9cb0: f0 9c dc 9c 02 00 00 00 ee 0e 1f 08 01 00 00 00  �.�.....�.......
0x29cdc9cc0: 00 00 00 00 00 00 00 00 78 9d dc 9c 02 00 00 00  ........x.�.....
0x29cdc9cd0: 50 00 00 00 00 00 00 00 10 00 84 a1 02 00 00 00  P..........�....
0x29cdc9ce0: 00 00 00 00 00 00 00 00
(lldb) memory read 0x29cdc94d0 -c 256
0x29cdc94d0: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc94e0: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc94f0: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc9500: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc9510: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc9520: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc9530: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc9540: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc9550: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc9560: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc9570: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc9580: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc9590: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc95a0: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc95b0: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
0x29cdc95c0: 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81  ................
(lldb)

First, we step into smb2_dispatch_ioctl until sub rsp, 0x218 where stack is allocated and then we look up contents of the stack. Command mem read $rsp -c 0x218 reveals that the stack contents are no longer neatly initialized as before, but still contain series of 0x81 bytes here and there. Second, memory read shows that most of the stack is still intact. Continuing execution leads to smb2::extract(uchar *&,uchar * const&,smb2::ioctl_request &,uchar * const&) which will extract the IOCTL_REQUEST structure and leave lioctl_request->Buffer uninitialized (but initialized by previous packet to 0x8181818181818181). Since IOCTL code in the PoC is set to FSCTL_VALIDATE_NEGOTIATE_INFO , next first time lioctl_request->Buffer is reused will be during a call to smb2::extract(unsigned char *&, unsigned char * const&, smb2::validate_negotiate_info_request &, unsigned char * const&). We can observe the following in the debugger:

Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = breakpoint 2.24
    frame #0: 0x00000001081f6b9c smbd`smb2::extract(unsigned char*&, unsigned char* const&, smb2::validate_negotiate_info_request&, unsigned char* const&)
smbd`smb2::extract:
->  0x1081f6b9c <+0>: push   rbp
    0x1081f6b9d <+1>: mov    rbp, rsp
    0x1081f6ba0 <+4>: mov    rax, qword ptr [rdi]
    0x1081f6ba3 <+7>: mov    rcx, qword ptr [rsi]
Target 0: (smbd) stopped.
(lldb) memory read $rdi
0x29cdc9be0: 81 81 81 81 81 81 81 81 50 cf 9f a0 02 00 00 00  ........P�.�....
0x29cdc9bf0: 90 9c dc 9c 02 00 00 00 68 11 ba 68 ff 7f 00 00  ..�.....h.�h�...
(lldb) memory read $rsi
0x29cdc9ba0: f3 cf 9f a0 02 00 00 00 39 00 00 00 04 02 14 00  ��.�....9.......
0x29cdc9bb0: 1f 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00  ................
(lldb) ni
Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = instruction step over
    frame #0: 0x00000001081f6b9d smbd`smb2::extract(unsigned char*&, unsigned char* const&, smb2::validate_negotiate_info_request&, unsigned char* const&) + 1
smbd`smb2::extract:
->  0x1081f6b9d <+1>:  mov    rbp, rsp
    0x1081f6ba0 <+4>:  mov    rax, qword ptr [rdi]
    0x1081f6ba3 <+7>:  mov    rcx, qword ptr [rsi]
    0x1081f6ba6 <+10>: sub    rcx, rax
Target 0: (smbd) stopped.
(lldb)
Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = instruction step over
    frame #0: 0x00000001081f6ba0 smbd`smb2::extract(unsigned char*&, unsigned char* const&, smb2::validate_negotiate_info_request&, unsigned char* const&) + 4
smbd`smb2::extract:
->  0x1081f6ba0 <+4>:  mov    rax, qword ptr [rdi]
    0x1081f6ba3 <+7>:  mov    rcx, qword ptr [rsi]
    0x1081f6ba6 <+10>: sub    rcx, rax
    0x1081f6ba9 <+13>: cmp    rcx, 0x18
Target 0: (smbd) stopped.
(lldb)
Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = instruction step over
    frame #0: 0x00000001081f6ba3 smbd`smb2::extract(unsigned char*&, unsigned char* const&, smb2::validate_negotiate_info_request&, unsigned char* const&) + 7
smbd`smb2::extract:
->  0x1081f6ba3 <+7>:  mov    rcx, qword ptr [rsi]
    0x1081f6ba6 <+10>: sub    rcx, rax
    0x1081f6ba9 <+13>: cmp    rcx, 0x18
    0x1081f6bad <+17>: jl     0x1081f6c11               ; <+117>
Target 0: (smbd) stopped.
(lldb)
Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = instruction step over
    frame #0: 0x00000001081f6ba6 smbd`smb2::extract(unsigned char*&, unsigned char* const&, smb2::validate_negotiate_info_request&, unsigned char* const&) + 10
smbd`smb2::extract:
->  0x1081f6ba6 <+10>: sub    rcx, rax
    0x1081f6ba9 <+13>: cmp    rcx, 0x18
    0x1081f6bad <+17>: jl     0x1081f6c11               ; <+117>
    0x1081f6baf <+19>: mov    r8d, dword ptr [rax]
Target 0: (smbd) stopped.
(lldb) memory read $rax
error: memory read failed for 0x8181818181818000
(lldb) ni
Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = instruction step over
    frame #0: 0x00000001081f6ba9 smbd`smb2::extract(unsigned char*&, unsigned char* const&, smb2::validate_negotiate_info_request&, unsigned char* const&) + 13
smbd`smb2::extract:
->  0x1081f6ba9 <+13>: cmp    rcx, 0x18
    0x1081f6bad <+17>: jl     0x1081f6c11               ; <+117>
    0x1081f6baf <+19>: mov    r8d, dword ptr [rax]
    0x1081f6bb2 <+22>: lea    rcx, [rax + 0x4]
Target 0: (smbd) stopped.
(lldb)
Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = instruction step over
    frame #0: 0x00000001081f6bad smbd`smb2::extract(unsigned char*&, unsigned char* const&, smb2::validate_negotiate_info_request&, unsigned char* const&) + 17
smbd`smb2::extract:
->  0x1081f6bad <+17>: jl     0x1081f6c11               ; <+117>
    0x1081f6baf <+19>: mov    r8d, dword ptr [rax]
    0x1081f6bb2 <+22>: lea    rcx, [rax + 0x4]
    0x1081f6bb6 <+26>: mov    qword ptr [rdi], rcx
Target 0: (smbd) stopped.
(lldb)
Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = instruction step over
    frame #0: 0x00000001081f6baf smbd`smb2::extract(unsigned char*&, unsigned char* const&, smb2::validate_negotiate_info_request&, unsigned char* const&) + 19
smbd`smb2::extract:
->  0x1081f6baf <+19>: mov    r8d, dword ptr [rax]
    0x1081f6bb2 <+22>: lea    rcx, [rax + 0x4]
    0x1081f6bb6 <+26>: mov    qword ptr [rdi], rcx
    0x1081f6bb9 <+29>: mov    dword ptr [rdx], r8d
Target 0: (smbd) stopped.
(lldb)
Process 52252 stopped
* thread #24, queue = 'com.apple.root.default-qos', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x00000001081f6baf smbd`smb2::extract(unsigned char*&, unsigned char* const&, smb2::validate_negotiate_info_request&, unsigned char* const&) + 19
smbd`smb2::extract:
->  0x1081f6baf <+19>: mov    r8d, dword ptr [rax]
    0x1081f6bb2 <+22>: lea    rcx, [rax + 0x4]
    0x1081f6bb6 <+26>: mov    qword ptr [rdi], rcx
    0x1081f6bb9 <+29>: mov    dword ptr [rdx], r8d
Target 0: (smbd) stopped.
(lldb) register read rax
     rax = 0x8181818181818181
(lldb) bt
* thread #24, queue = 'com.apple.root.default-qos', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
  * frame #0: 0x00000001081f6baf smbd`smb2::extract(unsigned char*&, unsigned char* const&, smb2::validate_negotiate_info_request&, unsigned char* const&) + 19
    frame #1: 0x00000001081d406b smbd`smb2_dispatch_ioctl(smb_request&, unsigned char*, unsigned char*) + 848
    frame #2: 0x00000001081d9abe smbd`smb2_dispatch_compound(smb_transport*, unsigned char*, unsigned char*) + 1358
    frame #3: 0x00000001081c51a1 smbd`invocation function for block in smb_transport::dispatch() + 54
    frame #4: 0x00007fff6b8186c4 libdispatch.dylib`_dispatch_call_block_and_release + 12
    frame #5: 0x00007fff6b819658 libdispatch.dylib`_dispatch_client_callout + 8
    frame #6: 0x00007fff6b827aa8 libdispatch.dylib`_dispatch_root_queue_drain + 663
    frame #7: 0x00007fff6b828097 libdispatch.dylib`_dispatch_worker_thread2 + 92
    frame #8: 0x00007fff6ba739f7 libsystem_pthread.dylib`_pthread_wqthread + 220
    frame #9: 0x00007fff6ba72b77 libsystem_pthread.dylib`start_wqthread + 15

Above shows that the uninitialized value indeed ends up being used as lioctl_request->Buffer and ultimately leads to invalid memory read causing a crash and denial of service.

It should be noted that above demonstrated crash is unreliable and extremely sensitive to configuration , users, available shares, but most importantly binary version. The ability to cause a crash or otherwise abuse this issue is largely dependent on stack variable ordering and therefore could be changed by the compiler. Nevertheless, even if not causing a crash, triggering the vulnerability can still be observed in the debugger by following uninitialized stack values from smb2_dispatch_tree_connect to smb2_dispatch_ioctl_request. Additionally, differences in memory layout can cause the rest of the code to take a different path which can be observed over the network.

Timeline

2021-03-02 - Vendor Disclosure
2021-05-25 - Vendor Patched
2021-06-01 - Public Release

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.