Improved logging format.

This commit is contained in:
Jakob Friedl
2025-08-21 15:08:52 +02:00
parent f69adc53a2
commit c9df7aba64
18 changed files with 91 additions and 115 deletions

View File

@@ -1,4 +1,4 @@
import ../common/[types, utils] import ../common/types
# Declare function prototypes # Declare function prototypes
proc executePs(ctx: AgentCtx, task: Task): TaskResult proc executePs(ctx: AgentCtx, task: Task): TaskResult
@@ -44,6 +44,7 @@ when defined(agent):
import winim import winim
import os, strutils, sequtils, strformat, tables, algorithm import os, strutils, sequtils, strformat, tables, algorithm
import ../agent/protocol/result import ../agent/protocol/result
import ../common/utils
# TODO: Add user context to process information # TODO: Add user context to process information
type type

View File

@@ -1,4 +1,4 @@
import ../common/[types, utils] import ../common/types
# Define function prototypes # Define function prototypes
proc executePwd(ctx: AgentCtx, task: Task): TaskResult proc executePwd(ctx: AgentCtx, task: Task): TaskResult
@@ -97,6 +97,7 @@ when defined(agent):
import os, strutils, strformat, times, algorithm, winim import os, strutils, strformat, times, algorithm, winim
import ../agent/protocol/result import ../agent/protocol/result
import ../common/utils
# Retrieve current working directory # Retrieve current working directory
proc executePwd(ctx: AgentCtx, task: Task): TaskResult = proc executePwd(ctx: AgentCtx, task: Task): TaskResult =

View File

@@ -1,5 +1,5 @@
import tables, strformat import tables, strformat
import ../common/[types, utils] import ../common/types
# Import modules # Import modules
import import

View File

@@ -1,4 +1,4 @@
import ../common/[types, utils] import ../common/types
# Define function prototype # Define function prototype
proc executeShell(ctx: AgentCtx, task: Task): TaskResult proc executeShell(ctx: AgentCtx, task: Task): TaskResult
@@ -24,8 +24,9 @@ when defined(server):
when defined(agent): when defined(agent):
import ../agent/protocol/result
import osproc, strutils, strformat import osproc, strutils, strformat
import ../agent/protocol/result
import ../common/utils
proc executeShell(ctx: AgentCtx, task: Task): TaskResult = proc executeShell(ctx: AgentCtx, task: Task): TaskResult =
try: try:

View File

