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.
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":
CLI Options:
--dotnetassembly: .NET Assembly to load/inject.
--assemblyargs: .NET assembly arguments.
--unlink-modules: Unlink .NET related modules such as CLR/MsCoree related DLLs from PEB data structures.
--stomp-headers: Stomp .NET assembly and reflective DLL PE DOS headers.
--etw: Bypass event tracing on windows (ETW).
--amsi: Bypass AMSI.
--spawnto: Choose spawnto process, list of .NET binaries loading the CLR by default when executed:
- PresentationHost.exe
- stordiag.exe
- ScriptRunner.exe
- caitstatic.exe
- Microsoft.Uev.SyncController.exe
- TsWpfWrp.exe
- UevAgentPolicyGenerator.exe
- UevAppMonitor.exe
- FileHistory.exe
- UevTemplateBaselineGenerator.exe
- UevTemplateConfigItemGenerator.exe
Check spawnto-list.txt for extra MS binaries loading the 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 may be)
ExecuteAssembly --dotnetassembly /tmp/Seatbelt.exe --assemblyargs LogonSessions --unlink-modules --stomp-headers --amsi --etw --spawnto PresentationHost.exe
ExecuteAssembly --amsi --etw --unlink-modules --stomp-headers --dotnetassembly /tmp/ghostpack/SharPersist.exe --assemblyargs -t reg -c "C:\Windows\SysWow64\mshta.exe C:\Users\admin\Downloads\Test2.hta" -k logonscript -m add --spawnto FileHistory.exe
ExecuteAssembly --unlink-modules --stomp-headers --dotnetassembly /tmp/ghostpack/SharPersist.exe --assemblyargs -t reg -k "logonscript" -v "C:\Windows\SysWow64\mshta.exe C:\Users\admin\Downloads\Test.hta" -m remove --spawnto FileHistory.exe
ExecuteAssembly --unlink-modules --amsi --dotnetassembly /tmp/ghostpack/SharpWMI.exe --assemblyargs action=query computername=localhost query="select * from win32_service" --spawnto FileHistory.exe
ExecuteAssembly --amsi --etw --dotnetassembly /tmp/ghostpack/SharpWMI.exe --assemblyargs action=query query="select * from win32_process" --spawnto PresentationHost.exe
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)ETW_FLAG
: 0|1STOMPHEADERS_FLAG
: 0|1UNLINKMODULES_FLAG
: 0|1LENGTH_FLAG
: .NET assembly size in bytesLL_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