None
An infinite loop vulnerability exists in the way DCERPC library as used in Apple macOS 12.6.1 deals with fragment sizes. A specially-crafted network packet can cause an infinite loop, which can result in unconstrained resource utilization and denial of service. An authenticated remote attacker can send a network request to trigger this vulnerability. A local attacker can write to a local socket to trigger this vulnerability.
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
macOS - https://apple.com
5.3 - CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H
CWE-835 - Loop with Unreachable Exit Condition (‘Infinite Loop’)
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 rpcsvchost
binary, which opens a number of UNIX sockets that expose different RPC functionality.
A structure common to all DCERPC packets is as follows:
typedef struct
{
unsigned8 rpc_vers; /* 00:01 RPC version - major */
unsigned8 rpc_vers_minor; /* 01:01 RPC version - minor */
unsigned8 ptype; /* 02:01 packet type */
unsigned8 flags; /* 03:01 flags */
unsigned8 drep[4]; /* 04:04 ndr format */
unsigned16 frag_len; /* 08:02 fragment length */ [1]
unsigned16 auth_len; /* 10:02 authentication length */
unsigned32 call_id; /* 12:04 call identifier */
} rpc_cn_common_hdr_t, *rpc_cn_common_hdr_p_t;
Packets are processed in a loop inside dispatch_receive
function, which will in turn call receive_packet
. Function receive_packet
will either read data from a socket or, if enough data was read, return the data for further processing. In either case, the following code is invoked:
if (fbp->data_size >= RPC_C_CN_FRAGLEN_HEADER_BYTES) [2]
{
/*
* Okay, we have enough of the header to figure out how big
* this fragment is.
*/
frag_length = RPC_CN_PKT_FRAG_LEN ((rpc_cn_packet_p_t)(fbp->data_p)); [3]
...
/*
* Figure out how many bytes we need.
*/
need_bytes = frag_length - fbp->data_size; [4]
First, at [2], size of received data is compared against minimal header size. If enough data is received, the parser can continue. At [3], frag_len
field from the common header [1] is read directly from the incoming data. Then, at [4], code tries to calculate how many more bytes it needs to complete the packet. Normally, frag_length
would be positive and either bigger or smaller than the actually-read incoming data. Variable need_bytes
is signed, and the possible negative value is expected. Negative value would signify that a complete packet has already been received and that parsing can fully continue:
if (need_bytes < 0)
{
/*
* Get an overflow fragment buffer.
*/
*ovf_fragbuf_p = rpc__cn_fragbuf_alloc (true); [5]
(*ovf_fragbuf_p)->data_size = abs(need_bytes);
/*
* Set the fragbuf data size to the fragment length and copy the
* excess data to the overflow fragment buffer.
*/
fbp->data_size = frag_length; [7]
memcpy ((*ovf_fragbuf_p)->data_p, [6]
(dce_pointer_t)((unsigned8 *)(fbp->data_p) + fbp->data_size),
(*ovf_fragbuf_p)->data_size);
}
Another buffer is allocated at [5] based on the difference, and packet data is copied into it at [6]. Notice, though, that at [7], frag_length
is used to set the data_size
of this fragment. Since frag_length
comes from the packet directly and isn’t being checked, this constitutes a discrepancy. Following this, the function returns and the packet continues to be processed. However, since data_size
comes from frag_length
, which is directly controlled by incoming packet data, it can be set to zero. This results in zero bytes being consumed from the incoming packet. This will cause the RPC server to loop indefinitely while trying to process packets. It will continue to allocate memory and send failure packets to the connecting client as long as the connection is kept open.
To trigger this vulnerability and cause denial of service, the attached PoC sends one buffer that contains two BIND packets. The first BIND packet is properly formed, has the expected fragment_length
value, and its purpose is to set the state machine. Second BIND packet is truncated and has its fragment_length
set to zero.
Since the service supported by DCERPC is inherently multithreaded, and connection timing is under attacker control, this can lead to immediate crash on a NULL pointer dereference or further memory corruption. This could be abused with other vulnerabilities. Complete resource exhaustion, which can lead to full system instability and reboot, is also possible.
Process 725 stopped
* thread #16, stop reason = EXC_BAD_ACCESS (code=1, address=0x5c)
frame #0: 0x000000010039922b DCERPC`rpc__cn_assoc_send_frag(assoc=0x000061600000ff80, iovector=0x00007000063678c0, sec=0x0000000000000000, st=0x000061600000ffd8) at cnassoc.c:2019:17
Target 0: (rpcsvchost) stopped.
(lldb) bt
* thread #16, stop reason = EXC_BAD_ACCESS (code=1, address=0x5c)
* frame #0: 0x000000010039922b DCERPC`rpc__cn_assoc_send_frag(assoc=0x000061600000ff80, iovector=0x00007000063678c0, sec=0x0000000000000000, st=0x000061600000ffd8) at cnassoc.c:2019:17
frame #1: 0x000000010039bce8 DCERPC`rpc__cn_assoc_send_fragbuf(assoc=0x000061600000ff80, fragbuf=0x0000631008124800, sec=0x0000000000000000, freebuf=1, st=0x000061600000ffd8) at cnassoc.c:2220:5
frame #2: 0x0000000100401025 DCERPC`reject_assoc_action_rtn(spc_struct=0x000061600000ff80, event_param=0x0000631008124800, sm=0x000061600000ff90) at cnsassm.c:1259:5
frame #3: 0x000000010041f993 DCERPC`rpc__cn_sm_eval_event(event_id=100, event_parameter=0x0000631008750800, spc_struct=0x000061600000ff80, sm=0x000061600000ff90) at cnsm.c:346:3
frame #4: 0x00000001003fa6d2 DCERPC`receive_dispatch(assoc=0x000061600000ff80) at cnrcvr.c:1256:13
frame #5: 0x00000001003ede31 DCERPC`rpc__cn_network_receiver(assoc=0x000061600000ff80) at cnrcvr.c:367:21
frame #6: 0x00000001001248b3 DCERPC`proxy_start(arg=0x000060300003a720) at dcethread_create.c:106:14
frame #7: 0x00007fff6ce82109 libsystem_pthread.dylib`_pthread_start + 148
frame #8: 0x00007fff6ce7db8b libsystem_pthread.dylib`thread_start + 15
Fixed by Apple on 2023-03-27, patch information available at: https://support.apple.com/en-us/HT213670
2022-12-06 - Vendor Disclosure
2023-03-27 - Vendor Patch Release
2023-07-13 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.