A remote code execution vulnerability exists in the SMB Server Apple macOS 10.15.7. A specially crafted SMB packet can trigger a stack-based buffer overflow, which can lead to arbitrary code execution and denial of service. This vulnerability can be triggered by sending a malicious packet to the vulnerable server.
Apple macOS Catalina 10.15.7
8.5 - CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H
CWE-121 - Stack-based Buffer Overflow
macOS is a series of proprietary operating systems developed by Apple. Among other services, macOS contains a proprietary implementation of an SMB server to support network file sharing.
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
TREE_CONNECT request processing.
TREE_CONNECT request is used to connect to a specified SMB share and has a relatively simple structure consisting of structure size, flags, path offset, path length and a utf16 string specifying a path. When processing the
TREE_CONNECT request, function
smb2_dispatch_tree_connect is invoked. First, parts of the
TREE_CONNECT structure are extracted by calling the appropriate
__text:0000000100002C5E lea rdi, [rbp+pTreeConnectBuffer] ; this __text:0000000100002C65 mov [rdi], rsi __text:0000000100002C68 lea rsi, [rbp+var_868] __text:0000000100002C6F mov [rsi], rdx __text:0000000100002C72 lea rcx, [r15+90h] __text:0000000100002C79 lea rdx, [rbp+tree_connect_request_extracted] __text:0000000100002C7D call smb2::extract(uchar *&,uchar * const&,smb2::tree_connect_request &,uchar * const&)
smb::extract will extract the utf16 path string as well as its length. Shortly following that is a
__text:0000000100002CCF cmovs rsi, rax ; void * __text:0000000100002CD3 lea edx, [rbx+rbx] ; size_t __text:0000000100002CD6 lea r12, [rbp+wcSharePath] __text:0000000100002CDD mov rdi, r12 ; void * __text:0000000100002CE0 call _memcpy
From the above, we can observe that destination for the
memcpy call is a stack buffer
rbp+wcSharePath, source is previously extracted pointer to path string and length is the specified length from the
TREE_CONNECT structure. Since the local stack buffer
wcSharePath is located in the stack and of static size this can constitute a straightforward stack buffer overflow. Indeed, looking up stack layout of this function reveals that
wcSharePath is 2048 bytes. Therefore, a specially crafted
TREE_CONNECT request packet with a very long share path utf16 string can result in a buffer overflow. This can be observed in a debugger:
Process 32047 launched: '/usr/sbin/smbd' (x86_64) Thu Jan 28 17:51:28 2021 is_darwin_xpc_error [darwin_xpc.cpp:43] is_darwin_xpc_error: XPC: listener, ipc error: Connection invalid Thu Jan 28 17:51:31 2021 smb2_dispatch_negotiate [negotiate.cpp:595] smb2_dispatch_negotiate: Client requires signing. 2021-01-28 17:51:31.766732-0600 smbd[32047:32558705] [User Defaults] All kCFPreferencesCurrentUser domains in this process will be volatile, because homeDirPath starts with /var/empty Thu Jan 28 17:51:31 2021 reply_smb2_negotiate [negotiate.cpp:292] reply_smb2_negotiate: SIGN: security_mode: Enabled: True, Required: True Thu Jan 28 17:51:31 2021 smb2_dispatch_session_setup [session_setup.cpp:538] smb2_dispatch_session_setup: Client requires signing, session_id: 0x3c18c5f800000001 Thu Jan 28 17:51:32 2021 smb2_dispatch_session_setup [session_setup.cpp:538] smb2_dispatch_session_setup: Client requires signing, session_id: 0x3c18c5f800000001 Thu Jan 28 17:51:32 2021 check_account [pam_mechanism.cpp:62] pam_end returned : 0 Thu Jan 28 17:51:32 2021 smb2_dispatch_session_setup [session_setup.cpp:607] smb2_dispatch_session_setup: New session established, session_id: 0x3c18c5f800000001 Process 32047 stopped * thread #3, queue = 'com.apple.root.default-qos', stop reason = breakpoint 1.1 frame #0: 0x0000000100002c3b smbd`smb2_dispatch_tree_connect(smb_request&, unsigned char*, unsigned char*) smbd`smb2_dispatch_tree_connect: -> 0x100002c3b <+0>: push rbp 0x100002c3c <+1>: mov rbp, rsp 0x100002c3f <+4>: push r15 0x100002c41 <+6>: push r14 (lldb) mem read $rsi 0x100813840: 09 00 00 00 48 00 ff 0a 41 41 41 41 41 41 41 41 ....H.�.AAAAAAAA 0x100813850: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA (lldb)
First, a breakpoint is set at
smb2_dispatch_tree_connect to observe
TREE_CONNECT buffer from our crafted packet. Continuing execution till
memcpy call lets us observe the parameters:
Process 32047 stopped * thread #3, queue = 'com.apple.root.default-qos', stop reason = instruction step over frame #0: 0x0000000100002ce0 smbd`smb2_dispatch_tree_connect(smb_request&, unsigned char*, unsigned char*) + 165 smbd`smb2_dispatch_tree_connect: -> 0x100002ce0 <+165>: call 0x10006bf96 ; symbol stub for: memcpy 0x100002ce5 <+170>: mov word ptr [rbp + 2*rbx - 0x840], 0x0 0x100002cef <+180>: lea eax, [rbx - 0x80000000] 0x100002cf5 <+186>: mov qword ptr [rbp - 0x860], r12 Target 0: (smbd) stopped. (lldb) mem read $rdi 0x7000066b24d0: 00 25 6b 06 00 70 00 00 c7 70 fe 71 ff 7f 00 00 .%k..p..�p�q�... 0x7000066b24e0: 00 00 00 00 00 00 00 00 c0 de 83 98 ff 7f 00 00 ........��..�... (lldb) mem read $rsi 0x100813848: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 0x100813858: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA (lldb) mem read $rdx error: memory read failed for 0xa00 (lldb) register read rdx rdx = 0x0000000000000a28
rdi is an address on the stack,
rsi points to a path buffer and
rdx (length) matches buffer size from
TREE_CONNECT request packet. Continuing execution forward results in:
* thread #3, queue = 'com.apple.root.default-qos', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT) frame #0: 0x0000000100025bcd smbd`smb2_schedule_error(smb_request&, unsigned int) + 78 smbd`smb2_schedule_error: -> 0x100025bcd <+78>: mov dword ptr [rax], esi 0x100025bcf <+80>: lea r14, [rdi + 0xb8] 0x100025bd6 <+87>: mov rcx, qword ptr [rdi + 0xb8] 0x100025bdd <+94>: mov rax, qword ptr [rdi + 0xc0] Target 0: (smbd) stopped. (lldb) register read General Purpose Registers: rax = 0x4141414141414149 rbx = 0x0000000000000514 rcx = 0x00007000066b2e40 rdx = 0x00000000007ed200 rdi = 0x00007000066b2d90 rsi = 0x00000000c00000be rbp = 0x00007000066b2470 rsp = 0x00007000066b2420 r8 = 0x0000000000008009 r9 = 0x0000000000000000 r10 = 0x0000000000000071 r11 = 0x0000000101000000 r12 = 0x00007000066b24d0 r13 = 0x00007000066b2dd0 r14 = 0x0000000000000001 r15 = 0x00007000066b2d90 rip = 0x0000000100025bcd smbd`smb2_schedule_error(smb_request&, unsigned int) + 78 rflags = 0x0000000000010206 cs = 0x000000000000002b fs = 0x0000000000000000 gs = 0x0000000000000000 (lldb) bt * thread #3, queue = 'com.apple.root.default-qos', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT) * frame #0: 0x0000000100025bcd smbd`smb2_schedule_error(smb_request&, unsigned int) + 78 frame #1: 0x0000000100002ee7 smbd`smb2_dispatch_tree_connect(smb_request&, unsigned char*, unsigned char*) + 684 (lldb)
The above shows a write access violation when trying to write to address pointed to by
rax which is clearly under control. Depending on the stack structures that were overwritten, the crash could happen at a different place. Additionally, while this particular function has stack cookie check enabled, there are number of other locals, and those of other stack frames, stack variables that could be overwritten which could lead to further memory corruption and ultimately arbitrary code execution.
2021-02-01 - Vendor Disclosure
2021-05-25 - Vendor Patched
2021-06-02 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.