Remote Procedure Calling (RPC): A hacker’s view

Published On: December 2024

Remote Procedure Calling (RPC): A hacker's view

Remote Procedure Calling (RPC) is a protocol that allows software on different systems to communicate efficiently. It enables a program to execute a function on a remote server as if it were local, making it an essential component in distributed computing environments. In practical terms, RPC helps systems and applications interact seamlessly, whether they’re on the same machine or across a network.
For cybersecurity professionals researching Windows, understanding RPC is crucial, especially in Windows environments where Microsoft’s implementation (MSRPC) is widely used. MSRPC is integrated into various Windows components like COM/DCOM and Event Viewer, making it a common target for attacks. Misconfigurations or vulnerabilities in RPC can open up systems to unauthorized access or privilege escalation, highlighting the need for proper security measures.
This blog will walk through the fundamentals of RPC, focusing on MSRPC. We’ll explore key concepts, practical implementations with code examples, and the security challenges associated with it. By the end, you should have a solid understanding of how RPC works and how to manage its risks effectively.

The Basics of RPC: A Closer Look

Remote Procedure Calling (RPC) is fundamentally about enabling communication between a client and server, whether they're running on the same system or across a network. Microsoft’s implementation, MSRPC, is a key focus for this discussion, as it’s integral to various Windows components and services.
MSRPC supports interactions within the same process space or between different hosts entirely, allowing it to be used for tasks such as remote printing, accessing the Event Viewer remotely, and managing COM/DCOM operations. Understanding how MSRPC is structured and implemented is essential for anyone managing or securing Windows environments.

Why Focus on MSRPC?

MSRPC isn’t just about one type of service; it’s versatile. It’s implemented in various Windows components to enable remote management and interaction between different systems. For example:

  • Remote Printing: MSRPC allows systems to manage print jobs remotely, which is common in enterprise settings.
  • COM/DCOM Operations: It facilitates object-oriented programming over a network, essential for distributed computing.
  • Event Viewer Access: Administrators can view logs from remote machines, enabling centralized monitoring and troubleshooting.

Connecting to a remote computer using event viewer

nmap showing MSRPC port open

COM/DCOM services utilize RPC for communication

Core Concepts and Terminology

Let’s break down some of the essential terms:

  • Marshalling and Unmarshalling: Marshalling is the process of converting data into a format suitable for transmission over a network. It’s similar to serialization. Unmarshalling is transforming the received data back into a usable format on the client or server side.
  • Stubs: These are generated pieces of code that act as an intermediary between the client and the server. The client stub sends a request, while the server stub handles the response, making it easier to manage communication without directly coding the transmission logic.
  • Protocol Sequence: This is the specific communication protocol agreed upon by the client and server to carry out RPC. Examples include:

    • ncacn_ip_tcp: Uses IP addresses and ports for communication.
    • ncacn_np: Uses named pipes, often in local environments.
    • ncacn_http: Routes through HTTP proxies for web-based interactions.

Understanding the RPC Communication Flow

The process of RPC communication involves several key steps on both the server and client sides. Let’s walk through the general steps involved in setting up and using MSRPC:

Server-Side Process

  1. Define RPC Interfaces (MIDL): The server starts by defining the interface using the Microsoft Interface Definition Language (MIDL). This file outlines the functions and parameters that the client can call.
  2. Implement Server Logic: The server logic is implemented based on the defined interface, ensuring the server knows how to handle incoming requests.
  3. Select a Protocol Sequence: The server chooses a protocol sequence (e.g., ncacn_ip_tcp) that the client and server will use to communicate.
  4. Register Interfaces: The server registers the interface, making it available for clients to access.
  5. Start Listening: The server starts listening for incoming requests, either on a specific endpoint or through the Endpoint Mapper (optional) to allow dynamic binding.

Client-Side Process:

  1. Define RPC Interfaces (MIDL): Just like the server, the client uses the MIDL file to ensure compatibility.
  2. Import Headers: The client imports necessary headers generated from the MIDL file.
  3. Create a Binding Handle: The client creates a binding handle that defines the connection parameters.
  4. Call Server Function: The client sends a request using this handle, invoking the server function as if it were a local call.