@@ -1,4 +1,4 @@
import ../common/[types, utils] import ../common/types
# Define function prototype # Define function prototype
proc executeSleep(ctx: AgentCtx, task: Task): TaskResult proc executeSleep(ctx: AgentCtx, task: Task): TaskResult
@@ -8,7 +8,7 @@ let commands* = @[
Command( Command(
name: "sleep", name: "sleep",
commandType: CMD_SLEEP, commandType: CMD_SLEEP,
description: "Update sleep delay ctxuration.", description: "Update sleep delay configuration.",
example: "sleep 5", example: "sleep 5",
arguments: @[ arguments: @[
Argument(name: "delay", description: "Delay in seconds.", argumentType: INT, isRequired: true) Argument(name: "delay", description: "Delay in seconds.", argumentType: INT, isRequired: true)
@@ -25,6 +25,7 @@ when defined(agent):
import os, strutils, strformat import os, strutils, strformat
import ../agent/protocol/result import ../agent/protocol/result
import ../common/utils
proc executeSleep(ctx: AgentCtx, task: Task): TaskResult = proc executeSleep(ctx: AgentCtx, task: Task): TaskResult =

View File

@@ -1,9 +1,9 @@
import terminal, strformat, strutils, sequtils, tables, times, system import terminal, strformat, strutils, sequtils, tables, times, system
import ../core/logger
import ../[utils, globals] import ../[utils, globals]
import ../db/database import ../db/database
import ../protocol/packer import ../protocol/packer
import ../core/logger
import ../../common/[types, utils] import ../../common/[types, utils]
#[ #[
@@ -22,13 +22,14 @@ proc register*(registrationData: seq[byte]): bool =
cq.writeLine(fgRed, styleBright, fmt"[ - ] {agent.ip} attempted to register to non-existent listener: {agent.listenerId}.", "\n") cq.writeLine(fgRed, styleBright, fmt"[ - ] {agent.ip} attempted to register to non-existent listener: {agent.listenerId}.", "\n")
return false return false
# # Store agent in database # Store agent in database
if not cq.dbStoreAgent(agent): if not cq.dbStoreAgent(agent):
cq.writeLine(fgRed, styleBright, fmt"[ - ] Failed to insert agent {agent.agentId} into database.", "\n") cq.writeLine(fgRed, styleBright, fmt"[ - ] Failed to insert agent {agent.agentId} into database.", "\n")
return false return false
# Create log directory
if not cq.makeAgentLogDirectory(agent.agentId): if not cq.makeAgentLogDirectory(agent.agentId):
cq.writeLine(fgRed, styleBright, "[-] Failed to create log") cq.writeLine(fgRed, styleBright, "[ - ] Failed to create writeLine")
return false return false
cq.agents[agent.agentId] = agent cq.agents[agent.agentId] = agent
@@ -81,24 +82,23 @@ proc handleResult*(resultData: seq[byte]) =
agentId = Uuid.toString(taskResult.header.agentId) agentId = Uuid.toString(taskResult.header.agentId)
listenerId = Uuid.toString(taskResult.listenerId) listenerId = Uuid.toString(taskResult.listenerId)
let date: string = now().format("dd-MM-yyyy HH:mm:ss") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] [ * ] ", resetStyle, fmt"{$resultData.len} bytes received.")
cq.info(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, fmt"{$resultData.len} bytes received.")
case cast[StatusType](taskResult.status): case cast[StatusType](taskResult.status):
of STATUS_COMPLETED: of STATUS_COMPLETED:
cq.success(fgBlack, styleBright, fmt"[{date}]", fgGreen, " [+] ", resetStyle, fmt"Task {taskId} completed.") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}]", fgGreen, " [ + ] ", resetStyle, fmt"Task {taskId} completed.")
of STATUS_FAILED: of STATUS_FAILED:
cq.error(fgBlack, styleBright, fmt"[{date}]", fgRed, styleBright, " [-] ", resetStyle, fmt"Task {taskId} failed.") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}]", fgRed, styleBright, " [ - ] ", resetStyle, fmt"Task {taskId} failed.")
of STATUS_IN_PROGRESS: of STATUS_IN_PROGRESS:
discard discard
case cast[ResultType](taskResult.resultType): case cast[ResultType](taskResult.resultType):
of RESULT_STRING: of RESULT_STRING:
if int(taskResult.length) > 0: if int(taskResult.length) > 0:
cq.info(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, "Output:") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] [ * ] ", resetStyle, "Output:")
# Split result string on newline to keep formatting # Split result string on newline to keep formatting
for line in Bytes.toString(taskResult.data).split("\n"): for line in Bytes.toString(taskResult.data).split("\n"):
cq.output(line) cq.writeLine(line)
of RESULT_BINARY: of RESULT_BINARY:
# Write binary data to a file # Write binary data to a file

View File

@@ -91,8 +91,7 @@ proc httpGet*(ctx: Context) {.async.} =
ctx.handled = true # Ensure that HTTP response is sent only once ctx.handled = true # Ensure that HTTP response is sent only once
# Notify operator that agent collected tasks # Notify operator that agent collected tasks
let date = now().format("dd-MM-yyyy HH:mm:ss") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] [ * ] ", resetStyle, fmt"{$response.len} bytes sent.")
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, fmt"{$response.len} bytes sent.")
except CatchableError: except CatchableError:
resp "", Http404 resp "", Http404

View File

@@ -1,6 +1,6 @@
import terminal, strformat, strutils, tables, times, system, parsetoml import terminal, strformat, strutils, tables, times, system, parsetoml
import ./[task, logger] import ./task
import ../utils import ../utils
import ../db/database import ../db/database
import ../../common/types import ../../common/types
@@ -114,12 +114,11 @@ proc agentInteract*(cq: Conquest, name: string) =
var command: string = "" var command: string = ""
# Change prompt indicator to show agent interaction # Change prompt indicator to show agent interaction
cq.interactAgent = agent
cq.setIndicator(fmt"[{agent.agentId}]> ") cq.setIndicator(fmt"[{agent.agentId}]> ")
cq.setStatusBar(@[("[mode]", "interact"), ("[username]", fmt"{agent.username}"), ("[hostname]", fmt"{agent.hostname}"), ("[ip]", fmt"{agent.ip}"), ("[domain]", fmt"{agent.domain}")]) cq.setStatusBar(@[("[mode]", "interact"), ("[username]", fmt"{agent.username}"), ("[hostname]", fmt"{agent.hostname}"), ("[ip]", fmt"{agent.ip}"), ("[domain]", fmt"{agent.domain}")])
cq.writeLine(fgYellow, styleBright, "[+] ", resetStyle, fmt"Started interacting with agent ", fgYellow, styleBright, agent.agentId, resetStyle, ". Type 'help' to list available commands.\n")
cq.interactAgent = agent
cq.log(fmt"Started interacting with agent {agent.agentId}.") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", fgYellow, "[ + ] ", resetStyle, fmt"Started interacting with agent ", fgYellow, styleBright, agent.agentId, resetStyle, ". Type 'help' to list available commands.\n")
while command.replace(" ", "") != "back": while command.replace(" ", "") != "back":
command = cq.readLine() command = cq.readLine()

