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 ../[types, globals, utils]
import ../db/database import ../db/database
@@ -16,6 +16,7 @@ Usage:
Commands: Commands:
list List all agents. 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. kill Terminate the connection of an active listener and remove it from the interface.
interact Interact with an active agent. interact Interact with an active agent.
@@ -23,10 +24,46 @@ Options:
-h, --help""") -h, --help""")
# List agents # List agents
proc agentList*(cq: Conquest, args: varargs[string]) = proc agentList*(cq: Conquest) =
let agents = cq.dbGetAllAgents() let agents = cq.dbGetAllAgents()
cq.drawTable(agents) 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 # Terminate agent and remove it from the database
proc agentKill*(cq: Conquest, name: string) = proc agentKill*(cq: Conquest, name: string) =
@@ -67,7 +104,7 @@ proc register*(agent: Agent): bool =
# Check if listener that is requested exists # Check if listener that is requested exists
# TODO: Verify that the listener accessed is also the listener specified in the URL # 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 # 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") cq.writeLine(fgRed, styleBright, fmt"[-] Agent from {agent.ip} attempted to register to non-existent listener: {agent.listener}.", "\n")
return false return false

View File

@@ -165,6 +165,41 @@ proc dbGetAllAgents*(cq: Conquest): seq[Agent] =
return agents 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 = proc dbDeleteAgentByName*(cq: Conquest, name: string): bool =
try: try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)

View File

@@ -1,3 +1,11 @@
import ./types import ./types
# Global variable for handling listeners, agents and console output
var cq*: Conquest 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.") help("Starts a new HTTP listener.")
option("-h", "-host", default=some("0.0.0.0"), help="IPv4 address to listen on.", required=false) 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) option("-p", "-port", help="Port to listen on.", required=true)
# TODO: Future features:
# flag("--dns", help="Use the DNS protocol for C2 communication.") # flag("--dns", help="Use the DNS protocol for C2 communication.")
# flag("--doh", help="Use DNS over HTTPS for C2 communication.) # flag("--doh", help="Use DNS over HTTPS for C2 communication.)
command("stop"): command("stop"):
help("Stop an active listener.") 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"): command("agent"):
help("Manage, build and interact with agents.") help("Manage, build and interact with agents.")
command("list"): command("list"):
help("List all agents.") 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>) # 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"): command("kill"):
help("Terminate the connection of an active listener and remove it from the interface.") 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"): command("interact"):
help("Interact with an active agent.") help("Interact with an active agent.")
@@ -81,7 +87,12 @@ proc handleConsoleCommand*(cq: Conquest, args: varargs[string]) =
of "agent": of "agent":
case opts.agent.get.command case opts.agent.get.command
of "list": 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": of "kill":
cq.agentKill(opts.agent.get.kill.get.name) cq.agentKill(opts.agent.get.kill.get.name)
of "interact": of "interact":

View File

@@ -1,4 +1,4 @@
import re, strutils import re, strutils, strformat, terminal
import ./types 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 = proc row(cells: seq[string], widths: seq[int]): string =
var row = vert var row = vert
for i, cell in cells: 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 return row
proc drawTable*(cq: Conquest, listeners: seq[Listener]) = 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]) = proc drawTable*(cq: Conquest, agents: seq[Agent]) =
let headers: seq[string] = @["Name", "Address", "Username", "Hostname", "Operating System", "Process", "PID"] 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(border(topLeft, topMid, topRight, widths))
cq.writeLine(row(headers, widths)) cq.writeLine(row(headers, widths))