Talos Vulnerability Report

TALOS-2023-1800

VMWare vCenter Server DCERPC presentation result list out of bounds memory access

July 13, 2023
CVE Number

CVE-2023-20896

SUMMARY

An out of bounds memory access vulnerability exists in the processing of packets containing presentation result lists in DCERPC library as used in VMWare vCenter Server 7.0.3.01000. 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.

VMware vCenter Server 7.0.3.01000

PRODUCT URLS

vCenter Server - https://www.vmware.com/products/vcenter-server.html

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 is used as a library in VMWare vCenter to implement this protocol and enable interoperability of Windows network services inside vCenter.

VMware vCenter is a key component of VMware vSphere, typically used in cloud environments enabling advanced management of VMs. It enables a number of services, like certificate management, directory services, single sign-on, etc. Some services use the DCERPC protocol for communication, with the implementation provided by the Likewise-Open library. Specifically, it is used in the daemons for VMware Certificate Management Service (vmcad, port 2014), VMware Directory Service (vmdird, port 2012) and VMware Authentication Framework (vmafdd, port 2020), accessible by default from the local network.

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.

Note that the original DCERPC codebase was independently modified and reused by both VMWare in vCenter and Apple in macOS, hence this analysis is largely the same as the one described in TALOS-2022-1659.

Crash Information

VMWare vCenter crash context:

Thread 94 "vmcad" received signal SIGSEGV, Segmentation fault.
[Switching to LWP 32954]
0x00007ffff7cf3cf3 in unpack_pres_result_list (presp=0x7fffb8038b9c, swap=0x1, end_of_pkt=0x7fffb802db7f "", st=0x7ffff451ff24) at ../../../dcerpc/ncklib/cnpkt.c:437
437         prn = presp->n_results;

gef➤  backtrace
#0  0x00007ffff7cf3cf3 in unpack_pres_result_list (presp=0x7fffb8038b9c, swap=0x1, end_of_pkt=0x7fffb802db7f "", st=0x7ffff451ff24) at ../../../dcerpc/ncklib/cnpkt.c:437
#1  0x00007ffff7cf4546 in rpc__cn_unpack_hdr (pkt_p=0x7fffb8028b80, data_size=0x4fff) at ../../../dcerpc/ncklib/cnpkt.c:851
#2  0x00007ffff7cf5ac5 in receive_dispatch (assoc=0x7fffc00009a0) at ../../../dcerpc/ncklib/cnrcvr.c:982
#3  0x00007ffff7cf4e55 in rpc__cn_network_receiver (assoc=0x7fffc00009a0) at ../../../dcerpc/ncklib/cnrcvr.c:349
#4  0x00007ffff7c6fead in proxy_start (arg=0x7fffc0000bf0) at ../../../dcerpc/libdcethread/dcethread_create.c:100
#5  0x00007ffff751ff87 in start_thread () from /lib/libpthread.so.0
#6  0x00007ffff741062f in clone () from /lib/libc.so.6
VENDOR RESPONSE

The vendor provided an advisory and fixes: https://www.vmware.com/security/advisories/VMSA-2023-0014.html

TIMELINE

2023-04-06 - Vendor Disclosure
2023-06-22 - Vendor Patch Release
2023-07-13 - Public Release

Credit

Discovered by Dimitrios Tatsis and Aleksandar Nikolic of Cisco Talos.