CVE-2020-10005
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 smb2::extract
method:
__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&)
Calling smb::extract
will extract the utf16 path string as well as its length. Shortly following that is a memcpy
call:
__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
Notice that 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.