Talos Vulnerability Report

TALOS-2022-1659

Apple DCERPC presentation result list out of bounds memory access

July 13, 2023
CVE Number

CVE-2023-23539

SUMMARY

An out of bounds memory access vulnerability exists in the processing of packets containing presentation result lists in DCERPC library as used in Apple macOS 12.6.1. A specially-crafted network packet can lead to out of bounds memory access which can lead to denial of service. A remote attacker can send a network request to trigger this vulnerability. A local attacker can write to a local socket to trigger this vulnerability.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

Apple macOS 12.6.1

PRODUCT URLS

macOS - https://apple.com

CVSSv3 SCORE

5.9 - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

CWE

CWE-823 - Use of Out-of-range Pointer Offset

DETAILS

DCERPC is a remote procedure call protocol that is the basis for RPC functionality on Windows. DCERPC framework on macOS implements this protocol and enables interoperability of Windows network services on macOS. For example, it is used on top of SMB, through which support for Active Directory is implemented. DCERPC framework is employed by the rpcsvchost binary, which opens a number of UNIX sockets that expose different RPC functionality.

A client can send a plethora of different packet types (RPC_C_CN_PKT_REQUEST, RPC_C_CN_PKT_BIND, etc.). For each packet type, the library parses the data received from the network and calculates different pointers for relevant structures inside the packet data.

#define RPC_CN_PKT_SIZEOF_BIND_ACK_HDR (RPC_CN_PKT_SIZEOF_COMMON_HDR + 2 + 2 + 4)
...	
case RPC_C_CN_PKT_BIND_ACK:
case RPC_C_CN_PKT_ALTER_CONTEXT_RESP:
	   ...
        secadrp = (rpc_cn_port_any_t *)
            ((unsigned8 *)(pkt_p) + RPC_CN_PKT_SIZEOF_BIND_ACK_HDR);          [1]
        presp = unpack_port_any (secadrp, drepp, end_of_pkt, &st);            [2]

In the case of RPC_C_CN_PKT_BIND_ACK and RPC_C_CN_PKT_ALTER_CONTEXT_RESP, we see the code at [1] calculating secadrp as a pointer inside network-received data that reside in pkt_p. Then the code proceeds to call unpack_port_any() at [2] returning the presp pointer.

