ExecuteAssembly: Load/Inject .NET assemblies
ExecuteAssembly
ExecuteAssembly is an alternative of CS execute-assembly, built with C/C++ and it can be used to Load/Inject .NET assemblies by; reusing the host (spawnto) process loaded CLR Modules/AppDomainManager, Stomping Loader/.NET assembly PE DOS headers, Unlinking .NET related modules, bypassing ETW+AMSI, avoiding EDR hooks via NT static syscalls (x64) and hiding imports by dynamically resolving APIs via superfasthash hashing algorithm.
TLDR (Features):
- CLR related modules unlinking from PEB data structures. (use MS “ListDLLs” utility instead of PH for confirmation)
- .NET Assembly and Reflective DLL headers stomping (MZ bytes, e_lfanew, DOS Header, Rich Text, PE Header).
- Use of static hardcoded syscalls for bypassing EDR Hooks. (x64 support only for now, from WinXP to Win10 19042)
- CLR “AppDomain/AppDomainManager” enumeration and re-use (ICLRMetaHost->EnumerateLoadedRuntimes), just set the spawnto/host process to a known Windows .NET process.
- Dynamic Resolution of WIN32 APIs (PEB) using APIs corresponding hash (SuperFastHash)
- AMSI and ETW patching prior to loading .NET assemblies.
- .NET assembly bytes parsing and scanning for the CLR version to load/use.
- No use of GetProcAddress/LoadLibrary/GetModuleHandle for ETW bypass.
- CLR Hosting using v4 COM API & Reflective DLL injection
Usage:
- x64(syscalls): this version depends mainly on the use of static syscalls to bypass EDR hooks, you can use this version to build the x64 version of the DLL only (x64 support only for now).
- x86|x64(PEB): retrieves required API addresses dynamically at runtime by walking the PEB modules EAP tables and resolving APIs via superfasthash hash. however doesn’t account for EDR hooks placed either on kernel32.dll or ntdll.dll, you can use this version to build both the x86 and x64 DLLs or only the x86 DLL and use x64(syscalls) version for building the x64 DLL to account for common EDR hooks.
- Build the required DLLs using VS2017 and/or Windows SDK 10.0.17134.0 (or compatible sdk versions).
- Make sure gzip is installed and the following artifacts are placed within the same folder then just load the aggressor script “ExecuteAssembly.cna”:
- ExecuteAssembly.cna
- ExecuteAssembly-x64.dll
- ExecuteAssembly-x86.dll
- CLI Options:
- Check spawnto-list.txt for extra MS binaries loading the CLR by default and are good candidates to set as a spawnto. (would avoid the known LOLBins unless if it is a dev’s machine maybe)
C2 Support:
Was created and tested mainly on cobalt strike, however it can be used with other C2 frameworks as well (MSF ..etc), just keep in mind that the reflective DLL DLLMAIN is expecting the one-liner payload as a parameter (lpReserved) in the following format (with no “.”);
- AMSI_FLAG|ETW_FLAG|STOMPHEADERS_FLAG|UNLINKMODULES_FLAG|LL_FLAG.LENGTH_FLAG.B64_ENCODED_COMPRESSED_PAYLOAD [SPACE SEPARATED ARGUMENTS]
- AMSI_FLAG: 0|1 (either 0 or 1)
: 0|1
- STOMPHEADERS_FLAG: 0|1
- UNLINKMODULES_FLAG: 0|1
- LENGTH_FLAG: .NET assembly size in bytes
- LL_FLAG: length_of(LENGTH_FLAG) (just bear with me here or pretend you didn’t read this)
- B64_ENCODED_COMPRESSED_PAYLOAD: Gzip compressed and base64 encoded .NET assembly.
- [SPACE SEPARATED ARGUMENTS]: .NET assembly arguments
- AMSI_FLAG: 0|1 (either 0 or 1)