2025-05-24 16:15:41 +02:00
import terminal , strformat , strutils , sequtils , tables , json , times , base64 , system , osproc , streams
2025-05-19 21:56:34 +02:00
import . / interact
2025-05-14 12:42:23 +02:00
import .. / [ types , globals , utils ]
2025-05-13 23:42:04 +02:00
import .. / db / database
2025-05-12 21:53:37 +02:00
#[
Agent management mode
These console commands allow dealing with agents from the Conquest framework ' s p r o m p t i n t e r f a c e
] #
proc agentUsage * ( cq : Conquest ) =
cq . writeLine ( """ Manage, build and interact with agents.
Usage :
agent [ options ] COMMAND
Commands :
list List all agents .
2025-05-15 14:27:45 +02:00
info Display details for a specific agent .
2025-05-14 15:48:01 +02:00
kill Terminate the connection of an active listener and remove it from the interface .
2025-05-12 21:53:37 +02:00
interact Interact with an active agent .
Options :
- h , - - help""" )
2025-05-14 15:48:01 +02:00
# List agents
2025-05-15 14:27:45 +02:00
proc agentList * ( cq : Conquest , listener : string ) =
2025-05-15 15:24:46 +02:00
# If no argument is passed via -n, list all agents, otherwise only display agents connected to a specific listener
if listener = = " " :
cq . drawTable ( cq . dbGetAllAgents ( ) )
2025-05-29 14:19:55 +02:00
2025-05-15 15:24:46 +02:00
else :
# Check if listener exists
if not cq . dbListenerExists ( listener . toUpperAscii ) :
cq . writeLine ( fgRed , styleBright , fmt" [-] Listener {listener.toUpperAscii} does not exist. " )
return
2025-05-15 14:27:45 +02:00
2025-05-15 15:24:46 +02:00
cq . drawTable ( cq . dbGetAllAgentsByListener ( listener . toUpperAscii ) )
2025-05-15 14:27:45 +02:00
2025-05-29 14:19:55 +02:00
2025-05-15 14:27:45 +02:00
# Display agent properties and details
proc agentInfo * ( cq : Conquest , name : string ) =
# Check if agent supplied via -n parameter exists in database
if not cq . dbAgentExists ( name . toUpperAscii ) :
cq . writeLine ( fgRed , styleBright , fmt" [-] Agent {name.toUpperAscii} does not exist. " )
return
let agent = cq . agents [ name . toUpperAscii ]
2025-05-15 15:24:46 +02:00
# TODO: Improve formatting
2025-05-15 14:27:45 +02:00
cq . writeLine ( fmt"""
Agent name ( UUID ) : { agent . name }
Connected to listener : { agent . listener }
──────────────────────────────────────────
Username : { agent . username }
Hostname : { agent . hostname }
Domain : { agent . domain }
IP - Address : { agent . ip }
Operating system : { agent . os }
──────────────────────────────────────────
Process name : { agent . process }
Process ID : { $ agent . pid }
Process elevated : { $ agent . elevated }
2025-05-23 13:55:00 +02:00
First checkin : { agent . firstCheckin . format ( " dd-MM-yyyy HH:mm:ss " ) }
Latest checkin : { agent . latestCheckin . format ( " dd-MM-yyyy HH:mm:ss " ) }
""" )
2025-05-15 14:27:45 +02:00
2025-05-14 15:48:01 +02:00
# Terminate agent and remove it from the database
proc agentKill * ( cq : Conquest , name : string ) =
# Check if agent supplied via -n parameter exists in database
if not cq . dbAgentExists ( name . toUpperAscii ) :
cq . writeLine ( fgRed , styleBright , fmt" [-] Agent {name.toUpperAscii} does not exist. " )
return
# TODO: Stop the process of the agent on the target system
# TODO: Add flag to self-delete executable after killing agent
# Remove the agent from the database
if not cq . dbDeleteAgentByName ( name . toUpperAscii ) :
cq . writeLine ( fgRed , styleBright , " [-] Failed to terminate agent: " , getCurrentExceptionMsg ( ) )
return
cq . delAgent ( name )
cq . writeLine ( fgYellow , styleBright , " [+] " , resetStyle , " Terminated agent " , fgYellow , styleBright , name . toUpperAscii , resetStyle , " . " )
2025-05-12 21:53:37 +02:00
# Switch to interact mode
2025-05-18 12:51:26 +02:00
proc agentInteract * ( cq : Conquest , name : string ) =
2025-05-12 21:53:37 +02:00
2025-05-18 12:51:26 +02:00
# Verify that agent exists
if not cq . dbAgentExists ( name . toUpperAscii ) :
cq . writeLine ( fgRed , styleBright , fmt" [-] Agent {name.toUpperAscii} does not exist. " )
return
let agent = cq . agents [ name . toUpperAscii ]
var command : string = " "
# Change prompt indicator to show agent interaction
cq . setIndicator ( fmt" [{agent.name}]> " )
cq . setStatusBar ( @ [ ( " [mode] " , " interact " ) , ( " [username] " , fmt" {agent.username} " ) , ( " [hostname] " , fmt" {agent.hostname} " ) , ( " [ip] " , fmt" {agent.ip} " ) , ( " [domain] " , fmt" {agent.domain} " ) ] )
2025-05-22 20:03:22 +02:00
cq . writeLine ( fgYellow , styleBright , " [+] " , resetStyle , fmt" Started interacting with agent " , fgYellow , styleBright , agent . name , resetStyle , " . Type ' help ' to list available commands. \n " )
2025-05-18 12:51:26 +02:00
cq . interactAgent = agent
2025-05-22 20:03:22 +02:00
while command . replace ( " " , " " ) ! = " back " :
2025-05-18 12:51:26 +02:00
command = cq . readLine ( )
cq . withOutput ( handleAgentCommand , command )
2025-05-12 21:53:37 +02:00
2025-05-18 12:51:26 +02:00
cq . interactAgent = nil
2025-05-12 21:53:37 +02:00
2025-05-24 16:15:41 +02:00
# Agent generation
proc agentBuild * ( cq : Conquest , listener , sleep , payload : string ) =
# Verify that listener exists
if not cq . dbListenerExists ( listener . toUpperAscii ) :
cq . writeLine ( fgRed , styleBright , fmt" [-] Listener {listener.toUpperAscii} does not exist. " )
return
let listener = cq . listeners [ listener . toUpperAscii ]
# Create/overwrite nim.cfg file to set agent configuration
let agentConfigFile = fmt" ../agents/{payload}/nim.cfg "
# The following shows the format of the agent configuration file that defines compile-time variables
let config = fmt"""
# Agent configuration
- d : ListenerUuid = " {listener.name} "
- d : ListenerIp = " {listener.address} "
- d : ListenerPort = { listener . port }
- d : SleepDelay = { sleep }
""" .replace( " " , " " )
writeFile ( agentConfigFile , config )
cq . writeLine ( fgBlack , styleBright , " [*] " , resetStyle , " Configuration file created. " )
# Build agent by executing the ./build.sh script on the system.
let agentBuildScript = fmt" ../agents/{payload}/build.sh "
cq . writeLine ( fgBlack , styleBright , " [*] " , resetStyle , " Building agent... " )
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
let process = startProcess ( agentBuildScript , options = { poUsePath , poStdErrToStdOut } )
let outputStream = process . outputStream
var line : string
while outputStream . readLine ( line ) :
cq . writeLine ( line )
let exitCode = process . waitForExit ( )
# Check if the build succeeded or not
if exitCode = = 0 :
cq . writeLine ( fgGreen , " [+] " , resetStyle , " Agent payload generated successfully. " )
else :
cq . writeLine ( fgRed , styleBright , " [-] " , resetStyle , " Build script exited with code " , $ exitCode )
except CatchableError as err :
cq . writeLine ( fgRed , styleBright , " [-] " , resetStyle , " An error occurred: " , err . msg )
2025-05-12 21:53:37 +02:00
#[
Agent API
Functions relevant for dealing with the agent API , such as registering new agents , querying tasks and posting results
] #
2025-05-13 23:42:04 +02:00
proc register * ( agent : Agent ) : bool =
2025-05-12 21:53:37 +02:00
# The following line is required to be able to use the `cq` global variable for console output
{. cast ( gcsafe ) . } :
2025-05-13 23:42:04 +02:00
# Check if listener that is requested exists
# TODO: Verify that the listener accessed is also the listener specified in the URL
# This can be achieved by extracting the port number from the `Host` header and matching it to the one queried from the database
2025-05-15 14:27:45 +02:00
if not cq . dbListenerExists ( agent . listener . toUpperAscii ) :
2025-05-22 20:03:22 +02:00
cq . writeLine ( fgRed , styleBright , fmt" [-] {agent.ip} attempted to register to non-existent listener: {agent.listener}. " , " \n " )
2025-05-13 23:42:04 +02:00
return false
# Store agent in database
if not cq . dbStoreAgent ( agent ) :
cq . writeLine ( fgRed , styleBright , fmt" [-] Failed to insert agent {agent.name} into database. " , " \n " )
return false
2025-05-14 15:48:01 +02:00
cq . add ( agent )
2025-05-23 13:55:00 +02:00
let date = agent . firstCheckin . format ( " dd-MM-yyyy HH:mm:ss " )
cq . writeLine ( fgYellow , styleBright , fmt" [{date}] " , resetStyle , " Agent " , fgYellow , styleBright , agent . name , resetStyle , " connected to listener " , fgGreen , styleBright , agent . listener , resetStyle , " : " , fgYellow , styleBright , fmt" {agent.username}@{agent.hostname} " , " \n " )
2025-05-13 23:42:04 +02:00
2025-05-22 20:03:22 +02:00
return true
proc getTasks * ( listener , agent : string ) : JsonNode =
{. cast ( gcsafe ) . } :
# 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 " )
return nil
# 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 " )
return nil
2025-05-23 13:55:00 +02:00
# Update the last check-in date for the accessed agent
cq . agents [ agent . toUpperAscii ] . latestCheckin = now ( )
2025-05-29 14:19:55 +02:00
# if not cq.dbUpdateCheckin(agent.toUpperAscii, now().format("dd-MM-yyyy HH:mm:ss")):
# return nil
2025-05-22 20:03:22 +02:00
2025-05-23 13:55:00 +02:00
# Return tasks in JSON format
return % cq . agents [ agent . toUpperAscii ] . tasks . filterIt ( it . status ! = Completed )
2025-05-22 20:03:22 +02:00
proc handleResult * ( listener , agent , task : string , taskResult : Task ) =
{. cast ( gcsafe ) . } :
cq . writeLine ( fgBlack , styleBright , fmt" [*] [{task}] " , resetStyle , " Task execution finished. " )
2025-05-23 16:02:16 +02:00
if taskResult . result ! = " " :
cq . writeLine ( fgBlack , styleBright , fmt" [*] [{task}] " , resetStyle , " Output: " )
# Split result string on newline to keep formatting
for line in decode ( taskResult . result ) . split ( " \n " ) :
cq . writeLine ( line )
# Update task queue to include all tasks, except the one that was just completed
cq . agents [ agent ] . tasks = cq . agents [ agent ] . tasks . filterIt ( it . id ! = task )
2025-05-22 20:03:22 +02:00
return