INTERNAL rpc_cn_pres_result_list_p_t unpack_port_any
(
    rpc_cn_port_any_t       *port_any_p,
    unsigned8               *drepp,
    unsigned8               *end_of_pkt,
    unsigned32              *st
)
{
	 union
    {                                            /* a "proper" union to shut up lint */
		 unsigned8 *string;                      /* a string pointer */
		 rpc_cn_pres_result_list_p_t rtn;        /* a return value */
    } ptr;
    ...	
    ptr.string = port_any_p->s;        /* init our string pointer */         [3]
	...
	ptr.string += port_any_p->length;                                        [4]
    return (ptr.rtn);

Here port_any_p is secadrp from [1] above. At [3] a new pointer is initialized, and at [4] port_any_p->length is added. Obviously, no checks are performed regarding port_any_p->length, meaning that the returned pointer can be influenced by an attacker.

Specifically, port_any_p->length above is of type uint16_t with a maximum value of 0xffff. The library uses buffers of 0x5000 bytes to receive network data. As a result, an attacker can set the value of the new pointer by up to 0xffff-0x5000 = 0xafff bytes beyond the end of pkt_p. After the calculation, it is a matter of how this specific pointer is used in order to assess exploitation.

After the function returns, presp is passed to unpack_pres_list():

    ... 
    authp = unpack_pres_result_list (presp, swap, end_of_pkt, &st);
    ...

	INTERNAL rpc_cn_auth_tlr_p_t unpack_pres_result_list
	(
	    rpc_cn_pres_result_list_p_t presp,
	    boolean32                   swap,
	    unsigned8                   *end_of_pkt,
	    unsigned32                  *st
	)
	{
	...
    prn = presp->n_results;                                                   [5]
    for (n = 0; (n < prn) && swap; n++)
    {
        SWAP_INPLACE_16 (&presp->pres_results[n].result, end_of_pkt, st);     [6]
        if (*st != rpc_s_ok)
        {
            return (NULL);
        }
		...

At [5] we see presp being used to get a counter and iterate over a list, calling SWAP_INPLACE_16() for each element from presp->pres_results. At first glance this appears to lead to memory corruption, as it leads to a SWAB_16 macro that swaps the order of bytes at [8] :

#define SWAP_INPLACE_16(ptr, end_of_pkt, st) { \
    if (((unsigned8 *) (ptr) + 1) < (const unsigned8 *) (end_of_pkt)) \    [7]
    { \
        *(ptr) = SWAB_16(*(ptr)); \                                        [8]
        *(st) = rpc_s_ok; \
    } \
    else \
    { \
        *(st) = rpc_s_bad_pkt; \
    } \
}
#endif /* SWAP_INPLACE_16 */

At [7] however we see that the code actually checks if the pointer is beyond end_of_pkt, which very descriptively holds the address of the last byte of the network packet received. As a result, the code at [8] that would be able to actually lead to a more promising memory corruption scenario is not executed.

In conclusion, the vulnerability does not appear capable of achieving a write primitive but is capable of performing a Denial of Service attack if, for instance, presp points inside a guard page depending on the memory layout of the target.

Crash Information

macOS rpcsvchost ASAN report:

==12544==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x63100001084c at pc 0x00010b4d639e bp 0x700009e445a0 sp 0x700009e44598
READ of size 1 at 0x63100001084c thread T15
==12544==WARNING: invalid path to external symbolizer!
==12544==WARNING: Failed to use and restart external symbolizer!
    #0 0x10b4d639d in unpack_pres_result_list+0xad (./DCERPC:x86_64+0x2cb39d)
    #1 0x10b4d1ea7 in rpc__cn_unpack_hdr+0x25a7 (./DCERPC:x86_64+0x2c6ea7)
    #2 0x10b4e1719 in receive_dispatch+0x54e9 (./DCERPC:x86_64+0x2d6719)
    #3 0x10b4d9f60 in rpc__cn_network_receiver+0x1b40 (./DCERPC:x86_64+0x2cef60)
    #4 0x10b2109e2 in proxy_start+0x1e2 (./DCERPC:x86_64+0x59e2)
    #5 0x7fff6d7d3108 in _pthread_start+0x93 (/usr/lib/system/libsystem_pthread.dylib:x86_64+0x6108)
    #6 0x7fff6d7ceb8a in thread_start+0xe (/usr/lib/system/libsystem_pthread.dylib:x86_64+0x1b8a)

0x63100001084c is located 15 bytes to the right of 65597-byte region [0x631000000800,0x63100001083d)
allocated by thread T15 here:
    #0 0x10b6974f0 in wrap_malloc+0xa0 (./libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x484f0)
    #1 0x10b3efd5d in rpc__mem_alloc+0x1d (./DCERPC:x86_64+0x1e4d5d)
    #2 0x10b3ee976 in rpc__list_element_alloc+0xb96 (./DCERPC:x86_64+0x1e3976)
    #3 0x10b4c30f8 in rpc__cn_fragbuf_alloc+0x28 (./DCERPC:x86_64+0x2b80f8)
    #4 0x10b4e7480 in receive_packet+0x4d0 (./DCERPC:x86_64+0x2dc480)
    #5 0x10b4dc9e8 in receive_dispatch+0x7b8 (./DCERPC:x86_64+0x2d19e8)
    #6 0x10b4d9f60 in rpc__cn_network_receiver+0x1b40 (./DCERPC:x86_64+0x2cef60)
    #7 0x10b2109e2 in proxy_start+0x1e2 (./DCERPC:x86_64+0x59e2)
    #8 0x7fff6d7d3108 in _pthread_start+0x93 (/usr/lib/system/libsystem_pthread.dylib:x86_64+0x6108)
    #9 0x7fff6d7ceb8a in thread_start+0xe (/usr/lib/system/libsystem_pthread.dylib:x86_64+0x1b8a)

Thread T15 created by T4 here:
    #0 0x10b69167c in wrap_pthread_create+0x5c (./libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x4267c)
    #1 0x10b2105bb in dcethread_create+0x3fb (./DCERPC:x86_64+0x55bb)
    #2 0x10b210c9c in dcethread_create_throw+0x2c (./DCERPC:x86_64+0x5c9c)
    #3 0x10b48d6df in rpc__cn_assoc_acb_create+0x47f (./DCERPC:x86_64+0x2826df)
    #4 0x10b3eee84 in rpc__list_element_alloc+0x10a4 (./DCERPC:x86_64+0x1e3e84)
    #5 0x10b477ee7 in rpc__cn_assoc_acb_alloc+0x107 (./DCERPC:x86_64+0x26cee7)
    #6 0x10b47e7a1 in rpc__cn_assoc_listen+0x251 (./DCERPC:x86_64+0x2737a1)
    #7 0x10b4c793e in rpc__cn_network_select_dispatch+0x12ce (./DCERPC:x86_64+0x2bc93e)
    #8 0x10b450f4f in lthread_loop+0x65f (./DCERPC:x86_64+0x245f4f)
    #9 0x10b44fdbc in lthread+0x28c (./DCERPC:x86_64+0x244dbc)
    #10 0x10b2109e2 in proxy_start+0x1e2 (./DCERPC:x86_64+0x59e2)
    #11 0x7fff6d7d3108 in _pthread_start+0x93 (/usr/lib/system/libsystem_pthread.dylib:x86_64+0x6108)
    #12 0x7fff6d7ceb8a in thread_start+0xe (/usr/lib/system/libsystem_pthread.dylib:x86_64+0x1b8a)

Thread T4 created by T2 here:
    #0 0x10b69167c in wrap_pthread_create+0x5c (./libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x4267c)
    #1 0x10b2105bb in dcethread_create+0x3fb (./DCERPC:x86_64+0x55bb)
    #2 0x10b210c9c in dcethread_create_throw+0x2c (./DCERPC:x86_64+0x5c9c)
    #3 0x10b44fad3 in rpc__nlsn_activate_desc+0xc3 (./DCERPC:x86_64+0x244ad3)
    #4 0x10b447d8b in rpc_server_listen+0x47b (./DCERPC:x86_64+0x23cd8b)
    #5 0x10b1f016b in run_dcerpc_svc(void*)+0x1c (/usr/libexec/rpcsvchost:x86_64+0x10000316b)
    #6 0x7fff6d7d3108 in _pthread_start+0x93 (/usr/lib/system/libsystem_pthread.dylib:x86_64+0x6108)
    #7 0x7fff6d7ceb8a in thread_start+0xe (/usr/lib/system/libsystem_pthread.dylib:x86_64+0x1b8a)

Thread T2 created by T0 here:
    #0 0x10b69167c in wrap_pthread_create+0x5c (./libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x4267c)
    #1 0x10b1efc1f in main+0x13ff (/usr/libexec/rpcsvchost:x86_64+0x100002c1f)
    #2 0x7fff6d5cecc8 in start+0x0 (/usr/lib/system/libdyld.dylib:x86_64+0x1acc8)

SUMMARY: AddressSanitizer: heap-buffer-overflow (./DCERPC:x86_64+0x2cb39d) in unpack_pres_result_list+0xad
Shadow bytes around the buggy address:
  0x1c62000020b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1c62000020c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1c62000020d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1c62000020e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1c62000020f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1c6200002100: 00 00 00 00 00 00 00 05 fa[fa]fa fa fa fa fa fa
  0x1c6200002110: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c6200002120: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c6200002130: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c6200002140: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c6200002150: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
VENDOR RESPONSE

Fixed by Apple on 2023-01-23, patch information available at: https://support.apple.com/en-us/HT213605

TIMELINE

2022-11-15 - Vendor Disclosure
2023-01-23 - Vendor Patch Release
2023-07-13 - Public Release

Credit

Discovered by Dimitrios Tatsis and Aleksandar Nikolic of Cisco Talos.