Talos Vulnerability Report


Apple macOS SMB server create file request uninitialized memory disclosure

June 2, 2021
CVE Number



A use of uninitialized data vulnerability exists in the SMB Server Apple macOS 11.2. A specially crafted SMB packet can cause uninitialized data to end up in server reply which can leak sensitive information. This vulnerability can be triggered by sending a malicious packet to the vulnerable server.

Tested Versions

Apple macOS 11.2

Product URLs


CVSSv3 Score

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


CWE-201 - Information Exposure Through Sent Data


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 CREATE_REQUEST processing which is used to create files on remote shares.

SMB CREATE_REQUEST structure as sent by the client contains a number of fields related to file creation including file name and zero or more SMB2_CREATE_CONTEXT buffers. Protocol specification enumerates a number of unique SMB2_CREATE_CONTEXT buffer types, one of which is SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST whose identifying value is 0x4d784163 (or “MxAc”) and which is used to request file access information to be included in server’s reply.

Specification surrounding SMB2_CREATE_CONTEXT buffers states that each of these, in request and replies both, must start on a 8 byte aligned boundary. If we examine the code responsible for creating a reply to MxAc request we can see the following in smb2::insert(smb2::create_context_insert const&, uint8_t*&, uint8_t* const&, uint8_t* const&) function:

100042161      int64_t rax_5 = *output_buffer                                                                 [0]
100042164      int64_t rcx_10 = *arg3
100042167      r12_1 = 0
100042172      if (rax_5 u<= rcx_10 && rax_5 - output_buffer_start s>= 0)
10004217b          uint64_t rcx_11 = zx.q(rcx_10.d - rax_5.d)
10004217d          uint64_t name_length = zx.q(arg1->name_length)
100042185          if (rcx_11.d u< name_length.d)
100042185              goto label_1000420f3
10004218b          if (name_length.w != 0)
100042191              char* name_pointer = arg1->name_pointer
100042195              if (name_pointer != 0)
1000421a4                  _memmove(rax_5, name_pointer, name_length, rcx_11)                                 [1]
1000421a9                  rdi = rdi
1000421ad                  int64_t r12_2 = name_length + *output_buffer
1000421b0                  *output_buffer = r12_2
1000421b3                  rax_5 = r12_2
1000421b6          rax_5 = rax_5 + 7
1000421ba          int64_t rax_6 = rax_5 & -8                                                                 [2]
1000421be          *output_buffer = rax_6
1000421c1          int16_t rax_7 = rax_6.w - output_buffer_start.w
1000421c4          *(rdi + 2) = rax_7.b
1000421c7          *(rdi + 3) = rax_7:1.b
1000421ca          int64_t rdi_2 = *output_buffer
1000421cd          int64_t rax_8 = *arg3
1000421d0          r12_1 = 0
1000421d3          if (rdi_2 u<= rax_8)
1000421df              void* rcx_13 = rdi_2 - output_buffer_start
1000421df              if (rdi_2 - output_buffer_start s>= 0)
1000421ea                  uint64_t data_length = zx.q(arg1->data_length)
1000421f1                  if (data_length.d u> rax_8.d - rdi_2.d)
1000421f1                      goto label_1000420f3
1000421f7                  r12_1.b = 1
1000421fa                  if (data_length.d != 0)
100042203                      int64_t data_pointer = arg1->data_pointer
100042207                      if (data_pointer != 0)
100042213                          _memmove(rdi_2, data_pointer, data_length, rcx_13)                          [3]
100042218                          *output_buffer = *output_buffer + data_length

At [0] in the above code, output buffer construction is started and at [1] a SMB2_CREATE_CONTEXT name is copied to the start of the buffer. Then, at [2], an 8 byte alignment check is performed and output buffer pointer is adjusted to accommodate the rest of the data. At [3], the rest of SMB2_CREATE_CONTEXT response data is copied into the structure.

When an allocation for this output buffer is made no initialization is performed, the memory contents of the previous allocation are still present. Depending on the structure sizes and alignment, by the time this call to smb2::insert is done, the output buffer can contain from one up to 4 bytes of uninitialized data which is then sent to the client. This is demonstrated by the attached proof of concept exploit that crafts a CREATE_REQUEST SMB2 packet with a large number of SMB2_CREATE_CONTEXT structures, each of which gets a reply. Create context of type MxAc is used in the PoC because its size makes the alignment work in our favour and each SMB2_CREATE_CONTEXT response structure in the reply will contain 4 bytes of random heap data.

$python smb_create_file_poc.py

('sending', 20630)
00000005 ('\x00\x00\x00\x05') 03000203 ('\x03\x00\x02\x03') ff7f0000 ('\xff\x7f\x00\x00') cb876863 ('\xcb\x87hc')
ff7f0000 ('\xff\x7f\x00\x00') ff7f0000 ('\xff\x7f\x00\x00') 9b7f0000 ('\x9b\x7f\x00\x00') ff7f0000 ('\xff\x7f\x00\x00')
ff7f0000 ('\xff\x7f\x00\x00') ff7f0000 ('\xff\x7f\x00\x00') 9b7f0000 ('\x9b\x7f\x00\x00') 9b7f0000 ('\x9b\x7f\x00\x00')
ff7f0000 ('\xff\x7f\x00\x00') 9b7f0000 ('\x9b\x7f\x00\x00') c773a242 ('\xc7s\xa2B') 9b7f0000 ('\x9b\x7f\x00\x00')
c773a242 ('\xc7s\xa2B') 9b7f0000 ('\x9b\x7f\x00\x00') 30d10102 ('0\xd1\x01\x02') 30d10102 ('0\xd1\x01\x02')

In our testing, and as demonstrated by the PoC above, leaked data can contain some user and machine specific data as well as heap or code pointer fragments which in turn could be used to determine heap and image bases and defeat ASLR and aid in further service exploitation.


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


Discovered by Aleksandar Nikolic of Cisco Talos.