2025-07-16 12:32:01 +02:00
|
|
|
import terminal, strformat, strutils, sequtils, tables, json, times, base64, system
|
2025-07-15 23:26:54 +02:00
|
|
|
|
2025-07-16 14:45:45 +02:00
|
|
|
import ../[utils, globals]
|
2025-07-15 23:26:54 +02:00
|
|
|
import ../db/database
|
2025-07-19 16:49:27 +02:00
|
|
|
import ../task/packer
|
2025-07-21 22:07:25 +02:00
|
|
|
import ../../common/[types, utils]
|
|
|
|
|
|
|
|
|
|
import sugar
|
2025-07-15 23:26:54 +02:00
|
|
|
|
2025-07-16 14:45:45 +02:00
|
|
|
# Utility functions
|
|
|
|
|
proc add*(cq: Conquest, agent: Agent) =
|
2025-07-21 22:07:25 +02:00
|
|
|
cq.agents[agent.agentId] = agent
|
2025-07-16 14:45:45 +02:00
|
|
|
|
2025-07-15 23:26:54 +02:00
|
|
|
#[
|
|
|
|
|
Agent API
|
|
|
|
|
Functions relevant for dealing with the agent API, such as registering new agents, querying tasks and posting results
|
|
|
|
|
]#
|
2025-07-21 22:07:25 +02:00
|
|
|
proc register*(registrationData: seq[byte]): bool =
|
2025-07-15 23:26:54 +02:00
|
|
|
|
|
|
|
|
# The following line is required to be able to use the `cq` global variable for console output
|
|
|
|
|
{.cast(gcsafe).}:
|
|
|
|
|
|
2025-07-21 22:07:25 +02:00
|
|
|
let agent: Agent = deserializeNewAgent(registrationData)
|
|
|
|
|
|
|
|
|
|
# Validate that listener exists
|
|
|
|
|
if not cq.dbListenerExists(agent.listenerId.toUpperAscii):
|
|
|
|
|
cq.writeLine(fgRed, styleBright, fmt"[-] {agent.ip} attempted to register to non-existent listener: {agent.listenerId}.", "\n")
|
2025-07-15 23:26:54 +02:00
|
|
|
return false
|
|
|
|
|
|
2025-07-21 22:07:25 +02:00
|
|
|
# # Store agent in database
|
2025-07-15 23:26:54 +02:00
|
|
|
if not cq.dbStoreAgent(agent):
|
2025-07-21 22:07:25 +02:00
|
|
|
cq.writeLine(fgRed, styleBright, fmt"[-] Failed to insert agent {agent.agentId} into database.", "\n")
|
2025-07-15 23:26:54 +02:00
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
cq.add(agent)
|
|
|
|
|
|
|
|
|
|
let date = agent.firstCheckin.format("dd-MM-yyyy HH:mm:ss")
|
2025-07-21 22:07:25 +02:00
|
|
|
cq.writeLine(fgYellow, styleBright, fmt"[{date}] ", resetStyle, "Agent ", fgYellow, styleBright, agent.agentId, resetStyle, " connected to listener ", fgGreen, styleBright, agent.listenerId, resetStyle, ": ", fgYellow, styleBright, fmt"{agent.username}@{agent.hostname}", "\n")
|
2025-07-15 23:26:54 +02:00
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
2025-07-18 14:24:07 +02:00
|
|
|
proc getTasks*(listener, agent: string): seq[seq[byte]] =
|
2025-07-15 23:26:54 +02:00
|
|
|
|
|
|
|
|
{.cast(gcsafe).}:
|
|
|
|
|
|
2025-07-19 16:49:27 +02:00
|
|
|
var result: seq[seq[byte]]
|
|
|
|
|
|
2025-07-15 23:26:54 +02:00
|
|
|
# Check if listener exists
|
|
|
|
|
if not cq.dbListenerExists(listener.toUpperAscii):
|
|
|
|
|
cq.writeLine(fgRed, styleBright, fmt"[-] Task-retrieval request made to non-existent listener: {listener}.", "\n")
|
2025-07-18 14:24:07 +02:00
|
|
|
raise newException(ValueError, "Invalid listener.")
|
2025-07-15 23:26:54 +02:00
|
|
|
|
|
|
|
|
# Check if agent exists
|
|
|
|
|
if not cq.dbAgentExists(agent.toUpperAscii):
|
|
|
|
|
cq.writeLine(fgRed, styleBright, fmt"[-] Task-retrieval request made to non-existent agent: {agent}.", "\n")
|
2025-07-18 14:24:07 +02:00
|
|
|
raise newException(ValueError, "Invalid agent.")
|
2025-07-15 23:26:54 +02:00
|
|
|
|
|
|
|
|
# Update the last check-in date for the accessed agent
|
|
|
|
|
cq.agents[agent.toUpperAscii].latestCheckin = now()
|
|
|
|
|
# if not cq.dbUpdateCheckin(agent.toUpperAscii, now().format("dd-MM-yyyy HH:mm:ss")):
|
|
|
|
|
# return nil
|
|
|
|
|
|
2025-07-19 16:49:27 +02:00
|
|
|
# Return tasks
|
|
|
|
|
for task in cq.agents[agent.toUpperAscii].tasks:
|
|
|
|
|
let taskData = serializeTask(task)
|
|
|
|
|
result.add(taskData)
|
|
|
|
|
|
|
|
|
|
return result
|
2025-07-15 23:26:54 +02:00
|
|
|
|
2025-07-19 16:49:27 +02:00
|
|
|
proc handleResult*(resultData: seq[byte]) =
|
2025-07-15 23:26:54 +02:00
|
|
|
|
|
|
|
|
{.cast(gcsafe).}:
|
|
|
|
|
|
2025-07-19 16:49:27 +02:00
|
|
|
let
|
|
|
|
|
taskResult = deserializeTaskResult(resultData)
|
|
|
|
|
taskId = uuidToString(taskResult.taskId)
|
|
|
|
|
agentId = uuidToString(taskResult.agentId)
|
|
|
|
|
listenerId = uuidToString(taskResult.listenerId)
|
|
|
|
|
|
2025-07-15 23:26:54 +02:00
|
|
|
let date: string = now().format("dd-MM-yyyy HH:mm:ss")
|
2025-07-19 16:49:27 +02:00
|
|
|
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, fmt"{$resultData.len} bytes received.")
|
2025-07-15 23:26:54 +02:00
|
|
|
|
2025-07-19 16:49:27 +02:00
|
|
|
case cast[StatusType](taskResult.status):
|
|
|
|
|
of STATUS_COMPLETED:
|
|
|
|
|
cq.writeLine(fgBlack, styleBright, fmt"[{date}]", fgGreen, " [+] ", resetStyle, fmt"Task {taskId} completed.")
|
2025-07-15 23:26:54 +02:00
|
|
|
|
2025-07-19 16:49:27 +02:00
|
|
|
of STATUS_FAILED:
|
|
|
|
|
cq.writeLine(fgBlack, styleBright, fmt"[{date}]", fgRed, styleBright, " [-] ", resetStyle, fmt"Task {taskId} failed.")
|
2025-07-15 23:26:54 +02:00
|
|
|
|
2025-07-19 16:49:27 +02:00
|
|
|
case cast[ResultType](taskResult.resultType):
|
|
|
|
|
of RESULT_STRING:
|
|
|
|
|
if int(taskResult.length) > 0:
|
|
|
|
|
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, "Output:")
|
2025-07-15 23:26:54 +02:00
|
|
|
# Split result string on newline to keep formatting
|
2025-07-19 16:49:27 +02:00
|
|
|
for line in taskResult.data.toString().split("\n"):
|
|
|
|
|
cq.writeLine(line)
|
2025-07-15 23:26:54 +02:00
|
|
|
|
2025-07-19 16:49:27 +02:00
|
|
|
of RESULT_BINARY:
|
|
|
|
|
# Write binary data to a file
|
|
|
|
|
cq.writeLine()
|
2025-07-15 23:26:54 +02:00
|
|
|
|
2025-07-19 16:49:27 +02:00
|
|
|
of RESULT_NO_OUTPUT:
|
|
|
|
|
cq.writeLine()
|
2025-07-15 23:26:54 +02:00
|
|
|
|
|
|
|
|
# Update task queue to include all tasks, except the one that was just completed
|
2025-07-19 16:49:27 +02:00
|
|
|
cq.agents[agentId].tasks = cq.agents[agentId].tasks.filterIt(it.taskId != taskResult.taskId)
|