Understanding this flow is critical for setting up and troubleshooting RPC connections in real-world scenarios. It also highlights where potential security risks might arise, such as misconfigured protocol sequences or unregistered interfaces.

The RCP Server/Client communication flow

Defining RPC Interfaces with MIDL

Microsoft Interface Definition Language (MIDL) is a cornerstone of developing RPC applications. It provides a structured way to define the interfaces that facilitate communication between the client and server. When defining an RPC interface, the MIDL file serves as the blueprint for generating critical components:

  • Header File: Contains the declarations and data types necessary for both the client and server to interact with the RPC functions.
  • Client Stub: Manages marshalling (converting data into a transmittable format) on the client side.
  • Server Stub: Handles unmarshalling (converting data back into its usable format) on the server side.

A typical MIDL file might look like this:

[
    // The uuid uniquely identifies the interface.
    uuid(73efcd65 - 810d - 47c8 - 8eab - 88fed2805866),
    version(1.0)] interface SimpleRPC {
  void MultiplyByTwo(
      // Input ([in]) and output ([out]) parameters define how data flows
      // between the client and server.
      [in] handle_t hBinding, [in] int number,
      // The handle_t binding handle establishes the connection parameters.
      [ out, retval ] int* result);
}

Upon compilation, the MIDL file generates:

  • interface_h.h: A header file with declarations.
  • interface_c.c and interface_s.c: Client and server stubs, respectively.

MIDL Generation Process

Endpoint Mapping and Dynamic Binding

In some scenarios, clients may not know the exact endpoint that is listening on the server. This is where the Endpoint Mapper comes into play. The Endpoint Mapper is a service that provides context for the client on connectivity details to the endpoint. The RPC server registers itself with the mapper using the RpcEpRegisterW function, enabling clients to query the mapper for endpoint details dynamically.

The SVCHOST process running the endpoint mapper (RPCSS)

Communication Protocol

To further illustrate and examine RPC communication, we can examine network captures using Wireshark while running the example code in the Github repository. We can use the “DCEPRC” filter to gather all relevant packets.

The following screenshot shows a packet capture of the bind request:

Following this, we see the client's request to the server. Notice the stub data which contains the actual payload for communication. In this case, we used MSRPC to achieve command execution, and thus, the cmd.exe /C whoami payload.

Then, we check the server's response to the client in the stub data with the machine name.

RPC Security

RPC's complexity makes it a frequent target for attackers. Securing RPC implementations, especially Microsoft’s implementation (MSRPC), requires a multi-layered approach which can be daunting. This section outlines key security considerations and best practices.

https://github.com/akamai/akamai-security-research/tree/main/rpc_toolkit

Implementing Secure Authentication in RPC

One of the primary methods to secure RPC communication is through authentication. Microsoft’s RPC implementation supports multiple authentication protocols, including Kerberos and NTLM, which are implemented through the RPC_C_AUTHN_GSS_NEGOTIATE and RPC_C_AUTHN_GSS_KERBEROS services, respectively. These protocols enable mutual authentication, ensuring that both the client and server validate each other's identities before communication is established.

To configure authentication on the server, developers can use the RpcServerRegisterAuthInfo function. This function allows the server to specify the authentication service, the principal name, and optionally, a callback function for retrieving security keys. Below is an example of how this can be implemented:

RPC_STATUS RpcServerRegisterAuthInfo(
    RPC_CSTR ServerPrincName,  // Server principal name (e.g., SPN for Kerberos)
    unsigned long
        AuthnSvc,  // Authentication service (e.g., RPC_C_AUTHN_GSS_NEGOTIATE)
    RPC_AUTH_KEY_RETRIEVAL_FN GetKeyFn,  // Optional key retrieval callback
    void *Arg                            // Callback context
);

The ServerPrincName identifies the server within the domain, while the AuthnSvc parameter defines the authentication mechanism, such as NTLM or Kerberos.

Why Protocol Selection Matters

