Overview
SonicWall Capture Labs Threat Research Team became aware of the threat CVE-2023-34048 (a vCenter Server out-of-bounds write vulnerability), assessed its impact, and developed mitigation measures for the vulnerability.
VMware vCenter Server is a centralized management platform for VMware vSphere environments, designed to provide a unified and efficient administration experience. It allows IT administrators to manage virtualized hosts and virtual machines across multiple physical servers from a single console. vCenter Server plays a crucial role in resource provisioning, monitoring and performance evaluation of virtual infrastructures.
The identified vulnerability in VMware’s vCenter Server is a critical security flaw stemming from an out-of-bounds write issue in its implementation of the Distributed Computing Environment / Remote Procedure Call (DCERPC) protocol. This protocol is used for inter-process communication, and the vulnerability arises due to improper handling of certain inputs. Consequently, a malicious actor with network access to vCenter Server can exploit this vulnerability to execute an out-of-bounds write operation.
This type of vulnerability can be particularly dangerous as it may allow the attacker to write data outside the bounds of allocated memory buffers, potentially leading to remote code execution. Such an exploit could provide the attacker with unauthorized control over the vCenter Server, posing a significant risk to the security and integrity of the virtualized environment managed by vCenter.
Our research concluded that the most feasible outcome of such an attack is the creation of a Denial of Service (DoS) condition on the server, and it is highly unlikely remote execution can be achieved. This vulnerability underscores the need for stringent security measures and prompt patching of software components in enterprise virtualization infrastructures. To mitigate this vulnerability, we recommend that you update to the latest version of VMware vCenter Server.
The affected versions are:
- vCenter Server 4.0 (including updates and specific builds like 4.0.0.10021, 4.0.0.12305)
- vCenter Server 4.1 (including updates 1 and 2, and specific builds like 4.1.0.12319, 4.1.0.14766, 4.1.0.17435)
- vCenter Server 5.0 (including beta, updates and specific build 5.0.0.16964)
- vCenter Server 5.5 (including updates and variations like 1, 1a, 1b, 1c, 2, 2b, 2d, 2e, 3, 3a, 3b, 3d, 3e, 3f, b, c, u1, u2, u3a, u3b)
- vCenter Server 7.0 (including updates and variations like a, b, c, d, 1, 1a, 1c, 1d, 2, 2a, 2b, 2c, 2d, 3, 3a, 3c, 3d, 3e, 3f, 3g, 3h, 3i, 3j, 3k, 3l, 3m, 3n)
- vCenter Server 8.0 (including updates and variations like a, b, c, 1, 1a, 1b, 1c)
CVE Details
This vulnerability has been assigned the Common Vulnerabilities and Exposures (CVE) identifier CVE-2023-34048.
The overall CVSS score is 9.8 (CVSS:3.1/ AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H/E:X/RL:X/RC:X).
The base score is 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H), based on the following metrics:
- Attack vector is network.
- Attack complexity is low.
- Privileges required is none.
- User interaction is none.
- Scope is unchanged.
- Impact of this vulnerability on data confidentiality is high.
- Impact of this vulnerability on data integrity is high.
- Impact of this vulnerability on data availability is high.
Technical Overview
In DCE/RPC, there are several packet types (as seen in Figure 1), each serving a specific purpose in the communication process. These packet types are defined as part of the protocol and are used to manage various aspects of RPC communication, such as establishing connections, transmitting data and handling errors.
Figure 1 – Packet Types
The packet types numbered 11 through 19 are of particular interest when assessing this vulnerability. The descriptions of each packet type are listed below:
- RPC Bind (11): Used to establish a connection between a client and a server. It contains information about the requested interface, data representation and any optional authentication data.
- RPC Bind Acknowledgement (12): Sent by the server in response to a bind request, indicating acceptance of the bind or providing negotiation results.
- RPC Bind NAK (Negative Acknowledgement) (13): Sent by the server if it cannot accept the bind request, often with information about why the request was rejected.
- RPC Alter Context (14): refers to an "Alter Context" indication, used to modify the context of an existing association between communication partners in an RPC session, typically to change or negotiate protocol data units or other session parameters.
- RPC Alter Context Response (15): is the "Alter Context Response," which serves as a confirmation to an Alter Context request (Packet Type 14), indicating the acceptance, rejection, or modification of the proposed context changes in an RPC session.
- RPC Auth3 (16): represents the "Auth3" indication, a message used in the authentication process, typically as the third step in a three-way handshake for establishing a secure and authenticated communication session.
- RPC Shutdown (17): signifies the "Shutdown" indication, which is used to notify the communication parties of an impending shutdown or termination of the association in the RPC session.
- RPC Remote Alert (18): is the "Remote Alert" indication, used to signal an alert or notification from a remote procedure call, indicating a significant event or condition that requires attention during the RPC communication process.
- RPC Orphaned (19): represents the "Orphaned" message, which is used to indicate that a remote procedure call has been orphaned, or abandoned, typically signaling that the client has terminated the call before the server has completed processing.
Delving further into the “libdcerpc.so” library to understand how vCenter Server manages RPC network communication is crucial. This exploration is key to comprehending the complexities of the vulnerability. The group of functions shown in Figure 2, within this library, play a vital role in regulating data flow, safeguarding security and upholding the communication process’s integrity. Working collaboratively, the following functions address different facets of network interaction, each with its distinct function.
Figure 2 – Simple Overview of Nested Functions
- Receive_dispatch(): This function acts as the entry point for processing incoming network messages, managing packet reception and handling based on their types and the connection state.
- Receive_packet(): Responsible for the actual reception and processing of packets, this function manages data fragments, handles overflow buffers and ensures the accurate reconstruction of data.
- Rpc__cn_unpack_hdr(): Specializing in unpacking packet headers, this function adjusts packet fields according to data representation and byte order, playing a critical role in the interoperability of different systems.
- Rpc_cn_assoc_eval_network_event(): This function processes network events in relation to an RPC association. It evaluates events with or without a fragment buffer, affecting the association’s state and maintaining the consistency of the network communication.
To further our understanding, it is essential to delve into two key foundational concepts:
- Pointer Manipulation: This involves the ability to effectively handle and manipulate memory addresses stored in pointers. Proficiency in pointer manipulation allows for direct interaction with memory, which is crucial in systems programming and memory management tasks.
- Structure Memory Layout: Understanding how data structures are laid out in memory is critical. This includes knowing the size and alignment of different data types and how they are sequenced within a structure. A thorough grasp of memory layout is vital for efficient memory usage and for interfacing with hardware or low-level system components.
Pointer Manipulation (Linear Understanding)
In 64-bit systems, pointers usually have an 8-byte (64-bit) size, enabling them to address a vast memory space. Manipulating these pointers for memory navigation involves a linear process. Consider a pointer defined as “long long *ptr = (long long *)0x7fff5fbffec0,” which points to an 8-byte long long integer. Moving this pointer is accomplished in increments equal to the size of the data type it references.
To advance the pointer to the next long long integer, you increment it: ptr++, which adds 8 bytes to the address, making it point to 0x7fff5fbffec8. Similarly, decrementing the pointer with ptr– moves it back by 8 bytes to 0x7fff5fbffec0.
This linear movement can be extended to skip multiple elements, like ptr += 2 to move forward 16 bytes, or ptr -= 2 to move back 16 bytes. This approach allows for effective traversal and manipulation of memory locations, but it requires careful handling to avoid errors like accessing invalid memory areas.
Figure 3 – Pointer Example
Pointer Manipulation (Vertical Understanding)
In the context of a debugger’s memory window, which typically displays memory in a linear, byte-oriented format, moving a pointer as demonstrated in the example reflects a vertical traversal through memory addresses. The memory window is often organized in rows and columns, with each row representing a contiguous block of memory addresses. When the pointer long long *ptr = (long long *)0x7fff5fbffec0; is incremented (ptr++), it moves ‘down’ to the next memory block, shifting from 0x7fff5fbffec0 to 0x7fff5fbffec8.
This is because each increment steps through 8 bytes (the size of a long long in a 64-bit system), effectively descending to the next row in the memory window. Conversely, decrementing the pointer (ptr–) moves it ‘up’ to the previous memory block. Adjustments like ptr += 2 or ptr -= 2 result in moving two rows down or up, respectively.
In the debugger’s memory window, this movement appears vertical, navigating through the memory rows, while each column within a row typically represents a single byte. This visualization aids in understanding how pointers access and manipulate specific locations in the system’s memory space.
Structure Layout
Viewing memory as linear or vertical from above, we can now understand the “rpc_cn_fragbuf_s_t” structure in C as seen Figure 4. This structure, with its specific memory alignment and field layout, is designed to manage fragment buffers in RPC communication. The structure begins with an ‘rpc_list_t’ link, occupying 16 bytes, which serves as a linked list node. This allows the fragment buffer to be part of a list, with pointers to the next and previous elements. Following this is a 4-byte ‘unsigned32 freebuf’, a flag indicating whether the buffer should be freed. Another 4-byte ‘unsigned32’ field, ‘max_data_size’, specifies the maximum data capacity of the fragment buffer.
Figure 4 – Structure Layout
The rpc_cn_fragbuf_dealloc_fn_t fragbuf_dealloc is an 8-byte function pointer for deallocating the fragment buffer. Next is an 8-byte pointer_t data_p, pointing to the actual data stored in the fragment buffer. The unsigned32 data_size field, 4 bytes long, records the current size of the data. An additional 4-byte unsigned8 overhead_area[4] is reserved for miscellaneous use or overhead information.
Finally, a 1-byte unsigned8 data_area[1] array is a flexible array member used to reference the start of the data region, typically used to access the packet data (pkt_p pointer) stored in the buffer. The structure is aligned to 8 bytes, ensuring optimal access on 64-bit systems and maintaining compatibility with different compilers through conditional compilation directives (__declspec(align(8)) for MSVC and __attribute__((aligned(8))) for GCC/others).
Triggering the Vulnerability
Identified in Figure 5 as the start of the vulnerability, this code manipulates pointers and memory within a network packet structure, specifically within an RPC binding header. Initially, the variable authlen is assigned the value of the auth_len field from the packet’s common header. An offset is then calculated, which is set to authlen + 8.
The pointer ppDealloc is set to point to a specific location within the packet, calculated by subtracting this offset from the address at the end of the fragment (frag_len from the packet’s start). The value at ppDealloc is then accessed and stored in pDealloc. Following this, the code performs a byte swap operation on the 4 bytes immediately following ppDealloc.
Figure 5 – epilogue of rpc__cn_unpack_hdr()
The reason behind why this works is the following formula:
ppDealloc = pkt_p + (frag_len – (auth_len + 8))
The math in the round brackets (frag_len – offset) creates a negative number result, which moves the pkt_p pointer upward to the fragbuf_dealloc pointers address when added together (in other words, subtracted because of the negative number).
In the disassembly of the RPC communication process, shown in Figure 6, a sequence of operations is performed on the “rpc_cn_fragbuf_s_t” structure. The operations reveal intricate aspects of vulnerability exploitation. The steps are as follows:
Figure 6 – Disassembly of rpc__cn_unpack_hdr()
- [1] Auth_len Retrieval: The disassembly begins with the retrieval of the auth_len value from the memory (indicated by movzx ecx, word ptr [pkt_p+0Ah]). This step is crucial for understanding the subsequent operations involving authentication length.
- [2] Frag_len Retrieval: Immediately following is the extraction of frag_len (movzx eax, word ptr [pkt_p+8]). This value is essential for determining the length of the fragment.
- [3] Auth_len Modification: The value of auth_len is then increased by 8 (lea rdx, [rcx+8]), resulting in a total of 24 in decimal. This modification plays a role in adjusting the authentication length for further processing.
- [4] Subtraction to Create Negative Value: A critical operation involves subtracting auth_len from frag_len (sub rax, rdx). This results in a negative number (-24), indicating a decrease in fragment length relative to the authentication length.
- [5] Pointer Manipulation: The magic of this vulnerability lies here. The pkt_p pointer is adjusted by adding the negative number (-24) (add rax, pkt_p). This effectively moves the pointer backward in memory, reaching the dealloc member above in the structure, and creating an unexpected behavior in memory management.
- [6] Byte Order Reversal: Finally, there is a reversal of the base address byte order from 0x00007f74 to 0x747f0000 (bswap edx). This operation changes the way bytes are read and interpreted, further complicating the vulnerability exploitation process.
Due to the vulnerability involving a byte-swap of the base address, it is important to note that an attacker lacks control over the final, byte-swapped base address. This limitation is critical because it restricts the attacker’s ability to execute arbitrary code or manipulate the server’s operations in a specific, intended manner.
Instead, the most feasible outcome of such an attack is the creation of a Denial of Service (DoS) condition on the server. In a DoS scenario, the attacker can disrupt the normal functioning of the server, potentially causing it to crash or become unresponsive, but cannot directly control or alter the server’s behavior beyond causing this disruption. This limitation stems from the inability to predict or influence the final state of the memory after the byte swap, making it highly unlikely to execute precision attacks like injecting or executing specific malicious code. As a result, the attack vector, while serious, is likely confined to causing service interruptions rather than enabling direct system compromise or data theft.
Figure 7 below involves checking packet information and determining an event type based on the packet information. Execution returns to the calling function receive_dispatch, and it then follows with a check on the “class” member of the “packet_info_table” at index “ptype” (packet type) which is true (non-zero). This area of code assigns v14 the value of fragbuf_p and returns from the if else statement calling rpc_cn_assoc_eval_network_event().
Figure 7 – Receive_Dispatch()
Once inside the rpc_cn_assoc_eval_network_event function, an attempt is made to deallocate the fragment buffer. However due to an invalid function pointer, the application will crash at the call to “fragbuf_dealloc(fragbuf),” suggesting the pointer has been manipulated from the above swap of the base address.
Figure 8 – RPC_CN_ASSOC_EVAL_NETWORK_EVENT()
Exploitation
The following packet, in Figure 9, produces a Denial-of-Service (DoS) crash or segment fault:
Figure 9 – Attack Packet
This demo video below demonstrates the results of the packet in Figure 9 being sent to the vulnerable version VMWare vCenter Server 8.0U1c. As explained in Figure 8, a crash occurs on a jmp rax instruction, since rax now has an invalid pointer stored within it.
Figure 10 – Video of attack packet causing crash
SonicWall Protections
To ensure SonicWall customers are prepared for any exploitation that may occur due to this vulnerability, the following signatures have been released:
- IPS: 4176 VMware vCenter Malformed DCERPC Traffic
- IPS: 4187 VMware vCenter Malformed DCERPC Traffic 2
- IPS: 19483 VMware vCenter Malformed DCERPC Traffic 3
- IPS: 19484 VMware vCenter Malformed DCERPC Traffic 4
- IPS: 19485 VMware vCenter Malformed DCERPC Traffic 5
Recommended Mitigations
The risks posed by this vulnerability can be mitigated or eliminated by:
- Applying the vendor-supplied patch to eliminate this vulnerability.
- Utilizing up-to-date IPS signatures to filter network traffic.
- Alternatively, consider taking the server offline.