In this excerpt of a Trend Micro Vulnerability Research Service vulnerability report, Grigory Dorodnov and Guy Lederfein of the Trend Micro Research Team detail a recently patched code execution vulnerability in the VMware vCenter Server. This bug was originally discovered by Hao Zheng and Zibo Li from TianGong Team of Legendsec at Qi'anxin Group. Successfully exploiting this vulnerability could lead to a heap buffer overflow, which could result in the execution of arbitrary code in the context of the vulnerable service. The following is a portion of their write-up covering CVE-2024-37079, with a few minimal modifications.
An integer underflow vulnerability has been reported for VMware vCenter Server. The vulnerability is due to a lack of validation of the calculated response header size used in subtraction.
A remote, unauthenticated attacker could exploit this vulnerability by sending a crafted DCERPC packet to the target server. Successfully exploiting this vulnerability could lead to a heap buffer overflow, which could result in the execution of arbitrary code in the context of the vulnerable service.
The Vulnerability
VMware vCenter Server is a data center management server application developed by VMware Inc. VMware vCenter Server is designed primarily for vSphere, VMware's platform for building virtualized cloud infrastructures. vCenter Server relies on VMware's fork of the Likewise Open project to support DCERPC or, more specifically, MSRPC protocol needed for interaction with Microsoft services and products like, for example, Active Directory. Specifically, vCenter uses the `libdcerpc.so` library from Likewise Open in VMware Certificate Management Service (vmcad), VMware Directory Service (vmdird) and VMware Authentication Framework (vmafdd), accessible from the external network on TCP ports 2014, 2012, and 2020 respectively.
DCERPC
DCERPC (Distributed Computing Environment/Remote Procedure Call) with Microsoft extensions (MSRPC) is used to transparently execute functions on remote servers. To facilitate this process, interfaces are defined using an interface definition language (IDL). IDL code is shared between the server and the client and defines the data structures used for network communications. Each interface is identified by a UUID and contains abstract data types and function declarations. To call a function on a remote server via DCERPC, the client establishes a context by sending a Bind request (RPC_C_CN_PKT_BIND, 0x0b) containing the interface UUID of the interface to which the function belongs. Once the context is established, the client sends the desired RPC calls, or modifies the context by sending an Alter Context (RPC_C_CN_PKT_ALTER_CONTEXT, 0x0e) request. Other RPC request and response types of interest to this report are:
— Bind Acknowledgement (RPC_C_CN_PKT_BIND_ACK, 0x0c) - an acknowledgment of successful completion of the Bind request
— Bind Negative Acknowledgement (RPC_C_CN_PKT_BIND_NAK, 0x0d) - a notification of rejection of the Bind request
— Alter Context Response (RPC_C_CN_PKT_ALTER_CONTEXT_RESP, 0x0f) - a response to the Alter Context request
A DCERPC message common header has the following structure:
The four most significant bits of the Data Representation field (i.e., ones obtained by ANDing with the big-endian 0xF0000000
) contain information about byte order. If these bits are all zero, then the DCERPC message is big-endian encoded; otherwise, it is little-endian encoded.
Of importance to this report is the lastfrag
flag (0x02) in the Packet Flags field. If set, the given fragment is the last one of a multi-fragment transmission.
DCERPC Request Packets
In the request packets, the common header is followed by the packet type-specific fields. For Bind and Alter Context the Type Specific Data field will be set to the following structure:
Of importance to this report is the Presentation Context List, which has the following structure:
where each Presentation Context Element is defined as:
DCERPC packets may include an optional authentication trailer (also known as an authentication verifier) that contains authentication and/or authorization data. The trailer is present if and only if the auth_len
header field is non-zero and set to the length of the trailer in bytes. The common authentication trailer has the following format:
Note, that auth_len in the common header only indicates the length of the auth_value, not the entire trailer, which has 8 additional bytes for static fields.
libdcerpc.so supports the following authentication types with their corresponding IDs:
— GSS-API with SPNEGO mechanism (0x09)
— NTLMSSP (0x0a)
— GSS-API with Kerberos mechanism (0x10)
— Netlogon secure channel (0x44)
This report relies on the first mechanism (0x09) in the following explanation and vulnerability demonstration. Note, that the selected type is not part of the vulnerability itself and is only used to demonstrate its impact.
GSS-API, SPNEGO, and SRP
When the auth_type field of the authentication trailer is set to GSS-API with SPNEGO mechanism (0x09), the auth_value field holds the ASN.1 encoded GSS-API data payload with the following structure:
When the mech field has a value of Object Identifier (OID) “1.3.6.1.5.5.2”, the GSS-API data represents a Simple and Protected GSS-API Negotiation (SPNEGO) message., which allows the client to negotiate the specific authentication mechanism with the server. mechTypes holds a list of OIDs of the authentication mechanisms supported by the client (e.g., Kerberos, NTLM, etc), and mechToken is the initial token for the first mechanism in the list.
One of the mechanisms that can be negotiated with vCenter's services is Secure Remote Password (SRP). The use of this mechanism with GSS-API is not well defined, but references to it can be found in VMware's Project Lightwave. A “made up” (i.e., not officially registered) OID “1.2.840.113554.1.2.10” is used for this mechanism, and the mechToken for it has the following pseudo-ASN.1 definition, reconstructed from the Lightwave source code:
where mechType is set to OID “1.2.840.113554.1.2.10”, ver_maj and ver_min are set to 1 and 0 respectively, userName is a valid User Principal Name (UPN), for example, "[email protected]", and publicValue is a large client's public value (referred to as A in RFC 2945).
The SPNEGO SRP response token can be defined as follows:
Here, publicValue is a large server's public value (referred to as B in RFC 2945).
DCERPC Response Packets
DCERPC response packets (i.e. those sent from the server back to the client) have a structure similar to the request packets. They start with the common header followed by packet-type specific data fields. The response packet types corresponding to the Bind and Alter Context request packets are Bind Acknowledgement, Bind Negative Acknowledgement, and Alter Context Response. Of these three, this report is only concerned with Bind Acknowledgement and Alter Context Response: Bind Negative Acknowledgement is only generated in case of an error, which should be avoided.
Both Bind Acknowledgement and Alter Context Response have the same structure:
The fields of importance here are Secondary Address, Presentation Result List, and Authentication Trailer. Secondary Address, in this case, is the target service's port, for example, "2012" for vmdird, including the null-terminator and prefixed by a 2-byte length integer. Presentation Result List, for each element in the request's Presentation Context List, contains a 24-byte result, plus 4 bytes for static fields. The authentication trailer's structure is the same as in the request. Note, that for the SPNEGO SRP mechanism described above the trailer contains a large authentication value.
Further Details on the Vulnerability
An integer underflow vulnerability has been reported for VMware vCenter Server. The vulnerability is due to a lack of validation of the calculated response header size used in subtraction.
Two types of request packets lead to this vulnerability being triggered: Bind Acknowledgement and Alter Context Response. As opposed to other packets (and, consequently, fragment buffers) there is no size restriction on these packets: while all other types can have up to 4096 bytes of data (which makes the fragment buffer go up to 4158 bytes), these packets have no such limit. This behavior can be observed in the receive_packet() function.
libdcerpc.so's operation is based on protocol state machines. When a packet is received, for the state machine corresponding to the given client, and event is generated. That event indicates what action has to be taken next to proceed with the protocol flow. Depending on the event, the received packet is passed to one of the handlers. In normal operation, for Bind packets, the handler is do_assoc_action_rtn(), and for Alter Context - incr_do_alter_action_rtn(). Both functions, eventually, call the actual packet processors responsible for the construction of the response: do_assoc_req_action_rtn() and do_alter_cont_req_action_rtn(), respectively. For the former to be reached, there is a restriction: either the lastfrag flag must be set or the minor RPC version must be 0.
From this point, we will focus on the do_assoc_req_action_rtn(); do_alter_cont_req_action_rtn() has an almost identical flow with some minor changes.
This gives us a restriction on the number of the Presentation Context List elements. If set to maximum, 169, the returned size of the Presentation Result List is 4060 bytes, which, together with the prior contents of the response buffer, takes 4092 bytes.
Assuming no error was returned, the vulnerable rpc__cn_assoc_process_auth_tlr() function, responsible for processing the request's authentication trailer and filling in one for the response, is called next, with its header_size parameter set to 4092, and auth_len parameter set to 4096 - 4092 = 4.
To reach the vulnerable part of the function, the following conditions must be met.
First, the request's auth_len must not be zero and the authentication trailer must not span beyond the beginning or the end of the packet.
Second, RPC_CN_AUTH_CVT_ID_WIRE_TO_API() must not return an error. This function converts the authentication type taken from the packet into the internal representation; so, using a valid type, for example, 0x9 for SPNEGO, is sufficient to pass this check.
Next, RPC_CN_AUTH_INQ_SUPPORTED() must return a positive result for the selected protocol. This function ensures the selected authentication type is supported by the runtime. Again, SPNEGO is allowed.
After that, a call to the RPC_CN_AUTH_VFY_CLIENT_REQ() function, responsible for verifying the request's authentication trailer, is made. This function is dynamic: the actual function being called depends on the selected authentication type. For SPNEGO, for example, it is rpc__gssauth_cn_vfy_client_req(). While this function does verify the authentication trailer, the credentials do not have to be valid, since an authentication rejection would also have to be placed in the response. Furthermore, for some protocols, for example, SPNEGO, multiple exchanges are necessary before completing credentials verification.
Finally, the response authentication trailer is being filled in. To do so, 8 bytes (trailer without auth_value) are added to the header_size, setting it to 4092 + 8 = 4100 and leaving no place for the auth_value. When the remaining space is calculated, however, an unsigned subtraction of 4100 from the maximum response size (4096) is performed, resulting in a large positive value 0xfffffffc, which is later passed into the RPC_CN_AUTH_FMT_SRVR_RESP() function. Same as RPC_CN_AUTH_VFY_CLIENT_REQ(), this function is dynamic, and the actual function being called depends on the selected authentication type.
Note, that the number of the Presentation Context List elements leading to a heap overflow is strictly 169: the upper bound has already been established above, and the lower bound can be determined from the fact that the resulting header_size is underflowing the maximum response packet size by only 4 bytes. Reducing the number of Presentation Context List elements even by one would cause the Presentation Result List to be 24 bytes less in length and, consequently, the header_size being 24 less, thus not leading to an underflow.
A remote, unauthenticated attacker could exploit this vulnerability by sending a crafted DCERPC packet to the target server. Successfully exploiting this vulnerability could lead to a heap buffer overflow, which could result in the execution of arbitrary code in the context of the vulnerable service.
What follows is an explanation of how this vulnerability leads to a heap overflow, using the SPNEGO SRP authentication protocol as an example.
For SPNEGO, the RPC_CN_AUTH_VFY_CLIENT_REQ() function call results in control being passed to the rpc__gssauth_cn_vfy_client_req() function. This function, in its turn, calls the gss_accept_sec_context() library function which returns an output_token containing a response auth_value. If any error occurs, the length of the output_token is set zero. This token is then saved into the krb_message field in the security context associated with the client.
While for other protocols the token may be small, for SPNEGO SRP it contains, as described above, a large server's public value.
When, eventually, the RPC_CN_AUTH_FMT_SRVR_RESP() function is called, control is passed to the rpc__gssauth_cn_fmt_srvr_resp() function with auth_value_len pointing at a large positive value 0xfffffffc, and auth_value pointing at the end of the response buffer. This function is responsible for copying the contents of the krb_message field to the auth_value, but only if there is enough space. To ensure this, the function first compares the length of the krb_message to the value at auth_value_len. Given that auth_value_len points to a value resulting from an integer underflow, and the comparison is unsigned, this check always passes. Next, the krb_message's contents are copied to auth_value, which, again, does not have any free space left. For SPNEGO SRP, which has a large response token in krb_message, this leads to heap buffer overflow and heap corruption.
Generic Attack Detection
To detect an attack exploiting this vulnerability, the detection device must monitor and parse traffic over ports 2012/TCP, 2014/TCP, and 2020/TCP (DCE/RPC).
The detection device must monitor DCE/RPC traffic looking for the messages with Packet type set to Bind (RPC_C_CN_PKT_BIND, 0x0b) or Alter Context (RPC_C_CN_PKT_ALTER_CONTEXT, 0x0e). If found, the detection device should check the auth_len field to be greater than zero. If so, the Number of Context Elements (n_context_elem) byte must be inspected. If it is greater than or equal to 169, the traffic should be considered suspicious; an attack exploiting this vulnerability is likely underway.
Conclusion
This vulnerability was patched by the vendor in June. At the time of the patch release, there was a fair amount of attention paid to this vulnerability. However, to date, there have been no attacks detected in the wild. As seen above, exploitation will not be straightforward. Still, this is a critical vulnerability and should be addressed by applying the vendor-supplied patch. If you are not able to apply the update, you can also filter malicious traffic using the information provided in the section entitled Attack Detection.
Special thanks to Grigory Dorodnov and Guy Lederfein of the Trend Micro Research Team for providing such a thorough analysis of this vulnerability. For an overview of Trend Micro Research services please visit http://go.trendmicro.com/tis/.
The threat research team will be back with other great vulnerability analysis reports in the future. Until then, follow the team on Twitter, Mastodon, LinkedIn, or Instagram for the latest in exploit techniques and security patches.