Not all protocol sequences support authentication. For example, ncacn_ip_tcp protocol does not support authentication. This limitation makes it unsuitable for scenarios where authenticated communication is mandatory. Instead, protocols like ncacn_http (which routes through an HTTP proxy) or ncacn_np (which uses named pipes) are preferred for their compatibility with authentication mechanisms.

Endpoint and Interface Security in RPC

Security can be configured at both the endpoint and the interface level. While these configurations provide flexibility, improper implementation or reliance on a single layer of security can lead to vulnerabilities which will be explained momentarily.

RPC interfaces can be secured using a Security Descriptor, which defines access control rules for the interface. A common approach involves creating a Security Descriptor using the function ConvertStringSecurityDescriptorToSecurityDescriptor. This descriptor is then applied to the interface using RpcServerUseProtseqEpEx. For example:

LPCWSTR sddl = L"D:(A;;GA;;;BA)";
ConvertStringSecurityDescriptorToSecurityDescriptor(…);
RpcServerUseProtseqEpEx(…, sddl, …);

  • sddl: Defines the access permissions in Security Descriptor Definition Language (SDDL). In this case, it grants generic access (GA) to the built-in administrators (BA) group.
  • This setup restricts access to the interface, ensuring that only authorized users can interact with the service.

However, relying solely on this approach is not recommended. A Security Descriptor applied at the interface level may not provide sufficient protection when other endpoints or interfaces on the same process are misconfigured.

Microsoft warns of relying on Endpoint Security

Why Endpoint Security Alone is Inadequate

Utilizing endpoint security alone effectiveness diminishes when multiple endpoints share the same process. If one endpoint is secured but another is loosely configured, clients may bypass restrictions by accessing the unsecured endpoint. For example, A client restricted by the Security Descriptor on one endpoint might connect to another, less secure endpoint within the same process, gaining access to interfaces indirectly.

Interface Security in RPC

Securing an RPC interface goes beyond endpoint configurations by applying protections directly to the interface itself. This ensures that the interface remains secure regardless of which endpoint is used to access it. A Security Descriptor can be assigned to the interface to control access explicitly.

The function RpcServerRegisterIf3 provides the capability to secure an interface. Along with standard configuration parameters, it includes options for assigning flags and a security descriptor.

RPC_STATUS RpcServerRegisterIf3(
[in] RPC_IF_HANDLE IfSpec,
[in, optional] UUID *MgrTypeUuid,
[in, optional] RPC_MGR_EPV *MgrEpv,
[in] unsigned int Flags,
[in] unsigned int MaxCalls,
[in] unsigned int MaxRpcSize,
[in, optional] RPC_IF_CALLBACK_FN *IfCallback,
[in, optional] void *SecurityDescriptor
);

  • RPC_IF_ALLOW_SECURE_ONLY: Using this flag restricts client calls to authenticated sessions only, ensuring that the interface is not accessible to unauthenticated clients.
  • RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH: Using this flag allows client calls without enforcing authentication, which may be useful for less sensitive operations but reduces overall security.

Case Study: PetitPotam

The PetitPotam attack highlights how vulnerabilities in RPC services can be exploited to gain unauthorized access. Specifically, this attack abuses the Encrypted File System RPC (EFSRPC) service, which manages encryption of data objects over RPC.

Overview of the Attack:

  1. Exploitation of EFSRPC:
    The attacker initiates an attack on the Encrypted File System RPC (EFSRPC) to compel a server to authenticate to the attacker's client.
  2. Capture of NTLM Authentication:
    Once the server attempts to authenticate, the attacker captures the NTLM authentication credentials from the server.
  3. Relay of NTLM Authentication:
    The captured NTLM authentication is relayed to the Active Directory Certificate Service (ADCS). This allows the attacker to generate a legitimate certificate.
  4. Certificate Usage:
    The generated certificate is then used to obtain a Kerberos Ticket Granting Ticket (TGT), granting elevated privileges and potentially full access to the domain.

PetitPotam Attack Flow

Reverse Engineering RPC

Given that much of RPC's internals are undocumented, reverse engineering provides valuable insights into how RPC servers and clients operate. We will use the PetitPotam attack as an example to show how it might’ve been found through reverse engineering. Here are some insights to consider when reverse engineering RPC:

