Implemented listing agents by listener UUID

This commit is contained in:
Jakob Friedl
2025-05-15 14:27:45 +02:00
parent 2810ed3a95
commit 9d652df4e3
5 changed files with 111 additions and 11 deletions

View File

@@ -1,4 +1,4 @@
import terminal, strformat, strutils
import terminal, strformat, strutils, tables
import ../[types, globals, utils]
import ../db/database
@@ -16,6 +16,7 @@ Usage:
Commands:
list List all agents.
info Display details for a specific agent.
kill Terminate the connection of an active listener and remove it from the interface.
interact Interact with an active agent.
@@ -23,10 +24,46 @@ Options:
-h, --help""")
# List agents
proc agentList*(cq: Conquest, args: varargs[string]) =
proc agentList*(cq: Conquest) =
let agents = cq.dbGetAllAgents()
cq.drawTable(agents)
proc agentList*(cq: Conquest, listener: string) =
# Check if listener exists
if not cq.dbListenerExists(listener.toUpperAscii):
cq.writeLine(fgRed, styleBright, fmt"[-] Listener {listener.toUpperAscii} does not exist.")
return
let agents = cq.dbGetAllAgentsByListener(listener.toUpperAscii)
cq.drawTable(agents)
# 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]
# TODO: Improve formating
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}
First checkin: {agent.firstCheckin}
""")
# Terminate agent and remove it from the database
proc agentKill*(cq: Conquest, name: string) =
@@ -67,7 +104,7 @@ proc register*(agent: Agent): bool =
# 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
if not cq.dbListenerExists(agent.listener):
if not cq.dbListenerExists(agent.listener.toUpperAscii):
cq.writeLine(fgRed, styleBright, fmt"[-] Agent from {agent.ip} attempted to register to non-existent listener: {agent.listener}.", "\n")
return false

View File

@@ -165,6 +165,41 @@ proc dbGetAllAgents*(cq: Conquest): seq[Agent] =
return agents
proc dbGetAllAgentsByListener*(cq: Conquest, listenerName: string): seq[Agent] =
var agents: seq[Agent] = @[]
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
for row in conquestDb.iterate("SELECT name, listener, sleep, jitter, process, pid, username, hostname, domain, ip, os, elevated, firstCheckin FROM agents WHERE listener = ?;", listenerName):
let (name, listener, sleep, jitter, process, pid, username, hostname, domain, ip, os, elevated, firstCheckin) = row.unpack((string, string, int, float, string, int, string, string, string, string, string, bool, string))
let a = Agent(
name: name,
listener: listener,
sleep: sleep,
pid: pid,
username: username,
hostname: hostname,
domain: domain,
ip: ip,
os: os,
elevated: elevated,
firstCheckin: firstCheckin,
jitter: jitter,
process: process,
tasks: @[]
)
agents.add(a)
conquestDb.close()
except:
cq.writeLine(fgRed, styleBright, "[-] ", getCurrentExceptionMsg())
return agents
proc dbDeleteAgentByName*(cq: Conquest, name: string): bool =
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)

View File

@@ -1,3 +1,11 @@
import ./types
# Global variable for handling listeners, agents and console output
var cq*: Conquest
# Colors
# https://colors.sh/
# TODO Replace all colored output with custom colors
const yellow* = "\e[48;5;232m"
const resetColor* = "\e[0m"

View File

@@ -20,22 +20,28 @@ var parser = newParser:
help("Starts a new HTTP listener.")
option("-h", "-host", default=some("0.0.0.0"), help="IPv4 address to listen on.", required=false)
option("-p", "-port", help="Port to listen on.", required=true)
# TODO: Future features:
# flag("--dns", help="Use the DNS protocol for C2 communication.")
# flag("--doh", help="Use DNS over HTTPS for C2 communication.)
command("stop"):
help("Stop an active listener.")
option("-n", "-name", help="Name of the listener to stop.", required=true)
option("-n", "-name", help="Name of the listener.", required=true)
command("agent"):
help("Manage, build and interact with agents.")
command("list"):
help("List all agents.")
option("-n", "-name", help="Name of the listener.")
# TODO: Add a flag that allows the user to only list agents that are connected to a specific listener (-n <uuid>)
command("info"):
help("Display details for a specific agent.")
option("-n", "-name", help="Name of the agent.", required=true)
command("kill"):
help("Terminate the connection of an active listener and remove it from the interface.")
option("-n", "-name", help="Name of the agent to stop.", required=true)
option("-n", "-name", help="Name of the agent.", required=true)
command("interact"):
help("Interact with an active agent.")
@@ -81,7 +87,12 @@ proc handleConsoleCommand*(cq: Conquest, args: varargs[string]) =
of "agent":
case opts.agent.get.command
of "list":
cq.agentList()
if opts.agent.get.list.get.name == "":
cq.agentList()
else:
cq.agentList(opts.agent.get.list.get.name)
of "info":
cq.agentInfo(opts.agent.get.info.get.name)
of "kill":
cq.agentKill(opts.agent.get.kill.get.name)
of "interact":

View File

@@ -1,4 +1,4 @@
import re, strutils
import re, strutils, strformat, terminal
import ./types
@@ -38,7 +38,16 @@ proc border(left, mid, right: string, widths: seq[int]): string =
proc row(cells: seq[string], widths: seq[int]): string =
var row = vert
for i, cell in cells:
row.add(" " & cell.alignLeft(widths[i] - 2) & " " & vert)
# Truncate content of a cell with "..." when the value to be inserted is longer than the designated width
let w = widths[i] - 2
let c = if cell.len > w:
if w >= 3:
cell[0 ..< w - 3] & "..."
else:
".".repeat(max(0, w))
else:
cell
row.add(" " & c.alignLeft(w) & " " & vert)
return row
proc drawTable*(cq: Conquest, listeners: seq[Listener]) =
@@ -62,7 +71,7 @@ proc drawTable*(cq: Conquest, listeners: seq[Listener]) =
proc drawTable*(cq: Conquest, agents: seq[Agent]) =
let headers: seq[string] = @["Name", "Address", "Username", "Hostname", "Operating System", "Process", "PID"]
let widths = @[10, 17, 25, 20, 22, 15, 8]
let widths = @[10, 17, 20, 20, 20, 15, 7]
cq.writeLine(border(topLeft, topMid, topRight, widths))
cq.writeLine(row(headers, widths))