A new tool for Windows RPC research
By Aaron LeMasters
Trail of Bits is releasing a new tool for exploring RPC clients and servers on Windows. RPC Investigator is a .NET application that builds on the NtApiDotNet platform for enumerating, decompiling/parsing and communicating with arbitrary RPC servers. We’ve added visualization and additional features that offer a new way to explore RPC.
RPC is an important communication mechanism in Windows, not only because of the flexibility and convenience it provides software developers but also because of the renowned attack surface its implementers afford to exploit developers. While there has been extensive research published related to RPC servers, interfaces, and protocols, we feel there’s always room for additional tooling to make it easier for security practitioners to explore and understand this prolific communication technology.
Below, we’ll cover some of the background research in this space, describe the features of RPC Investigator in more detail, and discuss future tool development.
If you prefer to go straight to the code, check out RPC Investigator on Github.
Background
Microsoft Remote Procedure Call (MSRPC) is a prevalent communication mechanism that provides an extensible framework for defining server/client interfaces. MSRPC is involved on some level in nearly every activity that you can take on a Windows system, from logging in to your laptop to opening a file. For this reason alone, it has been a popular research target in both the defensive and offensive infosec communities for decades.
A few years ago, the developer of the open source .NET library NtApiDotNet, James Foreshaw, updated his library with functionality for decompiling, constructing clients for, and interacting with arbitrary RPC servers. In an excellent blog post—focusing on using the new NtApiDotNet
functionality via powershell scripts and cmdlets in his NtObjectManager
package—he included a small section on how to use the powershell scripts to generate C# code for an RPC client that would work with a given RPC server and then compile that code into a C# application.
We built on this concept in developing RPC Investigator (RPCI), a .NET/C# Windows Forms UI application that provides a visual interface into the existing core RPC capabilities of the NtApiDotNet
platform:
- Enumerating all active ALPC RPC servers
- Parsing RPC servers from any PE file
- Parsing RPC servers from processes and their loaded modules, including services
- Integration of symbol servers
- Exporting server definitions as serialized .NET objects for your own scripting
Beyond visualizing these core features, RPCI provides additional capabilities:
- The Client Workbench allows you to create and execute an RPC client binary on the fly by right-clicking on an RPC server of interest. The workbench has a C# code editor pane that allows you to edit the client in real time and observe results from RPC procedures executed in your code.
- Discovered RPC servers are organized into a library with a customizable search interface, allowing you to pivot RPC server data in useful ways, such as by searching through all RPC procedures for all servers for interesting routines.
- The RPC Sniffer tool adds visibility into RPC-related Event Tracing for Windows (ETW) data to provide a near real-time view of active RPC calls. By combining ETW data with RPC server data from
NtApiDotNet
, we can build a more complete picture of ongoing RPC activity.
Features
Disclaimer: Please exercise caution whenever interacting with system services. It is possible to corrupt the system state or cause a system crash if RPCI is not used correctly.
Prerequisites and System Requirements
Currently, RPCI requires the following:
- The Windows operating system
- .NET Framework 4.8 or newer
- The Windows SDK with the Debugging Tools for Windows component installed
- Administrator access
By default, RPCI will automatically discover the Debugging Tools for Windows installation directory and configure itself to use the public Windows symbol server. You can modify these settings by clicking Edit -> Settings
. In the Settings dialog, you can specify the path to the debugging tools DLL (dbghelp.dll) and customize the symbol server and local symbol directory if needed (for example, you can specify the path srv*c:\symbols*https://msdl.microsoft.com/download/symbols
).
If you want to observe the debug output that is written to the RPCI log, set the appropriate trace level in the Settings window. The RPCI log and all other related files are written to the current user’s application data folder, which is typically C:\Users\(user)\AppData\Roaming\RpcInvestigator
. To view this folder, simply navigate to View -> Logs
. However, we recommend disabling tracing to improve performance.
It’s important to note that the bitness of RPCI must match that of the system: if you run 32-bit RPCI on a 64-bit system, only RPC servers hosted in 32-bit processes or binaries will be accessible (which is most likely none).
Searching for RPC servers
The first thing you’ll want to do is find the RPC servers that are running on your system. The most straightforward way to do this is to query the RPC endpoint mapper, a persistent service provided by the operating system. Because most local RPC servers are actually ALPC servers, this query is exposed via the File -> All RPC ALPC Servers…
menu item.
The discovered servers are listed in a table view according to the hosting process, as shown in the screenshot above. This table view is one starting point for navigating RPC servers in RPCI. Double-clicking a particular server will open another tab that lists all endpoints and their corresponding interface IDs. Double-clicking an endpoint will open another tab that lists all procedures that can be invoked on that endpoint’s interface. Right-clicking on an endpoint will open a context menu that presents other useful shortcuts, one of which is to create a new client to connect to this endpoint’s interface. We’ll describe that feature in a later section.
You can locate other RPC servers that are not running (or are not ALPC) by parsing the server’s image by selecting File -> Load from binary…
and locating the image on disk, or by selecting File->Load from service…
and selecting the service of interest (this will parse all servers in all modules loaded in the service process).
Exploring the Library
The other starting point for navigating RPC servers is to load the library view. The library is a file containing serialized .NET objects for every RPC server you have discovered while using RPCI. Simply select the menu item Library -> Servers
to view all discovered RPC servers and Library -> Procedures
to view all discovered procedures for all server interfaces. Both menu items will open in new tabs. To perform a quick keyword search in either tab, simply right-click on any row and type a search term into the textbox. The screenshot below shows a keyword search for “()” to quickly view procedures that have zero arguments, which are useful starting points for experimenting with an interface.
The first time you run RPCI, the library needs to be seeded. To do this, navigate to Library -> Refresh
, and RPCI will attempt to parse RPC servers from all modules loaded in all processes that have a registered ALPC server. Note that this process could take quite a while and use several hundred megabytes of memory; this is because there are thousands of such modules, and during this process the binaries are re-mapped into memory and the public Microsoft symbol server is consulted. To make matters worse, the Dbghelp API is single-threaded and I suspect Microsoft’s public symbol server has rate-limiting logic.
You can periodically refresh the database to capture any new servers. The refresh operation will only add newly-discovered servers. If you need to rebuild the library from scratch (for example, because your symbols were wrong), you can either erase it using the menu item Library -> Erase
or manually delete the database file (rpcserver.db
) inside the current user’s roaming application data folder. Note that RPC servers that are discovered by using the File -> Load from binary…
and File -> Load from service…
menu items are automatically added to the library.
You can also export the entire library as text by selecting Library -> Export as Text
.
Creating a New RPC Client
One of the most powerful features of RPCI is the ability to dynamically interact with an RPC server of interest that is actively running. This is accomplished by creating a new client in the Client Workbench window. To open the Client Workbench window, right-click on the server of interest from the library servers or procedures tab and select New Client
.
The workbench window is organized into three panes:
- Static RPC server information
- A textbox containing dynamic client output
- A tab control containing client code and procedures tabs
The client code tab contains C# source code for the RPC client that was generated by NtApiDotNet
. The code has been modified to include a “Run” function, which is the “entry point” for the client. The procedures tab is a shortcut reference to the routines that are available in the selected RPC server interface, as the source code can be cumbersome to browse (something we are working to improve!).
The process for generating and running the client is simple:
- Modify the “Run” function to call one or more of the procedures exposed on the RPC server interface; you can print the result if needed.
- Click the “Run” button.
- Observe any output produced by “Run”
In the screenshot above, I picked the “Host Network Service” RPC server because it exposes some procedures whose names imply interesting administrator capabilities. With a few function calls to the RPC endpoint, I was able to interact with the service to dump the name of what appears to be a default virtual network related to Azure container isolation.
Sniffing RPC Traffic with ETW Data
Another useful feature of RPCI is that it provides visibility into RPC-related ETW data. ETW is a diagnostic capability built into the operating system. Many years ago ETW was very rudimentary, but since the Endpoint Detection and Response (EDR) market exploded in the last decade, Microsoft has evolved ETW into an extremely rich source of information about what’s going on in the system. The gist of how ETW works is that an ETW provider (typically a service or an operating system component) emits well-structured data in “event” packets and an application can consume those events to diagnose performance issues.
RPCI registers as a consumer of such events from the Microsoft-RPC (MSRPC) ETW provider and displays those events in real time in either table or graph format. To start the RPC Sniffer tool, navigate to Tools -> RPC Sniffer…
and click the “play” button in the toolbar. Both the table and graph will be updated every few seconds as events begin to arrive.
The events emitted by the MSRPC provider are fairly simple. The events record the results of RPC calls between a client and server in RpcClientCall and RpcServerCall start and stop task pairs. The start events contain detailed information about the RPC server interface, such as the protocol, procedure number, options, and authentication used in the call. The stop events are typically less interesting but do include a status code. By correlating the call start/stop events between a particular RPC server and the requesting process, we can begin to make sense of the operations that are in progress on the system. In the table view, it’s easier to see these event pairs when the ETW data is grouped by ActivityId (click the “Group” button in the toolbar), as shown below.
The data can be overwhelming, because ETW is fairly noisy by design, but the graph view can help you wade through the noise. To use the graph view, simply click the “Node” button in the toolbar at any time during the trace. To switch back to the table view, click the “Node” button again.
A long-running trace will produce a busy graph like the one above. You can pan, zoom, and change the graph layout type to help drill into interesting server activity. We are exploring additional ways to improve this visualization!
In the zoomed-in screenshot above, we can see individual service processes that are interacting with system services such as Base Filtering Engine (BFE, the Windows Defender firewall service), NSI, and LSASS.
Here are some other helpful tips to keep in mind when using the RPC Sniffer tool:
- Keep RPCI diagnostic tracing disabled in Settings.
- Do not enable ETW debug events; these produce a lot of noise and can exhaust process memory after a few minutes.
- For optimum performance, use a release build of RPCI.
- Consider docking the main window adjacent to the sniffer window so that you can navigate between ETW data and library data (right-click on a table row and select
Open in library
or click on any RPC node while in the graph view). - Remember that the graph view will refresh every few seconds, which might cause you to lose your place if you are zooming and panning. The best use of the graph view is to take a capture for a fixed time window and explore the graph after the capture has been stopped.
What’s Next?
We plan to accomplish the following as we continue developing RPCI:
- Improve the code editor in the Client Workbench
- Improve the autogeneration of names so that they are more intuitive
- Introduce more developer-friendly coding features
- Improve the coverage of RPC/ALPC servers that are not registered with the endpoint mapper
- Introduce an automated ALPC port connector/scanner
- Improve the search experience
- Extend the graph view to be more interactive
Related Research and Further Reading
Because MSRPC has been a popular research topic for well over a decade, there are too many related resources and research efforts to name here. We’ve listed a few below that we encountered while building this tool:
- Finding Running RPC Server Information with NtObjectManager by @tiraniddo
- From NtObjectManager to PetitPotam, From RpcView to PetitPotam and A Survey of Windows RPC Discovery Tools by @clearbluejar
- Understanding Windows Containers Communication by Eviatar Gerzi
- RPC Security Essentials on Microsoft Learn
If you would like to see the source code for other related RPC tools, we’ve listed a few below:
If you’re unfamiliar with RPC internals or need a technical refresher, we recommend checking out one of the authoritative sources on the topic, Alex Ionescu’s 2014 SyScan talk in Singapore, “All about the RPC, LRPC, ALPC, and LPC in your PC.”