From 3038ad6f0e623f6a6559ae55586f6eafe9eade2a Mon Sep 17 00:00:00 2001 From: Jakob Friedl <71284620+jakobfriedl@users.noreply.github.com> Date: Mon, 12 May 2025 21:53:37 +0200 Subject: [PATCH] Started work on agent registration --- LICENSE | 2 +- server/agent.nim | 31 ------- server/agent/agent.nim | 59 ++++++++++++++ server/console.nim | 129 ----------------------------- server/db/database.nim | 26 +++--- server/globals.nim | 3 + server/listener/api.nim | 65 ++++++++++++--- server/listener/listener.nim | 63 +++++++-------- server/listener/utils.nim | 14 ++-- server/server.nim | 152 +++++++++++++++++++++++++++++++---- server/types.nim | 118 +++++++++++++++++++-------- 11 files changed, 391 insertions(+), 271 deletions(-) delete mode 100644 server/agent.nim create mode 100644 server/agent/agent.nim delete mode 100644 server/console.nim create mode 100644 server/globals.nim diff --git a/LICENSE b/LICENSE index 544d795..9347f44 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 Jakob Friedl +Copyright (cq) 2025 Jakob Friedl Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/server/agent.nim b/server/agent.nim deleted file mode 100644 index a2bde19..0000000 --- a/server/agent.nim +++ /dev/null @@ -1,31 +0,0 @@ -import ./types - -proc agentUsage*(console: Console) = - console.writeLine("""Manage, build and interact with agents. - -Usage: - agent [options] COMMAND - -Commands: - - list List all agents. - build Build an agent to connect to an active listener. - interact Interact with an active listener. - -Options: - -h, --help""") - -proc agentList*(console: Console, args: varargs[string]) = - discard - -proc agentBuild*(console: Console, args: varargs[string]) = - discard - -proc agentInteract*(console: Console, args: varargs[string]) = - - console.setIndicator("[AGENT] (username@hostname)> ") - console.setStatusBar(@[("mode", "interact"), ("listeners", "X"), ("agents", "4")]) - - var command: string = console.readLine() - - discard \ No newline at end of file diff --git a/server/agent/agent.nim b/server/agent/agent.nim new file mode 100644 index 0000000..6418956 --- /dev/null +++ b/server/agent/agent.nim @@ -0,0 +1,59 @@ +import terminal, strformat, times +import ../[types, globals] + + +#[ + Agent management mode + These console commands allow dealing with agents from the Conquest framework's prompt interface +]# +proc agentUsage*(cq: Conquest) = + cq.writeLine("""Manage, build and interact with agents. + +Usage: + agent [options] COMMAND + +Commands: + + list List all agents. + build Build an agent to connect to an active listener. + interact Interact with an active agent. + +Options: + -h, --help""") + +proc agentList*(cq: Conquest, args: varargs[string]) = + discard + +proc agentBuild*(cq: Conquest, args: varargs[string]) = + discard + +# Switch to interact mode +proc agentInteract*(cq: Conquest, args: varargs[string]) = + + cq.setIndicator("[AGENT] (username@hostname)> ") + cq.setStatusBar(@[("mode", "interact"), ("listeners", "X"), ("agents", "4")]) + + var command: string = cq.readLine() + + discard + +#[ + Agent API + Functions relevant for dealing with the agent API, such as registering new agents, querying tasks and posting results +]# +proc notifyAgentRegister*(agent: Agent) = + + let date: string = now().format("dd-MM-yyyy HH:mm:ss") + + + + # The following line is required to be able to use the `cq` global variable for console output + {.cast(gcsafe).}: + cq.writeLine(fgYellow, styleBright, fmt"[{date}] Agent {agent.name} connected.", "\n") + +#[ + Agent interaction mode + When interacting with a agent, the following functions are called: + - addTask, to add a new tasks to the agents task queue + - getTaskResult, get the result for the task from the agent +]# \ No newline at end of file diff --git a/server/console.nim b/server/console.nim deleted file mode 100644 index 906d282..0000000 --- a/server/console.nim +++ /dev/null @@ -1,129 +0,0 @@ -import prompt, terminal -import argparse -import strutils, strformat, times, system, unicode - -import ./[types, agent] -import listener/listener -import db/database - -#[ - Argument parsing -]# -var parser = newParser: - help("Console Command & Control") - - command("listener"): - help("Manage, start and stop listeners.") - - command("list"): - help("List all active listeners.") - command("start"): - help("Starts a new HTTP listener.") - option("-h", "-host", default=some("0.0.0.0"), help="IPv4 address to listen on.", required=false) - option("-p", "-port", help="Port to listen on.", required=true) - # flag("--dns", help="Use the DNS protocol for C2 communication.") - command("stop"): - help("Stop an active listener.") - option("-n", "-name", help="Name of the listener to stop.", required=true) - - command("agent"): - help("Manage, build and interact with agents.") - - command("list"): - help("List all agents.") - - command("build"): - help("Build an agent to connect to an active listener.") - - - command("interact"): - help("Interact with an active listener.") - - - command("help"): - nohelpflag() - - command("exit"): - nohelpflag() - -proc handleConsoleCommand*(console: Console, args: varargs[string]) = - - # Return if no command (or just whitespace) is entered - if args[0].replace(" ", "").len == 0: return - - let date: string = now().format("dd-MM-yyyy HH:mm:ss") - console.writeLine(fgCyan, fmt"[{date}] ", resetStyle, styleBright, args[0]) - - try: - let opts = parser.parse(args[0].split(" ").filterIt(it.len > 0)) - - case opts.command - - of "exit": # Exit program - echo "\n" - quit(0) - - of "help": # Display help menu - console.writeLine(parser.help()) - - of "listener": - case opts.listener.get.command - of "list": - console.listenerList() - of "start": - console.listenerStart(opts.listener.get.start.get.host, opts.listener.get.start.get.port) - of "stop": - console.listenerStop(opts.listener.get.stop.get.name) - else: - console.listenerUsage() - - of "agent": - case opts.agent.get.command - of "list": - console.agentList() - of "build": - console.agentBuild() - of "interact": - console.agentInteract() - else: - console.listenerUsage() - - # Handle help flag - except ShortCircuit as err: - if err.flag == "argparse_help": - console.writeLine(err.help) - - # Handle invalid arguments - except UsageError: - console.writeLine(fgRed, styleBright, "[-] ", getCurrentExceptionMsg()) - - console.writeLine("") - -proc header(console: Console) = - console.writeLine("") - console.writeLine("┏┏┓┏┓┏┓┓┏┏┓┏╋") - console.writeLine("┗┗┛┛┗┗┫┗┻┗ ┛┗ 0.1") - console.writeLine(" ┗ @jakobfriedl") - console.writeLine("─".repeat(21)) - console.writeLine("") - -proc initPrompt*() = - - var console = newConsole() - - # Print header - console.header() - - # Initialize database - console.dbInit() - console.restartListeners() - - # Main loop - while true: - console.setIndicator("[conquest]> ") - console.setStatusBar(@[("mode", "manage"), ("listeners", $console.listeners), ("agents", $console.agents)]) - console.showPrompt() - - var command: string = console.readLine() - console.withOutput(handleConsoleCommand, command) - diff --git a/server/db/database.nim b/server/db/database.nim index dd1147b..8f2d1c5 100644 --- a/server/db/database.nim +++ b/server/db/database.nim @@ -3,10 +3,10 @@ import ../types import system, terminal, strformat -proc dbInit*(console: Console) = +proc dbInit*(cq: Conquest) = try: - let conquestDb = openDatabase(console.dbPath, mode=dbReadWrite) + let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) # Create tables conquestDb.execScript(""" @@ -21,15 +21,15 @@ proc dbInit*(console: Console) = """) - console.writeLine(fgGreen, "[+] ", console.dbPath, ": Database created.") + cq.writeLine(fgGreen, "[+] ", cq.dbPath, ": Database created.") conquestDb.close() except SqliteError: - console.writeLine(fgGreen, "[+] ", console.dbPath, ": Database file found.") + cq.writeLine(fgGreen, "[+] ", cq.dbPath, ": Database file found.") -proc dbStore*(console: Console, listener: Listener): bool = +proc dbStoreListener*(cq: Conquest, listener: Listener): bool = try: - let conquestDb = openDatabase(console.dbPath, mode=dbReadWrite) + let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) conquestDb.exec(""" INSERT INTO listener (name, address, port, protocol, sleep, jitter) @@ -38,17 +38,17 @@ proc dbStore*(console: Console, listener: Listener): bool = conquestDb.close() except: - console.writeLine(fgRed, styleBright, "[-] ", getCurrentExceptionMsg()) + cq.writeLine(fgRed, styleBright, "[-] ", getCurrentExceptionMsg()) return false return true -proc dbGetAllListeners*(console: Console): seq[Listener] = +proc dbGetAllListeners*(cq: Conquest): seq[Listener] = var listeners: seq[Listener] = @[] try: - let conquestDb = openDatabase(console.dbPath, mode=dbReadWrite) + let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) for row in conquestDb.iterate("SELECT name, address, port, protocol, sleep, jitter FROM listener;"): let (name, address, port, protocol, sleep, jitter) = row.unpack((string, string, int, string, int, float )) @@ -65,13 +65,13 @@ proc dbGetAllListeners*(console: Console): seq[Listener] = conquestDb.close() except: - console.writeLine(fgRed, styleBright, "[-] ", getCurrentExceptionMsg()) + cq.writeLine(fgRed, styleBright, "[-] ", getCurrentExceptionMsg()) return listeners -proc dbDeleteListenerByName*(console: Console, name: string): bool = +proc dbDeleteListenerByName*(cq: Conquest, name: string): bool = try: - let conquestDb = openDatabase(console.dbPath, mode=dbReadWrite) + let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) conquestDb.exec("DELETE FROM listener WHERE name = ?", name) @@ -81,5 +81,5 @@ proc dbDeleteListenerByName*(console: Console, name: string): bool = return true -proc dbStore*(agent: Agent): bool = +proc dbStoreAgent*(agent: Agent): bool = discard \ No newline at end of file diff --git a/server/globals.nim b/server/globals.nim new file mode 100644 index 0000000..304873d --- /dev/null +++ b/server/globals.nim @@ -0,0 +1,3 @@ +import ./types + +var cq*: Conquest \ No newline at end of file diff --git a/server/listener/api.nim b/server/listener/api.nim index c5b12f3..f4e54e9 100644 --- a/server/listener/api.nim +++ b/server/listener/api.nim @@ -1,15 +1,62 @@ -import prologue +import prologue, nanoid +import terminal, sequtils, strutils -import ../types +import ../[types] +import ../agent/agent +import ./utils -proc index*(ctx: Context) {.async.} = - resp "Index" - -proc agentRegister*(ctx: Context) {.async.} = - resp "Register" +#[ + POST /{listener-uuid}/register + Called from agent to register itself to the conquest server +]# +proc register*(ctx: Context) {.async.} = -proc addTasks*(ctx: Context) {.async.} = + # Check headers + doAssert(ctx.request.getHeader("CONTENT-TYPE") == @["application/json"]) + doAssert(ctx.request.getHeader("USER-AGENT") == @["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"]) + + # Handle POST data, the register data should look like the following + #[ + { + "username": "username", + "hostname":"hostname", + "ip": "ip-address", + "os": "operating-system" + "pid": 1234 + "elevated": false + } + ]# - let name = ctx.getPathParams("name") + let + postData: JsonNode = %ctx.request.body() + name = generate(alphabet=join(toSeq('A'..'Z'), ""), size=8) + + let agent = new Agent + agent.name = name + notifyAgentRegister(agent) + + + resp agent.name + +#[ + GET /{listener-uuid}/{agent-uuid}/tasks + Called from agent to check for new tasks +]# +proc getTasks*(ctx: Context) {.async.} = + stdout.writeLine(ctx.getPathParams("listener")) + let name = ctx.getPathParams("agent") + + resp name + +#[ + POST /{listener-uuid}/{agent-uuid}/results + Called from agent to post results of a task + +]# +proc postResults*(ctx: Context) {.async.} = + + let name = ctx.getPathParams("agent") + + resp name \ No newline at end of file diff --git a/server/listener/listener.nim b/server/listener/listener.nim index bbf2c8e..5cdd37c 100644 --- a/server/listener/listener.nim +++ b/server/listener/listener.nim @@ -1,13 +1,12 @@ -import strformat, strutils, sequtils, checksums/sha1, nanoid, terminal, sugar +import strformat, strutils, sequtils, checksums/sha1, nanoid, terminal import prologue import ./[api, utils] import ../types import ../db/database - -proc listenerUsage*(console: Console) = - console.writeLine("""Manage, start and stop listeners. +proc listenerUsage*(cq: Conquest) = + cq.writeLine("""Manage, start and stop listeners. Usage: listener [options] COMMAND @@ -21,18 +20,18 @@ Commands: Options: -h, --help""") -proc listenerList*(console: Console) = - let listeners = console.dbGetAllListeners() - console.drawTable(listeners) +proc listenerList*(cq: Conquest) = + let listeners = cq.dbGetAllListeners() + cq.drawTable(listeners) -proc listenerStart*(console: Console, host: string, portStr: string) = +proc listenerStart*(cq: Conquest, host: string, portStr: string) = # Validate arguments # if not validateIPv4Address(host): - # console.writeLine(fgRed, styleBright, fmt"Invalid IPv4 IP address: {ip}.") + # cq.writeLine(fgRed, styleBright, fmt"Invalid IPv4 IP address: {ip}.") # return if not validatePort(portStr): - console.writeLine(fgRed, styleBright, fmt"[-] Invalid port number: {portStr}") + cq.writeLine(fgRed, styleBright, fmt"[-] Invalid port number: {portStr}") return let port = portStr.parseInt @@ -50,26 +49,25 @@ proc listenerStart*(console: Console, host: string, portStr: string) = var listener = newApp(settings = listenerSettings) # Define API endpoints - listener.addRoute("/", api.index, @[HttpGet]) - listener.addRoute("/register", api.agentRegister, @[HttpPost]) - listener.addRoute("/{name}/tasks", api.addTasks, @[HttpGet, HttpPost]) + listener.post("{listener}/register", api.register) + listener.get("{listener}/{agent}/tasks", api.getTasks) + listener.post("{listener}/{agent}/results", api.postResults) # Store listener in database let listenerInstance = newListener(name, host, port) - if not console.dbStore(listenerInstance): + if not cq.dbStoreListener(listenerInstance): return # Start serving try: discard listener.runAsync() - console.activeListeners.add(listener) - inc console.listeners - console.writeLine(fgGreen, "[+] ", resetStyle, "Started listener", fgGreen, fmt" {name} ", resetStyle, fmt"on port {portStr}.") + inc cq.listeners + cq.writeLine(fgGreen, "[+] ", resetStyle, "Started listener", fgGreen, fmt" {name} ", resetStyle, fmt"on port {portStr}.") except CatchableError as err: - console.writeLine(fgRed, styleBright, "[-] Failed to start listener: ", getCurrentExceptionMsg()) + cq.writeLine(fgRed, styleBright, "[-] Failed to start listener: ", getCurrentExceptionMsg()) -proc restartListeners*(console: Console) = - let listeners: seq[Listener] = console.dbGetAllListeners() +proc restartListeners*(cq: Conquest) = + let listeners: seq[Listener] = cq.dbGetAllListeners() # Restart all active listeners that are stored in the database for l in listeners: @@ -83,29 +81,28 @@ proc restartListeners*(console: Console) = listener = newApp(settings = settings) # Define API endpoints - listener.addRoute("/", api.index, @[HttpGet]) - listener.addRoute("/register", api.agentRegister, @[HttpPost]) - listener.addRoute("/{name}/tasks", api.addTasks, @[HttpGet, HttpPost]) + listener.post("{listener}/register", api.register) + listener.get("{listener}/{agent}/tasks", api.getTasks) + listener.post("{listener}/{agent}/results", api.postResults) try: discard listener.runAsync() - console.activeListeners.add(listener) - inc console.listeners - console.writeLine(fgGreen, "[+] ", resetStyle, "Restarted listener", fgGreen, fmt" {l.name} ", resetStyle, fmt"on port {$l.port}.") + inc cq.listeners + cq.writeLine(fgGreen, "[+] ", resetStyle, "Restarted listener", fgGreen, fmt" {l.name} ", resetStyle, fmt"on port {$l.port}.") except CatchableError as err: - console.writeLine(fgRed, styleBright, "[-] Failed to restart listener: ", getCurrentExceptionMsg()) + cq.writeLine(fgRed, styleBright, "[-] Failed to restart listener: ", getCurrentExceptionMsg()) # Delay before starting serving another listener to avoid crashing the application waitFor sleepAsync(10) - console.writeLine("") + cq.writeLine("") -proc listenerStop*(console: Console, name: string) = +proc listenerStop*(cq: Conquest, name: string) = - if not console.dbDeleteListenerByName(name.toUpperAscii): - console.writeLine(fgRed, styleBright, "[-] Failed to stop listener: ", getCurrentExceptionMsg()) + if not cq.dbDeleteListenerByName(name.toUpperAscii): + cq.writeLine(fgRed, styleBright, "[-] Failed to stop listener: ", getCurrentExceptionMsg()) return - dec console.listeners - console.writeLine(fgGreen, "[+] ", resetStyle, "Stopped listener ", fgGreen, fmt"{name.toUpperAscii}.") + dec cq.listeners + cq.writeLine(fgGreen, "[+] ", resetStyle, "Stopped listener ", fgGreen, name.toUpperAscii, resetStyle, ".") \ No newline at end of file diff --git a/server/listener/utils.nim b/server/listener/utils.nim index 16ddf4b..1ba92db 100644 --- a/server/listener/utils.nim +++ b/server/listener/utils.nim @@ -41,23 +41,23 @@ proc row(cells: seq[string], widths: seq[int]): string = row.add(" " & cell.alignLeft(widths[i] - 2) & " " & vert) return row -proc drawTable*(console: Console, listeners: seq[Listener]) = +proc drawTable*(cq: Conquest, listeners: seq[Listener]) = # Column headers and widths let headers = @["Name", "Address", "Port", "Protocol", "Agents"] let widths = @[10, 15, 7, 10, 8] - console.writeLine(border(topLeft, topMid, topRight, widths)) - console.writeLine(row(headers, widths)) - console.writeLine(border(midLeft, midMid, midRight, widths)) + cq.writeLine(border(topLeft, topMid, topRight, widths)) + cq.writeLine(row(headers, widths)) + cq.writeLine(border(midLeft, midMid, midRight, widths)) for l in listeners: # TODO: Add number of agents connected to the listener let row = @[l.name, l.address, $l.port, $l.protocol, "X"] - console.writeLine(row(row, widths)) + cq.writeLine(row(row, widths)) - console.writeLine(border(botLeft, botMid, botRight, widths)) + cq.writeLine(border(botLeft, botMid, botRight, widths)) -proc drawTable*(console: Console, agents: seq[Agent]) = +proc drawTable*(cq: Conquest, agents: seq[Agent]) = discard \ No newline at end of file diff --git a/server/server.nim b/server/server.nim index eebe09b..08b5a52 100644 --- a/server/server.nim +++ b/server/server.nim @@ -1,19 +1,141 @@ -import ./console +import prompt, terminal +import argparse +import strutils, strformat, times, system, unicode -# Handle CTRL+C, -proc exit() {.noconv.} = - echo "Received CTRL+C. Type \"exit\" to close the application.\n" - -proc main() = - # Initialize TUI - # initUi() - - setControlCHook(exit) - - # Initialize prompt interface - initPrompt() +import ./[types, globals] +import agent/agent, listener/listener, db/database #[ - Start main function + Argument parsing +]# +var parser = newParser: + help("Conquest Command & Control") + + command("listener"): + help("Manage, start and stop listeners.") + + command("list"): + help("List all active listeners.") + command("start"): + help("Starts a new HTTP listener.") + option("-h", "-host", default=some("0.0.0.0"), help="IPv4 address to listen on.", required=false) + option("-p", "-port", help="Port to listen on.", required=true) + # flag("--dns", help="Use the DNS protocol for C2 communication.") + command("stop"): + help("Stop an active listener.") + option("-n", "-name", help="Name of the listener to stop.", required=true) + + command("agent"): + help("Manage, build and interact with agents.") + + command("list"): + help("List all agents.") + + command("build"): + help("Build an agent to connect to an active listener.") + + + command("interact"): + help("Interact with an active agent.") + + + command("help"): + nohelpflag() + + command("exit"): + nohelpflag() + +proc handleConsoleCommand*(cq: Conquest, args: varargs[string]) = + + # Return if no command (or just whitespace) is entered + if args[0].replace(" ", "").len == 0: return + + let date: string = now().format("dd-MM-yyyy HH:mm:ss") + cq.writeLine(fgCyan, fmt"[{date}] ", resetStyle, styleBright, args[0]) + + try: + let opts = parser.parse(args[0].split(" ").filterIt(it.len > 0)) + + case opts.command + + of "exit": # Exit program + echo "\n" + quit(0) + + of "help": # Display help menu + cq.writeLine(parser.help()) + + of "listener": + case opts.listener.get.command + of "list": + cq.listenerList() + of "start": + cq.listenerStart(opts.listener.get.start.get.host, opts.listener.get.start.get.port) + of "stop": + cq.listenerStop(opts.listener.get.stop.get.name) + else: + cq.listenerUsage() + + of "agent": + case opts.agent.get.command + of "list": + cq.agentList() + of "build": + cq.agentBuild() + of "interact": + cq.agentInteract() + else: + cq.agentUsage() + + # Handle help flag + except ShortCircuit as err: + if err.flag == "argparse_help": + cq.writeLine(err.help) + + # Handle invalid arguments + except UsageError: + cq.writeLine(fgRed, styleBright, "[-] ", getCurrentExceptionMsg()) + + cq.writeLine("") + +proc header(cq: Conquest) = + cq.writeLine("") + cq.writeLine("┏┏┓┏┓┏┓┓┏┏┓┏╋") + cq.writeLine("┗┗┛┛┗┗┫┗┻┗ ┛┗ V0.1") + cq.writeLine(" ┗ @jakobfriedl") + cq.writeLine("─".repeat(21)) + cq.writeLine("") + + +#[ + Conquest framework entry point ]# -main() \ No newline at end of file +proc main() = + + # Handle CTRL+C, + proc exit() {.noconv.} = + echo "Received CTRL+C. Type \"exit\" to close the application.\n" + + setControlCHook(exit) + + # Initialize framework + cq = initConquest() + + # Print header + cq.header() + + # Initialize database + cq.dbInit() + cq.restartListeners() + + # Main loop + while true: + cq.setIndicator("[conquest]> ") + cq.setStatusBar(@[("mode", "manage"), ("listeners", $cq.listeners), ("agents", $cq.agents)]) + cq.showPrompt() + + var command: string = cq.readLine() + cq.withOutput(handleConsoleCommand, command) + +when isMainModule: + main() \ No newline at end of file diff --git a/server/types.nim b/server/types.nim index 5fdb289..d0b8689 100644 --- a/server/types.nim +++ b/server/types.nim @@ -2,58 +2,110 @@ import prompt import prologue #[ - Console + Conquest ]# type - Console* = ref object + Conquest* = ref object prompt*: Prompt listeners*: int agents*: int dbPath*: string - activeListeners*: seq[Prologue] - Command* = object - cmd*: string - execute*: proc(console: Console, args: varargs[string]) - -proc newConsole*(): Console = - var console = new Console +proc initConquest*(): Conquest = + var cq = new Conquest var prompt = Prompt.init() - console.prompt = prompt - console.dbPath = "db/conquest.db" - console.listeners = 0 - console.agents = 0 - console.activeListeners = @[] + cq.prompt = prompt + cq.dbPath = "db/conquest.db" + cq.listeners = 0 + cq.agents = 0 - return console + return cq -template writeLine*(console: Console, args: varargs[untyped]) = - console.prompt.writeLine(args) -proc readLine*(console: Console): string = - return console.prompt.readLine() -template setIndicator*(console: Console, indicator: string) = - console.prompt.setIndicator(indicator) -template showPrompt*(console: Console) = - console.prompt.showPrompt() -template hidePrompt*(console: Console) = - console.prompt.hidePrompt() -template setStatusBar*(console: Console, statusBar: seq[StatusBarItem]) = - console.prompt.setStatusBar(statusBar) -template clear*(console: Console) = - console.prompt.clear() +template writeLine*(cq: Conquest, args: varargs[untyped]) = + cq.prompt.writeLine(args) +proc readLine*(cq: Conquest): string = + return cq.prompt.readLine() +template setIndicator*(cq: Conquest, indicator: string) = + cq.prompt.setIndicator(indicator) +template showPrompt*(cq: Conquest) = + cq.prompt.showPrompt() +template hidePrompt*(cq: Conquest) = + cq.prompt.hidePrompt() +template setStatusBar*(cq: Conquest, statusBar: seq[StatusBarItem]) = + cq.prompt.setStatusBar(statusBar) +template clear*(cq: Conquest) = + cq.prompt.clear() # Overwrite withOutput function to handle function arguments -proc withOutput*(console: Console, outputFunction: proc(console: Console, args: varargs[string]), args: varargs[string]) = - console.hidePrompt() - outputFunction(console, args) - console.showPrompt() +proc withOutput*(cq: Conquest, outputFunction: proc(cq: Conquest, args: varargs[string]), args: varargs[string]) = + cq.hidePrompt() + outputFunction(cq, args) + cq.showPrompt() #[ Agent ]# type + + TaskCommand* = enum + ExecuteShell = "shell" + ExecuteBof = "bof" + ExecuteAssembly = "dotnet" + ExecutePe = "pe" + + TaskStatus* = enum + Created = "created" + Completed = "completed" + Pending = "pending" + Failed = "failed" + Cancelled = "cancelled" + + TaskResult* = string + + Task* = ref object + id*: int + agent*: string + command*: TaskCommand + args*: seq[string] + result*: TaskResult + status*: TaskStatus + + AgentRegistrationData* = object + username*: string + hostname*: string + ip*: string + os*: string + pid*: int + elevated*: bool + Agent* = ref object name*: string + listener*: string + sleep*: int + jitter*: float + pid*: int + username*: string + hostname*: string + ip*: string + os*: string + elevated*: bool + tasks*: seq[Task] + +proc newAgent*(name, listener, username, hostname, ip, os: string, pid: int, elevated: bool): Agent = + var agent = new Agent + agent.name = name + agent.listener = listener + agent.pid = pid + agent.username = username + agent.hostname = hostname + agent.ip = ip + agent.os = os + agent.elevated = elevated + agent.sleep = 10 + agent.jitter = 0.2 + agent.tasks = @[] + + return agent #[ Listener