In 2018, VerSprite’s research team invested a significant amount of time into abusing insecure Windows Communication Foundation (WCF) endpoints.
In our previous blog, we discuss the analysis and exploitation of this vulnerability class. In addition, we presented our research at Ekoparty 2018 where we alluded to the exploitation of a remotely accessible WCF endpoint vulnerability. Until now, we’ve not discussed the exploitation of remote WCF vulnerabilities in detail.
In this blog, we’ll be discussing the discovery, analysis, and exploitation of CVE-2019-8917 which is a remotely exploitable WCF vulnerability that we discovered in SolarWinds Orion NPM 12.3.
This issue was patched in the 12.4 release of SolarWinds Orion NPM which was made available in December of 2018.
We were inspired to discover a remotely exploitable WCF vulnerability that could be shared as an example of the remote attack surface introduced by insecure endpoints. We searched Google for results containing the error message “there is no endpoint listening at net.tcp” as is defined by .NET’s EndpointNotFound error. Our goal was to identify applications known to leverage WCF’s NetTcpBinding to expose a remote endpoint. This query led us to discover that SolarWinds exposes a remote WCF endpoint on port 17777 within all versions of NPM.
SolarWinds Orion NPM (Network Performance Monitor) is a software designed to reduce network outages and improve performance. As we sought to learn more about the WCF service exposed by NPM, we downloaded and installed the free trial edition within a virtual machine. We then reviewed the details of the service named OrionModuleEngine which was installed alongside NPM:
Note that this service uses “NetTcpPortSharing”.
This service is also started as “LocalSystem” which suggests that exploiting any of the WCF services that may be exposed by OrionModuleEngine will provide us with SYSTEM privileges.
This is a component of WCF that allows multiple services from different processes to share a single “net.tcp” port. Using dnSpy, we decompiled the assembly found at “C:\Program Files (x86)\SolarWinds\Orion\SolarWinds.BusinessLayerHost.exe” for analysis.
We found that when the service is started, it first handles license verification, then attempts to detect and load “plugins” immediately after. Log4net is used throughout this service to log the entire process to “C:\ProgramData\SolarWinds\Logs\Orion\BusinessLayerHost.log”. Reviewing this log file revealed to us the plugins that had been loaded:
... SolarWinds.BusinessLayerHost.PluginManager - Discovered plugins: SolarWinds.Orion.Core.BusinessLayer.dll SolarWinds.Orion.ServiceDirectory.BusinessLayer.dll Solarwinds.Settings.BusinessLayer.dll SolarWinds.AddNode.BusinessLayer.dll SolarWinds.AgentManagement.ServiceCore.dll SolarWinds.AgentManagement.Watchdog.dll SolarWinds.ASA.BusinessLayer.dll SolarWinds.Orion.Cli.BusinessLayer.dll ...
According to the decompiled source code, when the plugin associated with “SolarWinds.Orion.Core.BusinessLayer.dll” is loaded, a new instance of CoreBusinessLayerService is started.
The service definition found in “SolarWinds.Orion.Core.BusinessLayer.dll.config” informs us that there are several endpoint addresses associated with this service, including the following remotely accessible endpoint: “net.tcp://localhost:17777/orion/core/businesslayer”
The associated Service Contract we reviewed, ICoreBusinessLayer, exposed 383 different methods. Following a cursory analysis of several methods, we decided to focus our attention on the method named InvokeActionMethod.
The server-side implementation of InvokeActionMethod is defined in the ActionMethodInvoker class of the SolarWinds.Orion.Core.Actions namespace.
InvokeActionMethod attempts to match the user provided actionTypeID string against actionTypeID values associated with a known ActionMethodInvoker. If a matching ActionMethodInvoker is found, then it’s InvokeMethod method is called using the user provided methodName, and args arguments. Reviewing the decompiled source code for ActionTypeID strings revealed the following.
Each of the ActionTypeID strings correspond to an implementation namespace within SolarWinds.Orion.Core.Actions. Some of these namespaces include an invoker class that implements IActionMethodInvoker, which is required by InvokeActionMethod. This leaves us with the following action namespaces:
Obviously, the actions whose names begin with “Execute” immediately caught our attention. Next, we reviewed the decompiled source of the ExecuteExternalProgramInvoker class to determine which values should be passed for methodName and args.
Based on the source of the invoker’s InvokeMethod method, only one methodName is acceptable: ValidateAccess.
In addition, an instance of this action’s configuration class, ExecuteExternalProgramConfiguration, is created from a deserialization of the args parameter. The Data Contract associated with this class defines Data Members for the program path and active directory credentials.
Initially, we feared that this was the end of the road for this approach, as we presumed that the active directory credentials would be used for access validation. However, if the Credentials member of the configuration object are left null, the ValidataAccessToFile method will simply pass the ProgramPath string to a method named ExecuteWithoutCredentials.
This method will then call Microsoft.VisualBasic.Interaction.Shell and proceed to execute our program as the current user, “NT AUTHORITY\SYSTEM”.
Armed with knowledge of the actionTypeID, methodName, and args values required to reach an exploitable code path from the InvokeActionMethod method, our next step is exploitation.
Most of our WCF exploits have abused the NetNamedPipeBinding, gaining us privilege escalation. However, the NetTcpBinding was less familiar to us, so we consulted the decompiled source code for guidance on communicating with the service endpoint.
Although, we have remotely exploited insecure WCF endpoints in the past, we had yet to encounter any transport security, until now. This endpoint requires credentials in the form of a UserName and Password string in order to successfully establish a connection:
Luckily, the GetPassword method simply returns a Base64 encoded hash of the username that we provide. By leveraging the MessageUtilities class in our exploit, authentication becomes trivial.
Another roadblock we encounter was that attempting to connect to the target using an IP address resulted in the occurrence of a DNS identity error which terminated our connection.
Identity check failed for outgoing message. The expected DNS identity of the remote endpoint was '192.168.1.243' but the remote endpoint provided DNS claim 'MSEDGEWIN10'. If this is a legitimate remote endpoint, you can fix the problem by explicitly specifying DNS identity 'MSEDGEWIN10' as the Identity property of EndpointAddress when creating channel proxy.
As a workaround, we first attempted a connection using the target’s IP address, then we parsed to DNS identity error message in or to retrieve the expected DNS identify. Finally, we attempted a second connection providing this new explicit endpoint identity as part of our EndpointAddress.
After this was sorted, we were able to successful create a channel to the CoreBusinessLayer service. Next, we want to call the remote InvokeActionMethod method, but first we must populate the configuration object to be serialized as our args argument.
The command we chose for the ProgramPath Data Member was a PowerShell reverse connect one-liner from Nishang, an open-source PowerShell framework for red teaming. A TCP listener is launched at the beginning of the exploit in preparation for receiving this reverse connect shell.
For the Credentials Data Member, we simply set the value to null to circumvent access validation, as explained earlier.
After our configuration object is populated, we call InvokeActionMethod with “ExecuteExternalProgram” as the actionTypeID argument, “ValidateAccess” as the methodName argument, and we serialize our configuration object to and XML string so that it may be used as our args argument.
Moments after our exploit is launched, we are greeted by a SYSTEM shell courtesy of the vulnerable target.
When we reported this security issue to SolarWinds, their product security and incident response team were very receptive. We worked together ensure that a patch would be made available prior to disclosure. As mentioned earlier, this issue is patched in the SolarWinds Orion NPM 12.4 release. In the patched version of this software, it is no longer trivial for arbitrary clients to authenticate to the service and call remote methods.
This case study of remote WCF endpoint exploitation should help to clarify the threat impact of this vulnerability class.
As the adoption of WCF continues to increase, so does the chance of exposure for insecure endpoint. We encourage you to help us spread awareness of this vulnerability class to security professionals and software developers alike.
If you’d like to read more on vulnerabilities and exploit development from our research team, be sure to check out our latest presentation on Abusing Insecure WCF Endpoints.
Learn useful techniques to identify vulnerable WCF services, discover what to look for when analyzing decomposed .NET assemblies, including those that have been obfuscated, and watch a demonstration of attacks against real software.
Learn More →
VerSprite's Research and Development division (a.k.a VS-Labs) is comprised of individuals who are passionate about diving into the internals of various technologies. From advanced technical security training to our research for hire B.O.S.S offering, we help organizations solve their most complex technical challenges.
Learn More →