CVE: CVE-2021-30845

Tested Versions:

  • macOS BigSur 11.0 - 11.2.3

Product URL(s):

Description of the vulnerability

smbfs is a kext driver which handles SMB connection between the user and SMB Server. This vulnerability occurs in smbfs, which allows an attacker to leak kernel memory to achieve further exploitation.

The vulnerability exists in the smbfs_mount function, which can be triggered via mount syscall. mount syscall will take data from user input and pass it to smbfs_mount. smbfs_mount does not require any privilege to mount and smb to a folder.

The structure of smbfs_mount_args is defined by the following code:

struct smb_mount_args {
	int32_t		version;
	int32_t		dev;
	int32_t		altflags;
	int32_t		KernelLogLevel;
	uid_t		uid;
	gid_t 		gid;
	mode_t 		file_mode;
	mode_t 		dir_mode;
	uint32_t	path_len;
	int32_t		unique_id_len;
	char		path[MAXPATHLEN] __attribute((aligned(8))); /* The starting path they want used for the mount */
	char		url_fromname[MAXPATHLEN] __attribute((aligned(8))); /* The from name is the url used to mount the volume. */
	unsigned char	unique_id[SMB_MAX_UNIQUE_ID] __attribute((aligned(8))); /* A set of bytes that uniquely identifies this volume */
	char		volume_name[MAXPATHLEN] __attribute((aligned(8))); /* The starting path they want used for the mount */
	uint64_t	ioc_reserved __attribute((aligned(8))); /* Force correct size always */
	int32_t         ip_QoS;
	int32_t		max_resp_timeout;
	int32_t		dir_cache_async_cnt;
	int32_t		dir_cache_max;
	int32_t		dir_cache_min;
	int32_t         max_dirs_cached;
	int32_t         max_dir_entries_cached;
	uint32_t        max_read_size;
	uint32_t        max_write_size;
	/* Snapshot time support */
	char		snapshot_time[32] __attribute((aligned(8)));
	time_t 		snapshot_local_time;
}

There are two interesting fields unique_id_len and unique_id[SMB_MAX_UNIQUE_ID] was defined in this struct. If we look carefully at the smbfs_mount function, these fields are handled by the following code snippet:

	/* Now get the mounted volumes unique id */
	smp->sm_args.unique_id_len = args->unique_id_len;
	SMB_MALLOC(smp->sm_args.unique_id, unsigned char *, smp->sm_args.unique_id_len, 
		   M_SMBFSDATA, M_WAITOK);
	if (smp->sm_args.unique_id) {
		bcopy(args->unique_id, smp->sm_args.unique_id, smp->sm_args.unique_id_len);
	} else {
		smp->sm_args.unique_id_len = 0;
	}

As we can see, the code doesn’t check if args->unique_id_len is larger than args->unique_id. If an attacker crafts a smbfs_mount_args with args->unique_id_len that is larger than the actual size of args->unique_id, it could lead to memory Out-of-Bounds Read.

Proof Of Concept

  1. Setup an samba server following these information:
  • SMB User: peter
  • SMB Password: share123
  • SMB Share: share
  1. Compiling PoC with:
  • clang smbfs_mount_oob_read.c -o smbfs_mount_oob_read -framework CoreFoundation -framework NetFS
  • Attach virtual machine into lldb
  • Run ./smbfs_mount_oob_read <samba ip>
  • lldb will output like below:
-----------------------------------------------------------------------------------------------------------------------[regs]
  RAX: 0x0000001D15970FD8  RBX: 0xFFFFFFB06AB6F000  RBP: 0xFFFFFFB06A363630  RSP: 0xFFFFFFB06A363618  o d I t s z a p c
  RDI: 0xFFFFFFB06ABB4FD8  RSI: 0xFFFFFF9355244000  RDX: 0x0000000041414141  RCX: 0x00000000413CE169
  RIP: 0xFFFFFF800FB01082  R8 : 0xFFFFFF868B3DDBF0  R9 : 0x0000000000000001  R10: 0x0000000000000000
  R11: 0x00000000FFFFFFFF  R12: 0x0000000000000016  R13: 0xFFFFFF93551FD800  R14: 0xFFFFFF868BDAC438
  R15: 0xFFFFFF869CB5E460
  CS : 0008  GS : 6AB60000  FS : FFFF0000
-----------------------------------------------------------------------------------------------------------------------[code]
bcopy @ /Users/peternguyen/Build/binaries/kernel.development:
->  0xffffff800fb01082 (0xffffff8000101082): f3 a4        rep   movsb	byte ptr es:[rdi], byte ptr [rsi]
    0xffffff800fb01084 (0xffffff8000101084): c3           ret
    0xffffff800fb01085 (0xffffff8000101085): 48 01 cf     add   rdi, rcx
    0xffffff800fb01088 (0xffffff8000101088): 48 01 ce     add   rsi, rcx
    0xffffff800fb0108b (0xffffff800010108b): 48 ff cf     dec   rdi
    0xffffff800fb0108e (0xffffff800010108e): 48 ff ce     dec   rsi
    0xffffff800fb01091 (0xffffff8000101091): 48 83 e1 07  and   rcx, 0x7
    0xffffff800fb01095 (0xffffff8000101095): fd           std
-----------------------------------------------------------------------------------------------------------------------------

Process 1 stopped
* thread #1, stop reason = EXC_BAD_ACCESS (code=2, address=0x55244000)
    frame #0: 0xffffff800fb01082 kernel.development`bcopy + 18
Target 0: (kernel.development) stopped.
(lldbinit) bt
* thread #1, stop reason = EXC_BAD_ACCESS (code=2, address=0x55244000)
  * frame #0: 0xffffff800fb01082 kernel.development`bcopy + 18
    frame #1: 0xffffff800feaa844 kernel.development`memmove(dst=0xffffffb06ab6f000, src=<unavailable>, ulen=<unavailable>) at loose_ends.c:923:2 [opt]
    frame #2: 0xffffff7fa9ed4d1c smbfs`smbfs_mount + 2326
    frame #3: 0xffffff8010022d8f kernel.development`mount_common(fstypename=<unavailable>, pvp=0xffffff8689626a00, vp=<unavailable>, cnp=0xffffffb000000001, fsmountargs=140732792811600, flags=24, internal_flags=0, labelstr=0x0000000000000000, kernelmount=0, ctx=0xffffff868bdac438) at vfs_syscalls.c:1228:11 [opt]
    frame #4: 0xffffff8010024601 kernel.development`__mac_mount(p=<unavailable>, uap=<unavailable>, retval=<unavailable>) at vfs_syscalls.c:599:10 [opt]
    frame #5: 0xffffff801002419e kernel.development`mount(p=<unavailable>, uap=<unavailable>, retval=<unavailable>) at vfs_syscalls.c:359:9 [opt]
    frame #6: 0xffffff80104bf3f1 kernel.development`unix_syscall64(state=0xffffff868aadd800) at systemcalls.c:412:10 [opt]
    frame #7: 0xffffff800fcc11f6 kernel.development`hndl_unix_scall64 + 22

Suggested Mitigations

  • Update to macOS latest if it is available.

Timeline

  • 2021-06-18 Vendor disclosure
  • 2021-09-13 Vendor patched