Undocumented Internals:

  • A significant portion of RPC's functionality lacks public documentation. Reverse engineering tools and techniques help bridge this gap.
  • Examining MIDL output and the associated header files is a good starting point for understanding RPC interfaces.

Protocol-Specific Insights:

  • The EFSRPC protocol is hosted in DLL files such as efsslsaext.dll and efssvc.dll. This information can be obtained from browsing Microsoft’s MSDN or by reverse engineering the imports of the EFSRPC service.
  • The DLL files contain functions and metadata critical for understanding how RPC services are structured and interact with one another.

Cross-Referencing Key Calls:

  • Functions such as RpcServerRegisterIf, RpcServerRegisterAuthInfo, RpcServerInterfaceGroupCreateW, and RpcServerListen are great entry points for investigating the behavior of RPC services as they are utilized by almost every RPC service.
  • We will treat the function InitializeLsaExtension in efsslsaext.dll is as an initial entry point.

We are going to dive deeper into the efsslsaext DLL and try to identify the root cause of the attack.

The code inside efssvc.dll shows the use of the function ConvertStringSecurityDescriptorToSecurityDescriptorW, which converts a string representation of a Security Descriptor into a usable format for defining access control. This function is worth investigating as it relates to security configuration on the RPC service. We can copy the security descriptor string to analyze. We can do that either manually or using Powershell's ConvertFrom-SddlString utility. Below is the function call:

ConvertStringSecurityDescriptorToSecurityDescriptor(
"D:(A;;GRGWGX;;;WD)(A;;GRGWGX;;;RC)(A;;GA;;;BA)(A;;GA;;;OW)…", lu,
&SecurityDescriptor, &hMem);

By manually analyzing the SDDL string, we can conclude the following:

  • "D:(A;;GRGWGX;;;WD)": Grants GenericRead, GenericWrite, and GenericExecute permissions to the Everyone group.
  • "A;;GA;;;BA": Grants GenericAll (full access) to the built-in Administrators group.

Using Powershell to decode the SDDL

The resulting Security Descriptor is applied to the RPC service, defining permissions for different user groups and levels. This essentially means that the function grants broad access (read, write, execute) to the Everyone group and Ensures full control for Administrators and the resource owner. This creates the entry point for the attack surface as no access restriction is imposed on who can launch the RPC service.

 Moving on to the RpcServerRegisterAuthInfoW, we see that the following parameters are passed:

v9 = RpcServerRegisterAuthInfoW(0i64, 9u, 0i64, 0i64);

By referring to MSDN, we find that the second parameter passed is the authentication service to use when the server receives a request for a remote procedure call. Although this is not mentioned on the MSDN page, the full definition the service constants can be found in this page. The number 9 corresponds to RPC_C_AUTHN_GSS_NEGOTIATE which is NTLM authentication, and thus, the NTLM relay attack surface.

Interesting Calls: RpcServerInterfaceGroupCreateW and RpcServerInterfaceGroupActivate

Moving on with the code in with the code in the DLL, we find calls to the functions RpcServerInterfaceGroupCreateW and RpcServerInterfaceGroupActivate. Those are particularly useful for managing multiple RPC interfaces in an organized way, especially in scenarios involving complex services like the EFS RPC interface.

RpcServerInterfaceGroupCreateW

This function is used to create a group of RPC interfaces. The first parameter to this function is a pointer to an array of interfaces, defined as RPC_INTERFACE_TEMPLATE structures. These structures specify the interfaces to be grouped and exposed. We can see some interesting variables passed to this function.

RpcServerInterfaceGroupCreateW

  • v30 = L"EFS RPC Interface" and v39 = L"EFSK RPC Interface":
    These variables indicate the specific RPC interfaces being registered.
  • v13 = "ncalrpc":
    Refers to the ncalrpc protocol sequence, which is typically used for local interprocess communication.
  • v18 = "ncacn_np":
    Refers to the ncacn_np protocol, which uses named pipes for communication.
  • v19 = "\\pipe\\efsrpc":
    Specifies the named pipe endpoint associated with the EFS RPC interface.