View File

@@ -37,7 +37,7 @@ proc serializeConfiguration(cq: Conquest, listener: Listener, sleep: int): seq[b
wipeKey(aesKey) wipeKey(aesKey)
cq.writeLine(fgBlack, styleBright, "[*] ", resetStyle, "Profile configuration serialized.") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] [ * ] ", resetStyle, "Profile configuration serialized.")
return encMaterial & encData return encMaterial & encData
proc replaceAfterPrefix(content, prefix, value: string): string = proc replaceAfterPrefix(content, prefix, value: string): string =
@@ -63,10 +63,10 @@ proc compile(cq: Conquest, placeholderLength: int): string =
.replaceAfterPrefix("-o:", exeFile) .replaceAfterPrefix("-o:", exeFile)
writeFile(configFile, config) writeFile(configFile, config)
cq.writeLine(fgBlack, styleBright, "[*] ", resetStyle, fmt"Placeholder created ({placeholder.len()} bytes).") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] [ * ] ", resetStyle, fmt"Placeholder created ({placeholder.len()} bytes).")
# Build agent by executing the ./build.sh script on the system. # Build agent by executing the ./build.sh script on the system.
cq.writeLine(fgBlack, styleBright, "[*] ", resetStyle, "Compiling agent.") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] [ * ] ", resetStyle, "Compiling agent.")
try: try:
# Using the startProcess function from the 'osproc' module, it is possible to retrieve the output as it is received, line-by-line instead of all at once # Using the startProcess function from the 'osproc' module, it is possible to retrieve the output as it is received, line-by-line instead of all at once
@@ -81,19 +81,19 @@ proc compile(cq: Conquest, placeholderLength: int): string =
# Check if the build succeeded or not # Check if the build succeeded or not
if exitCode == 0: if exitCode == 0:
cq.writeLine(fgGreen, "[*] ", resetStyle, "Agent payload generated successfully.") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", fgGreen, "[ + ] ", resetStyle, "Agent payload generated successfully.")
return exeFile return exeFile
else: else:
cq.writeLine(fgRed, styleBright, "[-] ", resetStyle, "Build script exited with code ", $exitCode) cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", fgRed, "[ - ] ", resetStyle, "Build script exited with code ", $exitCode)
return "" return ""
except CatchableError as err: except CatchableError as err:
cq.writeLine(fgRed, styleBright, "[-] ", resetStyle, "An error occurred: ", err.msg) cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", fgRed, "[ - ] ", resetStyle, "An error occurred: ", err.msg)
return "" return ""
proc patch(cq: Conquest, unpatchedExePath: string, configuration: seq[byte]): bool = proc patch(cq: Conquest, unpatchedExePath: string, configuration: seq[byte]): bool =
cq.writeLine(fgBlack, styleBright, "[*] ", resetStyle, "Patching profile configuration into agent.") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] [ * ] ", resetStyle, "Patching profile configuration into agent.")
try: try:
var exeBytes = readFile(unpatchedExePath) var exeBytes = readFile(unpatchedExePath)
@@ -103,17 +103,17 @@ proc patch(cq: Conquest, unpatchedExePath: string, configuration: seq[byte]): bo
if placeholderPos == -1: if placeholderPos == -1:
raise newException(CatchableError, "Placeholder not found.") raise newException(CatchableError, "Placeholder not found.")
cq.writeLine(fgBlack, styleBright, "[+] ", resetStyle, fmt"Placeholder found at offset 0x{placeholderPos:08X}.") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] [ + ] ", resetStyle, fmt"Placeholder found at offset 0x{placeholderPos:08X}.")
# Patch placeholder bytes # Patch placeholder bytes
for i, c in Bytes.toString(configuration): for i, c in Bytes.toString(configuration):
exeBytes[placeholderPos + i] = c exeBytes[placeholderPos + i] = c
writeFile(unpatchedExePath, exeBytes) writeFile(unpatchedExePath, exeBytes)
cq.writeLine(fgGreen, "[+] ", resetStyle, fmt"Agent payload patched successfully: {unpatchedExePath}.") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", fgGreen, "[ + ] ", resetStyle, fmt"Agent payload patched successfully: {unpatchedExePath}.")
except CatchableError as err: except CatchableError as err:
cq.writeLine(fgRed, styleBright, "[-] ", resetStyle, "An error occurred: ", err.msg) cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", fgRed, styleBright, "[ - ] ", resetStyle, "An error occurred: ", err.msg)
return false return false
return true return true

