From 0fc8ff3caa39fa2bbd5dad6482edb31ed4c635f0 Mon Sep 17 00:00:00 2001 From: Jakob Friedl <71284620+jakobfriedl@users.noreply.github.com> Date: Fri, 17 Oct 2025 09:42:08 +0200 Subject: [PATCH] Standardized console spacing between commands. --- src/agent/nim.cfg | 2 +- src/client/views/console.nim | 5 +++-- src/modules/filesystem.nim | 6 +++--- src/modules/filetransfer.nim | 2 +- src/modules/sleep.nim | 2 +- src/modules/systeminfo.nim | 4 ++-- src/modules/token.nim | 9 ++++----- src/server/api/handlers.nim | 7 ++++--- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/agent/nim.cfg b/src/agent/nim.cfg index e4784a2..02d118e 100644 --- a/src/agent/nim.cfg +++ b/src/agent/nim.cfg @@ -3,6 +3,6 @@ -d:release --opt:size --passL:"-s" # Strip symbols, such as sensitive function names --d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" +-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" -d:MODULES="511" -o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe" \ No newline at end of file diff --git a/src/client/views/console.nim b/src/client/views/console.nim index 38f328e..3a54f67 100644 --- a/src/client/views/console.nim +++ b/src/client/views/console.nim @@ -150,7 +150,6 @@ proc displayHelp(component: ConsoleComponent) = for module in getModules(component.agent.modules): for cmd in module.commands: component.console.addItem(LOG_OUTPUT, " * " & cmd.name.alignLeft(15) & cmd.description) - component.console.addItem(LOG_OUTPUT, "") proc displayCommandHelp(component: ConsoleComponent, command: Command) = var usage = command.name & " " & command.arguments.mapIt( @@ -172,7 +171,6 @@ proc displayCommandHelp(component: ConsoleComponent, command: Command) = for arg in command.arguments: let isRequired = if arg.isRequired: "YES" else: "NO" component.console.addItem(LOG_OUTPUT, " * " & arg.name.alignLeft(15) & " " & ($arg.argumentType).toUpperAscii().alignLeft(6) & " " & isRequired.align(8) & " " & arg.description) - component.console.addItem(LOG_OUTPUT, "") proc handleHelp(component: ConsoleComponent, parsed: seq[string]) = try: @@ -185,6 +183,9 @@ proc handleHelp(component: ConsoleComponent, parsed: seq[string]) = # Command was not found component.console.addItem(LOG_ERROR, "The command '" & parsed[1] & "' does not exist.") + # Add newline at the end of help text + component.console.addItem(LOG_OUTPUT, "") + proc handleAgentCommand*(component: ConsoleComponent, connection: WsConnection, input: string) = # Convert user input into sequence of string arguments let parsedArgs = parseInput(input) diff --git a/src/modules/filesystem.nim b/src/modules/filesystem.nim index 77b535a..5227f27 100644 --- a/src/modules/filesystem.nim +++ b/src/modules/filesystem.nim @@ -118,7 +118,7 @@ when defined(agent): if length == 0: raise newException(OSError, fmt"Failed to get working directory ({GetLastError()}).") - let output = $buffer[0 ..< (int)length] & "\n" + let output = $buffer[0 ..< (int)length] return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(output)) except CatchableError as err: @@ -186,7 +186,7 @@ when defined(agent): hFind = FindFirstFileW(searchPatternW, &findData) if hFind == INVALID_HANDLE_VALUE: - raise newException(OSError, fmt"Failed to find files ({GetLastError()}).") + raise newException(OSError, fmt"Failed to list files ({GetLastError()}).") # Directory was found and can be listed else: @@ -286,7 +286,7 @@ when defined(agent): # Add summary of how many files/directories have been found output &= "\n" & fmt"{totalFiles} file(s)" & "\n" - output &= fmt"{totalDirs} dir(s)" & "\n" + output &= fmt"{totalDirs} dir(s)" return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(output)) diff --git a/src/modules/filetransfer.nim b/src/modules/filetransfer.nim index 61d042e..cc758f7 100644 --- a/src/modules/filetransfer.nim +++ b/src/modules/filetransfer.nim @@ -83,7 +83,7 @@ when defined(agent): let destination = fmt"{paths.getCurrentDir()}\{fileName}" writeFile(fmt"{destination}", fileContents) - return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(fmt"File uploaded to {destination}." & "\n")) + return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(fmt"File uploaded to {destination}.")) except CatchableError as err: return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) diff --git a/src/modules/sleep.nim b/src/modules/sleep.nim index 1a68612..07a9493 100644 --- a/src/modules/sleep.nim +++ b/src/modules/sleep.nim @@ -68,7 +68,7 @@ when defined(agent): case int(task.argCount): of 0: # Retrieve sleepmask settings - let response = fmt"Sleepmask settings: Technique: {$ctx.sleepTechnique}, Delay: {$ctx.sleep}ms, Stack spoofing: {$ctx.spoofStack}" & "\n" + let response = fmt"Sleepmask settings: Technique: {$ctx.sleepTechnique}, Delay: {$ctx.sleep}ms, Stack spoofing: {$ctx.spoofStack}" return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(response)) of 1: diff --git a/src/modules/systeminfo.nim b/src/modules/systeminfo.nim index ea6b4a3..78d411d 100644 --- a/src/modules/systeminfo.nim +++ b/src/modules/systeminfo.nim @@ -61,7 +61,7 @@ when defined(agent): # Take a snapshot of running processes let hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) if hSnapshot == INVALID_HANDLE_VALUE: - raise newException(CatchableError, protect("Invalid permissions.\n")) + raise newException(CatchableError, protect("Invalid permissions.")) # Close handle after object is no longer used defer: CloseHandle(hSnapshot) @@ -71,7 +71,7 @@ when defined(agent): # Loop over processes to fill the map if Process32First(hSnapshot, addr pe32) == FALSE: - raise newException(CatchableError, protect("Failed to get processes.\n")) + raise newException(CatchableError, protect("Failed to get processes.")) while true: var procInfo = ProcessInfo( diff --git a/src/modules/token.nim b/src/modules/token.nim index e4a2879..c90aded 100644 --- a/src/modules/token.nim +++ b/src/modules/token.nim @@ -4,7 +4,6 @@ import ../common/[types, utils] proc executeMakeToken(ctx: AgentCtx, task: Task): TaskResult proc executeRev2Self(ctx: AgentCtx, task: Task): TaskResult - # Module definition let module* = Module( name: protect("token"), @@ -17,7 +16,7 @@ let module* = Module( description: protect("Create an access token from username and password."), example: protect("make-token LAB\\john Password123!"), arguments: @[ - Argument(name: protect("username"), description: protect("Account username prefixed with domain in format: domain\\username."), argumentType: STRING, isRequired: true), + Argument(name: protect("domain\\username"), description: protect("Account domain and username."), argumentType: STRING, isRequired: true), Argument(name: protect("password"), description: protect("Account password."), argumentType: STRING, isRequired: true), Argument(name: protect("logonType"), description: protect("Logon type (https://learn.microsoft.com/en-us/windows-server/identity/securing-privileged-access/reference-tools-logon-types)."), argumentType: INT, isRequired: false) ], @@ -59,14 +58,14 @@ when defined(agent): # Split username and domain at separator '\' let userParts = username.split("\\", 1) if userParts.len() != 2: - raise newException(CatchableError, protect("Invalid username format. Expected: \\.\n")) + raise newException(CatchableError, protect("Expected format domain\\username.")) if task.argCount == 3: logonType = cast[DWORD](Bytes.toUint32(task.args[2].data)) if not makeToken(userParts[1], password, userParts[0], logonType): - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(protect("Failed to create token.\n"))) - return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(fmt"Impersonated {username}." & "\n")) + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(protect("Failed to create token."))) + return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(fmt"Impersonated {username}.")) except CatchableError as err: return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) diff --git a/src/server/api/handlers.nim b/src/server/api/handlers.nim index 3f7d97f..230b29f 100644 --- a/src/server/api/handlers.nim +++ b/src/server/api/handlers.nim @@ -146,10 +146,11 @@ proc handleResult*(resultData: seq[byte]) = cq.client.sendLoot(lootItem) cq.output(fmt"File downloaded to {downloadPath} ({$fileData.len()} bytes).", "\n") - cq.client.sendConsoleItem(agentId, LOG_OUTPUT, fmt"File downloaded to {downloadPath} ({$fileData.len()} bytes)." & "\n") + cq.client.sendConsoleItem(agentId, LOG_OUTPUT, fmt"File downloaded to {downloadPath} ({$fileData.len()} bytes).") + else: discard - of RESULT_NO_OUTPUT: - cq.output() + # Send newline to separate commands + cq.client.sendConsoleItem(agentId, LOG_OUTPUT, "") except CatchableError as err: cq.error(err.msg, "\n")