After defining the group of interfaces using RpcServerInterfaceGroupCreateW, the RpcServerInterfaceGroupActivate function is called to activate the group. This step makes the interfaces available for clients to use.

Now going back to EfsRpcEncryptFileSrv, we can see the Opnum 4, which dictates the server function to be executed. Most of the times, this will be in the same order in the dispatch table.

Following DispatchTable to find the RPC interface

DispatchTable entries in IDA

Finding the Opnum from the packet capture generated by the RPC communication, which corresponds to the DispatchTable entry

Takeaways

Other than Microsoft, many third parties provide RPC interfaces

RPC (Remote Procedure Call) is not exclusive to Microsoft; many third-party developers implement their own RPC interfaces for various purposes. These implementations are used in diverse applications, from enterprise software to embedded systems.

  • Significance: Each third-party implementation comes with its own design and potential vulnerabilities, making it a valuable area for security research. While Microsoft's MSRPC is widely documented and researched, less-documented third-party interfaces may offer untapped opportunities for identifying novel bugs and attack vectors.

RPC as an attack surface

RPC interfaces, particularly those created by third parties, often present a rich attack surface due to:

  • Inconsistent security implementations.
  • Lack of robust input validation.
  • Overly permissive configurations.

These characteristics make RPC interfaces a fruitful target for attackers seeking to exploit vulnerabilities such as:

  • Unauthenticated access.
  • Memory corruption.
  • Arbitrary code execution.

Example:

By analyzing third-party RPC implementations, researchers can uncover logic flaws or insecure configurations that lead to privilege escalation or data breaches.

RPC is huge, but a fundamental understanding can be enough to get started

The RPC protocol is vast, encompassing numerous features, protocols, and use cases. While it can be daunting to dive into all its details, understanding the fundamentals provides a strong foundation:

  • How RPC calls work (client-server interaction).
  • Protocol sequences (e.g., ncacn_np, ncacn_ip_tcp).
  • The role of interface definitions, such as those written in MIDL (Microsoft Interface Definition Language).

With this knowledge, one can effectively explore RPC vulnerabilities, analyze traffic, or even develop basic RPC-based applications.

A complex protocol such as MSRPC makes it more possible to create bugs

MSRPC's complexity arises from its extensive features, such as:

  • Support for multiple authentication protocols.
  • Dynamic endpoint mapping.
  • Advanced marshalling/unmarshalling of data.

This complexity increases the likelihood of bugs, particularly in:

  • Interface definitions.
  • Memory handling during RPC communication.
  • Authentication and authorization mechanisms.

Implications:

For attackers, these bugs can lead to significant vulnerabilities such as remote code execution or privilege escalation. For developers, it underscores the need for thorough testing and adherence to best practices.

Developing your own RPC applications is an efficient approach for exploring RPC

Building custom RPC applications provides a hands-on understanding of how the protocol operates. This includes:

  • Defining and implementing interfaces using MIDL.
  • Configuring endpoints and protocols.
  • Managing security using authentication and access control.

Benefits:

  • Learn by Doing: Gain practical insights into RPC’s behavior, potential pitfalls, and debugging techniques.
  • Test Vulnerabilities: Experiment with misconfigurations and intentionally introduced flaws to understand real-world attack scenarios.

Explore what else utilizes RPC (CVEs, Windows services, web services, etc.)

RPC is not limited to a single context—it underpins a wide variety of systems and applications:

  • Windows Services: Many core Windows functionalities, such as EFSRPC (exploited by PetitPotam), depend on RPC.
  • Web Services: Some APIs and distributed applications utilize RPC-like mechanisms for communication.
  • Common Vulnerabilities and Exposures (CVEs): Analyzing CVEs related to RPC reveals recurring patterns of vulnerabilities, such as improper input validation or authentication flaws.

Next Steps:

  • Hunt for CVEs: Study RPC-related vulnerabilities in public databases to learn how they were exploited and patched.
  • Analyze Traffic: Use tools like Wireshark to observe RPC communications in real-time.
  • Expand Knowledge: Explore services beyond MSRPC to understand third-party implementations and alternative protocols.

References and Further Reads