View File

@@ -1,7 +1,5 @@
import strformat, strutils, sequtils, terminal import strformat, strutils, terminal
import prologue, parsetoml import prologue, parsetoml
import sugar
import ../utils import ../utils
import ../api/routes import ../api/routes

View File

@@ -1,4 +1,4 @@
import terminal, times, strformat, strutils import times, strformat, strutils
import std/[dirs, paths] import std/[dirs, paths]
import ../../common/[types, profile] import ../../common/[types, profile]
@@ -11,21 +11,17 @@ proc makeAgentLogDirectory*(cq: Conquest, agentId: string): bool =
return false return false
proc log*(cq: Conquest, logEntry: string) = proc log*(cq: Conquest, logEntry: string) =
if cq.interactAgent == nil:
return
let let
date = now().format("dd-MM-yyyy") date = now().format("dd-MM-yyyy")
timestamp = now().format("dd-MM-yyyy HH:mm:ss")
cqDir = cq.profile.getString("conquest_directory") cqDir = cq.profile.getString("conquest_directory")
agentLogPath = fmt"{cqDir}/data/logs/{cq.interactAgent.agentId}/{date}.log" agentLogPath = fmt"{cqDir}/data/logs/{cq.interactAgent.agentId}/{date}.session.log"
# Write log entry to file # Write log entry to file
let file = open(agentLogPath, fmAppend) let file = open(agentLogPath, fmAppend)
file.writeLine(fmt"[{timestamp}] {logEntry}") file.writeLine(fmt"{logEntry}")
file.flushFile() file.flushFile()
proc extractStrings(args: string): string = proc extractStrings*(args: string): string =
if not args.startsWith("("): if not args.startsWith("("):
return args return args
@@ -35,23 +31,3 @@ proc extractStrings(args: string): string =
if str.startsWith("\""): if str.startsWith("\""):
message &= str message &= str
return message.replace("\"", "") return message.replace("\"", "")
template info*(cq: Conquest, args: varargs[untyped]) =
cq.writeLine(fgBlack, styleBright, "[*] ", resetStyle, args)
cq.log("[*] " & extractStrings($(args)))
template error*(cq: Conquest, args: varargs[untyped]) =
cq.writeLine(fgRed, styleBright, "[-] ", resetStyle, args)
cq.log("[-] " & extractStrings($(args)))
template warn*(cq: Conquest, args: varargs[untyped]) =
cq.writeLine(fgYellow, "[!] ", resetStyle, args)
cq.log("[!] " & extractStrings($(args)))
template success*(cq: Conquest, args: varargs[untyped]) =
cq.writeLine(fgGreen, "[+] ", resetStyle, args)
cq.log("[+] " & extractStrings($(args)))
template output*(cq: Conquest, args: varargs[untyped]) =
cq.writeLine(args)
cq.log("[>] " & extractStrings($(args)))

View File

@@ -1,10 +1,10 @@
import prompt, terminal, argparse, parsetoml import prompt, terminal, argparse, parsetoml
import strutils, strformat, times, system, tables import strutils, strformat, times, system, tables
import ./[agent, listener, builder, logger] import ./[agent, listener, builder]
import ../[globals, utils] import ../[globals, utils]
import ../db/database import ../db/database
import ../../common/[types, utils, crypto, profile] import ../../common/[types, crypto, profile]
#[ #[
Argument parsing Argument parsing
@@ -67,8 +67,7 @@ proc handleConsoleCommand(cq: Conquest, args: string) =
# Return if no command (or just whitespace) is entered # Return if no command (or just whitespace) is entered
if args.replace(" ", "").len == 0: return if args.replace(" ", "").len == 0: return
let date: string = now().format("dd-MM-yyyy HH:mm:ss") cq.writeLine(fgBlue, styleBright, fmt"[{getTimestamp()}] ", resetStyle, styleBright, args)
cq.writeLine(fgBlue, styleBright, fmt"[{date}] ", resetStyle, styleBright, args)
try: try:
let opts = parser.parse(args.split(" ").filterIt(it.len > 0)) let opts = parser.parse(args.split(" ").filterIt(it.len > 0))

