James Forshaw - 24th July 2012
In May 2012 Microsoft released MS12-035 which was a security update for all versions of the .NET framework (including v4.0) based on some security research I performed over 12 months prior. It aimed to fix some serious issues in the way the .NET framework handled binary object serialization, which could lead to remote code execution or privilege escalation. While the update certainly mitigated some of the more immediate threats there is still a significant attack surface left, which leads me to the topic of this blog post; if you use .NET remoting services in your estate then you might be at risk of compromise.
This post is not going to be an in-depth analysis of my research; if that is your interest then I would highly recommend reading the Blackhat USA 2012 whitepaper on the subject I wrote to accompany my presentation at the conference. Instead it will focus on one particular technology which has most impact on our corporate clients. While the research presented at Blackhat ISA 2012 covered a number of possible technologies, including escaping from sandboxed XAML Browser Applications, the technology this post will focus on is .NET remoting, something I have encountered numerous times in engagements in my years of security consultancy.
To start, what is serialization? Serialization could be described as a process for taking instances of live objects in a system and applying a function that converts them into a form which can be persisted to disk or transmitted over the network. An object in this case is a fundamental type in the .NET framework; it acts to encapsulate data into a single structure which can be easily referenced. Another important aspect is that this process can be round-tripped, i.e. the function can be inverted and the original object restored from the persisted representation.
Binary serialization, which was the specific topic of my research and Blackhat presentation, is a built in set of functionality in the .NET framework which is used in a myriad of places. It is especially powerful as it provides a significant amount of flexibility in what types of objects it can handle, a developer only has to add one line to an object type definition to automatically gain serialization functionality. But this can lead to sloppy programming and the framework can end up exposing objects outside of the context in which they were expected to be used leading to security vulnerabilities.
.NET remoting is a specific technology which has been in place since the very first version of the framework which requires binary serialization to function. It implements a set of core functionality which allows a developer to expose .NET object instances to remote clients, either via a TCP/IP network or between users on the same machine over common inter-process communication mechanisms. Its goal is similar to technologies such as DCOM, CORBA and Java RMI which allow a service to expose objects to clients while integrating with minimal overhead on the development language.
These minimal overheads, especially in the case of .NET which requires almost no changes to make it work, provide significant power and flexibility in the hands of a developer to get the job done. It is certainly possible to write unsafe remote services, for example if you exposed a call which executed an arbitrary process on the server machine or something equally dangerous, that is an issue which can be audited through security review. Unfortunately the power of remoting and the level of built-in functionality outside of the developer’s control bring with it the potential for serious harm; a mechanism to achieve this was identified through my research.
The reason I am discussing .NET remoting and binary serialization is that unless a developer went to significant effort to change the defaults remoting uses binary serialization as its transport mechanism. The most significant issue I identified during my research which relates to remoting is the implicit functionality in certain object types, this functionality kicks in just through the act of deserializing the incoming stream of bytes from the client. This acts inside the framework’s own code; it does not even expose this process to the developer’s implementation. Through this functionality a malicious actor could attack your unsecured services (or reverse the attack scenario and attack clients if they were able to impersonate the server) and in the worst case get arbitrary remote code execution on the machine on which they are hosted.
A real example of an attack is the ability to induce the service to make an SMB request to a remote machine, which can be used to relay credentials to gain access to other Windows machines on a network (commonly referred to as SMB relaying) permitting the execution of arbitrary processes on the target.
That these issues exist should not come as a surprise as Microsoft document it (see here) but the problem is various issues conspire to put you at risk even if you do not change the default “secure” configuration. So what can you do about it as a security architect or developer that might be considering a new .NET-based remotely-accessible system?
The official recommendation from Microsoft is not to use .NET remoting at all, they consider it a legacy technology. Starting with version 3.0 of the framework the technology was effectively (although not officially) deprecated in favour of Windows Communication Foundation (WCF). This provides a more structured form of serialization which requires explicit annotation of object types to function. This significantly limits the risk, although there are always ways in which a poor design could still cause issues, but that is directly manageable.
If the technology cannot be changed, especially in legacy environment, then there are still some things you can do to mitigate the risks involved:
- The most obvious one, do not expose the services to untrusted networks such as the Internet.
- Enable authentication, this can be as simple as passing a single flag when registering a remoting channel (although this will not prevent an attacker inverting the attack and exploiting clients).
- Implement end to end SSL endpoint authentication (for example see this MSDN magazine article).
- Ensure that the TypeFilterLevel for the remoting services has not been changed from the default ‘Low’ setting.
- Each service is referred to through its ‘Well Known Name’. This is a string provided by the developer and is the only piece of information an attacker would need to know to attack your service. By choosing a long, randomly generated value (at design time, not each time it is started) it will increase the difficulty of a brute force attack against an unknown service, although it will not protect it if the attacker can see it through network capture or by having a copy of a client.
- Run the service as a local account with limited permissions, this would potentially limit the damage if it was compromised.
So what are the conclusions from all this? The biggest danger inherent to large, complex frameworks such as .NET remoting is the quantity of implicit functionality that a developer never sees. If that functionality has potential for security issues it might not be evident just from the review of the source code itself. Of course developing a completely bespoke infrastructure just so that it can be code-reviewed would be foolhardy, and the advantages of a well designed, secure framework are many. So if you are using .NET remoting in an untrusted environment, with no authentication or other mechanisms to protect it, you might want to review the security of it to ensure you cannot be compromised.