View File

@@ -1,6 +1,5 @@
import times, strformat, terminal, tables, sequtils, strutils import times, strformat, terminal, tables, sequtils, strutils
import ./logger
import ../utils import ../utils
import ../protocol/parser import ../protocol/parser
import ../../modules/manager import ../../modules/manager
@@ -55,9 +54,7 @@ proc handleAgentCommand*(cq: Conquest, input: string) =
# Return if no command (or just whitespace) is entered # Return if no command (or just whitespace) is entered
if input.replace(" ", "").len == 0: return if input.replace(" ", "").len == 0: return
let date: string = now().format("dd-MM-yyyy HH:mm:ss") cq.writeLine(fgBlue, styleBright, fmt"[{getTimestamp()}] ", fgYellow, fmt"[{cq.interactAgent.agentId}] ", resetStyle, styleBright, input)
cq.writeLine(fgBlue, styleBright, fmt"[{date}] ", fgYellow, fmt"[{cq.interactAgent.agentId}] ", resetStyle, styleBright, input)
cq.log(fmt"Agent command received: {input}")
# Convert user input into sequence of string arguments # Convert user input into sequence of string arguments
let parsedArgs = parseInput(input) let parsedArgs = parseInput(input)
@@ -80,8 +77,8 @@ proc handleAgentCommand*(cq: Conquest, input: string) =
# Add task to queue # Add task to queue
cq.interactAgent.tasks.add(task) cq.interactAgent.tasks.add(task)
cq.info(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, fmt"Tasked agent to {command.description.toLowerAscii()}") cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] [ * ] ", resetStyle, fmt"Tasked agent to {command.description.toLowerAscii()}")
except CatchableError: except CatchableError:
cq.error(getCurrentExceptionMsg() & "\n") cq.writeLine(getCurrentExceptionMsg() & "\n")
return return

View File

@@ -2,7 +2,7 @@ import system, terminal, tiny_sqlite
import ./[dbAgent, dbListener] import ./[dbAgent, dbListener]
import ../utils import ../utils
import ../../common/[types, utils] import ../../common/types
# Export functions so that only ./db/database is required to be imported # Export functions so that only ./db/database is required to be imported
export dbAgent, dbListener export dbAgent, dbListener

View File

@@ -1,7 +1,7 @@
import system, terminal, tiny_sqlite, times, sequtils import system, terminal, tiny_sqlite, times, sequtils
import ../utils import ../utils
import ../../common/[types, utils] import ../../common/types
#[ #[
Agent database functions - Updated with session key support (no jitter) Agent database functions - Updated with session key support (no jitter)

View File

@@ -1,7 +1,7 @@
import system, terminal, tiny_sqlite import system, terminal, tiny_sqlite
import ../utils import ../utils
import ../../common/[types, utils] import ../../common/types
# Utility functions # Utility functions
proc stringToProtocol*(protocol: string): Protocol = proc stringToProtocol*(protocol: string): Protocol =

View File

@@ -1,5 +1,4 @@
import strutils, streams, times, tables import strutils, streams, times, tables
import ../utils
import ../../common/[types, utils, serialize, sequence, crypto] import ../../common/[types, utils, serialize, sequence, crypto]
proc serializeTask*(cq: Conquest, task: var Task): seq[byte] = proc serializeTask*(cq: Conquest, task: var Task): seq[byte] =

View File

@@ -11,9 +11,14 @@ proc validatePort*(portStr: string): bool =
except ValueError: except ValueError:
return false return false
proc getTimestamp*(): string =
return now().format("dd-MM-yyyy HH:mm:ss")
# Function templates and overwrites # Function templates and overwrites
template writeLine*(cq: Conquest, args: varargs[untyped]) = template writeLine*(cq: Conquest, args: varargs[untyped] = "") =
cq.prompt.writeLine(args) cq.prompt.writeLine(args)
if cq.interactAgent != nil:
cq.log(extractStrings($(args)))
proc readLine*(cq: Conquest): string = proc readLine*(cq: Conquest): string =
return cq.prompt.readLine() return cq.prompt.readLine()