Loot (downloads/screenshots) is now sent by the teamserver either on client-connection or when new loot is added. For images, smaller thumbnails are used to reduce size of network packets.

This commit is contained in:
Jakob Friedl
2025-10-09 12:14:38 +02:00
parent bcf845288c
commit 4e0eae77b8
12 changed files with 230 additions and 97 deletions

View File

@@ -30,3 +30,4 @@ requires "zippy >= 0.10.16"
requires "mummy >= 0.4.6"
requires "whisky >= 0.1.3"
requires "native_dialogs >= 0.2.0"
requires "pixie >= 5.1.0"

View File

@@ -153,6 +153,19 @@ proc main(ip: string = "localhost", port: int = 37573) =
event.timestamp
)
of CLIENT_LOOT_ADD:
let lootItem = event.data.to(LootItem)
case lootItem.itemType:
of DOWNLOAD:
lootDownloads.items.add(lootItem)
of SCREENSHOT:
lootScreenshots.addItem(lootItem)
else: discard
of CLIENT_SYNC_LOOT:
discard
else: discard
# Draw/update UI components/views
@@ -175,6 +188,7 @@ proc main(ip: string = "localhost", port: int = 37573) =
# This is done to ensure that closed console windows can be opened again
consoles = newConsoleTable
igShowDemoWindow(nil)
# render
@@ -183,5 +197,6 @@ proc main(ip: string = "localhost", port: int = 37573) =
if not showConquest:
app.handle.setWindowShouldClose(true)
when isMainModule:
import cligen; dispatch main

View File

@@ -1,4 +1,4 @@
import strformat, strutils, times
import strformat, strutils, times, os
import imguin/[cimgui, glfw_opengl, simple]
import ../../utils/[appImGui, colors]
import ../../../common/[types, utils]
@@ -6,7 +6,7 @@ import ../../../common/[types, utils]
type
DownloadsComponent* = ref object of RootObj
title: string
items: seq[LootItem]
items*: seq[LootItem]
selectedIndex: int
@@ -16,24 +16,6 @@ proc LootDownloads*(title: string): DownloadsComponent =
result.items = @[]
result.selectedIndex = -1
result.items.add(@[LootItem(
agentId: "DEADBEEF",
path: "C:\\Software\\Conquest\\README.md",
timestamp: now().toTime().toUnix(),
size: 1000,
host: "WKS-1",
data: string.toBytes("README.md\nPreview\nHello world.")
),
LootItem(
agentId: "DEADBEEF",
path: "C:\\Software\\Conquest\\README.md",
timestamp: now().toTime().toUnix(),
size: 1000,
host: "WKS-1",
data: string.toBytes("README.md\nPreview\nHello world.")
)
])
proc draw*(component: DownloadsComponent, showComponent: ptr bool) =
igBegin(component.title, showComponent, 0)
defer: igEnd()
@@ -60,12 +42,14 @@ proc draw*(component: DownloadsComponent, showComponent: ptr bool) =
ImGui_TableFlags_SizingStretchSame.int32
)
let cols: int32 = 4
let cols: int32 = 6
if igBeginTable("##Items", cols, tableFlags, vec2(0.0f, 0.0f), 0.0f):
igTableSetupColumn("ID", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("AgentID", ImGuiTableColumnFlags_DefaultHide.int32, 0.0f, 0)
igTableSetupColumn("Host", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Path", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Creation Date", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Size", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Host", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupScrollFreeze(0, 1)
igTableHeadersRow()
@@ -75,18 +59,24 @@ proc draw*(component: DownloadsComponent, showComponent: ptr bool) =
if igTableSetColumnIndex(0):
igPushID_Int(i.int32)
let isSelected = component.selectedIndex == i
if igSelectable_Bool(item.path.cstring, isSelected, ImGuiSelectableFlags_SpanAllColumns.int32 or ImGuiSelectableFlags_AllowOverlap.int32, vec2(0, 0)):
if igSelectable_Bool(item.lootId.cstring, isSelected, ImGuiSelectableFlags_SpanAllColumns.int32 or ImGuiSelectableFlags_AllowOverlap.int32, vec2(0, 0)):
component.selectedIndex = i
igPopID()
if igTableSetColumnIndex(1):
igText(item.timestamp.fromUnix().local().format("dd-MM-yyyy HH:mm:ss"))
igText(item.agentId)
if igTableSetColumnIndex(2):
igText($item.size)
igText(item.host.cstring)
if igTableSetColumnIndex(3):
igText(item.host.cstring)
igText(item.path.extractFilename().replace("C_", "C:/").replace("_", "/"))
if igTableSetColumnIndex(4):
igText(item.timestamp.fromUnix().local().format("dd-MM-yyyy HH:mm:ss"))
if igTableSetColumnIndex(5):
igText($item.size)
igEndTable()
@@ -99,7 +89,13 @@ proc draw*(component: DownloadsComponent, showComponent: ptr bool) =
if component.selectedIndex >= 0 and component.selectedIndex < component.items.len:
let item = component.items[component.selectedIndex]
igText(item.path)
igText(fmt("[{item.host}] "))
igSameLine(0.0f, 0.0f)
igText(item.path.extractFilename().replace("C_", "C:/").replace("_", "/"))
igSeparator()
igText(item.data)
else:
igText("Select item to preview contents")

View File

@@ -20,32 +20,14 @@ proc LootScreenshots*(title: string): ScreenshotsComponent =
result.title = title
result.items = @[]
result.selectedIndex = -1
result.items.add(@[LootItem(
agentId: "DEADBEEF",
timestamp: now().toTime().toUnix(),
size: 1000,
path: "/mnt/c/Users/jakob/Documents/Projects/conquest/data/loot/570DCB57/screenshot_1757769346.bmp",
host: "WKS-1",
data: string.toBytes(readFile("/mnt/c/Users/jakob/Documents/Projects/conquest/data/loot/570DCB57/screenshot_1757769346.bmp"))
),
LootItem(
agentId: "DEADBEEF",
timestamp: now().toTime().toUnix(),
path: "/mnt/c/Users/jakob/Documents/Projects/conquest/data/loot/C2468819/screenshot_1759238569.png",
size: 1000,
host: "WKS-1",
data: string.toBytes(readFile("/mnt/c/Users/jakob/Documents/Projects/conquest/data/loot/C2468819/screenshot_1759238569.png"))
)
])
result.textures = initTable[string, ScreenshotTexture]()
for item in result.items:
var textureId: GLuint
let (width, height) = loadTextureFromBytes(item.data, textureId)
proc addItem*(component: ScreenshotsComponent, screenshot: LootItem) =
component.items.add(screenshot)
result.textures[item.path] = ScreenshotTexture(
var textureId: GLuint
let (width, height) = loadTextureFromBytes(string.toBytes(screenshot.data), textureId)
component.textures[screenshot.path] = ScreenshotTexture(
textureId: textureId,
width: width,
height: height
@@ -77,12 +59,13 @@ proc draw*(component: ScreenshotsComponent, showComponent: ptr bool) =
ImGui_TableFlags_SizingStretchSame.int32
)
let cols: int32 = 4
let cols: int32 = 5
if igBeginTable("##Items", cols, tableFlags, vec2(0.0f, 0.0f), 0.0f):
igTableSetupColumn("Path", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Creation Date", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Size", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("ID", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("AgentID", ImGuiTableColumnFlags_DefaultHide.int32, 0.0f, 0)
igTableSetupColumn("Host", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Creation Date", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("File Size", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupScrollFreeze(0, 1)
igTableHeadersRow()
@@ -92,18 +75,21 @@ proc draw*(component: ScreenshotsComponent, showComponent: ptr bool) =
if igTableSetColumnIndex(0):
igPushID_Int(i.int32)
let isSelected = component.selectedIndex == i
if igSelectable_Bool(item.path.cstring, isSelected, ImGuiSelectableFlags_SpanAllColumns.int32 or ImGuiSelectableFlags_AllowOverlap.int32, vec2(0, 0)):
if igSelectable_Bool(item.lootId.cstring, isSelected, ImGuiSelectableFlags_SpanAllColumns.int32 or ImGuiSelectableFlags_AllowOverlap.int32, vec2(0, 0)):
component.selectedIndex = i
igPopID()
if igTableSetColumnIndex(1):
igText(item.timestamp.fromUnix().local().format("dd-MM-yyyy HH:mm:ss"))
igText(item.agentId)
if igTableSetColumnIndex(2):
igText($item.size)
igText(item.host.cstring)
if igTableSetColumnIndex(3):
igText(item.host.cstring)
igText(item.timestamp.fromUnix().local().format("dd-MM-yyyy HH:mm:ss"))
if igTableSetColumnIndex(4):
igText($item.size)
igEndTable()

View File

@@ -248,13 +248,14 @@ type
type
EventType* = enum
CLIENT_HEARTBEAT = 0'u8 # Basic checkin
CLIENT_KEY_EXCHANGE = 200'u8
CLIENT_KEY_EXCHANGE = 200'u8 # Unencrypted public key sent by both parties for key exchange
# Sent by client
CLIENT_AGENT_BUILD = 1'u8 # Generate an agent binary for a specific listener
CLIENT_AGENT_TASK = 2'u8 # Instruct TS to send queue a command for a specific agent
CLIENT_LISTENER_START = 3'u8 # Start a listener on the TS
CLIENT_LISTENER_STOP = 4'u8 # Stop a listener
CLIENT_REQUEST_SYNC = 5'u8 # Request to download a file/screenshot to the client
# Sent by team server
CLIENT_PROFILE = 100'u8 # Team server profile and configuration
@@ -349,10 +350,16 @@ type
spoofStack*: bool
modules*: uint32
LootItemType* = enum
DOWNLOAD = 0'u8
SCREENSHOT = 1'u8
LootItem* = ref object
itemType*: LootItemType
lootId*: string
agentId*: string
host*: string
path*: string
timestamp*: int64
size*: int
host*: string
data*: seq[byte]
data*: string # Image bytes or file content (binary data prefixed with length)

View File

@@ -1,4 +1,5 @@
import terminal, strformat, strutils, sequtils, tables, system, std/[dirs, paths]
import terminal, strformat, strutils, sequtils, tables, os, times
import std/[dirs, paths]
import ../globals
import ../db/database
@@ -118,20 +119,44 @@ proc handleResult*(resultData: seq[byte]) =
of RESULT_BINARY:
# Write binary data to a file
# A binary result packet consists of the filename and file contents, both prefixed with their respective lengths as a uint32 value, unless it is fragmented
# A binary result packet consists of the filename and file contents, both prefixed with their respective lengths as a uint32 value
var unpacker = Unpacker.init(Bytes.toString(taskResult.data))
let
fileName = unpacker.getDataWithLengthPrefix().replace("\\", "_").replace(":", "") # Replace path characters for better storage of downloaded files
fileBytes = unpacker.getDataWithLengthPrefix()
fileData = unpacker.getDataWithLengthPrefix()
# Create loot directory for the agent
createDir(cast[Path](fmt"{CONQUEST_ROOT}/data/loot/{agentId}"))
let downloadPath = fmt"{CONQUEST_ROOT}/data/loot/{agentId}/{fileName}"
writeFile(downloadPath, fileBytes)
writeFile(downloadPath, fileData)
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).")
# Get file information
let fileInfo = getFileInfo(downloadPath)
var lootItem = LootItem(
lootId: generateUuid(),
itemType: parseEnum[LootItemType](($cast[CommandType](taskResult.command)).split("_")[1]), # CMD_DOWNLOAD -> DOWNLOAD, CMD_SCREENSHOT -> SCREENSHOT
agentId: agentId,
path: downloadPath,
timestamp: fileInfo.creationTime.toUnix(),
size: fileInfo.size,
host: cq.agents[agentId].hostname
)
if lootItem.itemType == SCREENSHOT:
lootItem.data = createThumbnail(readFile(downloadPath)) # Create a smaller thumbnail version of the screenshot for better transportability
elif lootItem.itemType == DOWNLOAD:
lootItem.data = readFile(downloadPath) # Read downloaded file
# Store loot in database
if not cq.dbStoreLoot(lootItem):
raise newException(ValueError, fmt"Failed to store loot in database." & "\n")
# Send packet to client to display file/screenshot in the UI
cq.client.sendLoot(lootItem)
cq.output(fmt"File downloaded to {downloadPath} ({$fileData.len()} bytes).", "\n")
cq.client.sendConsoleItem(agentId, LOG_OUTPUT, fmt"File downloaded to {downloadPath} ({$fileData.len()} bytes).")
of RESULT_NO_OUTPUT:
cq.output()

View File

@@ -143,3 +143,12 @@ proc sendBuildlogItem*(client: WsConnection, logType: LogType, message: string)
)
if client != nil:
client.ws.sendEvent(event, client.sessionKey)
proc sendLoot*(client: WsConnection, loot: LootItem) =
let event = Event(
eventType: CLIENT_LOOT_ADD,
timestamp: now().toTime().toUnix(),
data: %loot
)
if client != nil:
client.ws.sendEvent(event, client.sessionKey)

View File

@@ -1,11 +1,11 @@
import system, terminal, tiny_sqlite
import ./[dbAgent, dbListener]
import ./[dbAgent, dbListener, dbLoot]
import ../core/logger
import ../../common/types
# Export functions so that only ./db/database is required to be imported
export dbAgent, dbListener
export dbAgent, dbListener, dbLoot
proc dbInit*(cq: Conquest) =
@@ -15,15 +15,15 @@ proc dbInit*(cq: Conquest) =
# Create tables
conquestDb.execScript("""
CREATE TABLE listeners (
name TEXT PRIMARY KEY,
listenerId TEXT PRIMARY KEY,
address TEXT NOT NULL,
port INTEGER NOT NULL UNIQUE,
protocol TEXT NOT NULL CHECK (protocol IN ('http'))
);
CREATE TABLE agents (
name TEXT PRIMARY KEY,
listener TEXT NOT NULL,
agentId TEXT PRIMARY KEY,
listenerId TEXT NOT NULL,
process TEXT NOT NULL,
pid INTEGER NOT NULL,
username TEXT NOT NULL,
@@ -40,6 +40,16 @@ proc dbInit*(cq: Conquest) =
sessionKey BLOB NOT NULL
);
CREATE TABLE loot (
lootId TEXT PRIMARY KEY,
itemType INTEGER NOT NULL,
agentId TEXT NOT NULL,
host TEXT NOT NULL,
path TEXT NOT NULL,
timestamp INTEGER NOT NULL,
size INTEGER NOT NULL
);
""")
cq.info("Using new database: \"", cq.dbPath, "\".\n")

View File

@@ -15,7 +15,7 @@ proc dbStoreAgent*(cq: Conquest, agent: Agent): bool =
let sessionKeyBlob = agent.sessionKey.toSeq()
conquestDb.exec("""
INSERT INTO agents (name, listener, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, sleep, modules, firstCheckin, latestCheckin, sessionKey)
INSERT INTO agents (agentId, listenerId, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, sleep, modules, firstCheckin, latestCheckin, sessionKey)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
""", 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)
@@ -32,7 +32,7 @@ proc dbGetAllAgents*(cq: Conquest): seq[Agent] =
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
for row in conquestDb.iterate("SELECT name, listener, sleep, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKey FROM agents;"):
for row in conquestDb.iterate("SELECT agentId, listenerId, 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, 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
@@ -77,7 +77,7 @@ proc dbGetAllAgentsByListener*(cq: Conquest, listenerName: string): seq[Agent] =
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
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):
for row in conquestDb.iterate("SELECT agentId, listenerId, sleep, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKey FROM agents WHERE listenerId = ?;", listenerName):
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
@@ -114,11 +114,11 @@ proc dbGetAllAgentsByListener*(cq: Conquest, listenerName: string): seq[Agent] =
return agents
proc dbDeleteAgentByName*(cq: Conquest, name: string): bool =
proc dbDeleteAgentByName*(cq: Conquest, agentId: string): bool =
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
conquestDb.exec("DELETE FROM agents WHERE name = ?", name)
conquestDb.exec("DELETE FROM agents WHERE agentId = ?", agentId)
conquestDb.close()
except:
@@ -131,7 +131,7 @@ proc dbAgentExists*(cq: Conquest, agentName: string): bool =
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
let res = conquestDb.one("SELECT 1 FROM agents WHERE name = ? LIMIT 1", agentName)
let res = conquestDb.one("SELECT 1 FROM agents WHERE agentId = ? LIMIT 1", agentName)
conquestDb.close()
@@ -144,7 +144,7 @@ proc dbUpdateSleep*(cq: Conquest, agentName: string, delay: int): bool =
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
conquestDb.exec("UPDATE agents SET sleep = ? WHERE name = ?", delay, agentName)
conquestDb.exec("UPDATE agents SET sleep = ? WHERE agentId = ?", delay, agentName)
conquestDb.close()
return true

View File

@@ -19,7 +19,7 @@ proc dbStoreListener*(cq: Conquest, listener: Listener): bool =
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
conquestDb.exec("""
INSERT INTO listeners (name, address, port, protocol)
INSERT INTO listeners (listenerId, address, port, protocol)
VALUES (?, ?, ?, ?);
""", listener.listenerId, listener.address, listener.port, $listener.protocol)
@@ -37,7 +37,7 @@ proc dbGetAllListeners*(cq: Conquest): seq[Listener] =
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
for row in conquestDb.iterate("SELECT name, address, port, protocol FROM listeners;"):
for row in conquestDb.iterate("SELECT listenerId, address, port, protocol FROM listeners;"):
let (listenerId, address, port, protocol) = row.unpack((string, string, int, string))
let l = Listener(
@@ -54,11 +54,11 @@ proc dbGetAllListeners*(cq: Conquest): seq[Listener] =
return listeners
proc dbDeleteListenerByName*(cq: Conquest, name: string): bool =
proc dbDeleteListenerByName*(cq: Conquest, listenerId: string): bool =
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
conquestDb.exec("DELETE FROM listeners WHERE name = ?", name)
conquestDb.exec("DELETE FROM listeners WHERE listenerId = ?", listenerId)
conquestDb.close()
except:
@@ -70,7 +70,7 @@ proc dbListenerExists*(cq: Conquest, listenerName: string): bool =
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
let res = conquestDb.one("SELECT 1 FROM listeners WHERE name = ? LIMIT 1", listenerName)
let res = conquestDb.one("SELECT 1 FROM listeners WHERE listenerId = ? LIMIT 1", listenerName)
conquestDb.close()

77
src/server/db/dbLoot.nim Normal file
View File

@@ -0,0 +1,77 @@
import strutils, system, terminal, tiny_sqlite, pixie
import stb_image/write as stbiw
import ../core/logger
import ../../common/[types, utils]
proc dbStoreLoot*(cq: Conquest, loot: LootItem): bool =
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
conquestDb.exec("""
INSERT INTO loot (lootId, itemType, agentId, host, path, timestamp, size)
VALUES (?, ?, ?, ?, ?, ?, ?);
""", loot.lootId, int(loot.itemType), loot.agentId, loot.host, loot.path, loot.timestamp, loot.size)
conquestDb.close()
except:
cq.error(getCurrentExceptionMsg())
return false
return true
proc createThumbnail*(data: string, maxWidth: int = 1024, quality: int = 90): string =
let img: Image = decodeImage(data)
let aspectRatio = img.height.float / img.width.float
let
width = min(maxWidth, img.width)
height = int(width.float * aspectRatio)
# Resize image
let thumbnail = img.resize(width, height)
# Convert to JPEG image for smaller file size
var rgbaData = newSeq[byte](width * height * 4)
var i = 0
for y in 0..<height:
for x in 0..<width:
let color = thumbnail[x, y]
rgbaData[i] = color.r
rgbaData[i + 1] = color.g
rgbaData[i + 2] = color.b
rgbaData[i + 3] = color.a
i += 4
return Bytes.toString(stbiw.writeJPG(width, height, 4, rgbaData, quality))
proc dbGetLoot*(cq: Conquest): seq[LootItem] =
var loot: seq[LootItem] = @[]
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
for row in conquestDb.iterate("SELECT lootId, itemType, agentId, host, path, timestamp, size FROM loot;"):
let (lootId, itemType, agentId, host, path, timestamp, size) = row.unpack((string, int, string, string, string, int64, int))
var l = LootItem(
lootId: lootId,
itemType: cast[LootItemType](itemType),
agentId: agentId,
host: host,
path: path,
timestamp: timestamp,
size: size
)
if l.itemType == SCREENSHOT:
l.data = createThumbnail(readFile(path)) # Create a smaller thumbnail version of the screenshot for better transportability
elif l.itemType == DOWNLOAD:
l.data = readFile(path) # Read downloaded file
loot.add(l)
conquestDb.close()
except:
cq.error(getCurrentExceptionMsg())
return loot

View File

@@ -56,14 +56,21 @@ proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {.
cq.client.sessionKey = deriveSessionKey(cq.keyPair, publicKey)
# Send relevant information to the client
# - C2 profile
# - agent sessions
# - listeners
# C2 profile
cq.client.sendProfile(cq.profile)
# Listeners
for id, listener in cq.listeners:
cq.client.sendListener(listener)
# Agent sessions
for id, agent in cq.agents:
cq.client.sendAgent(agent)
# Downloads & Screenshots
for lootItem in cq.dbGetLoot():
cq.client.sendLoot(lootItem)
cq.client.sendEventlogItem(LOG_SUCCESS_SHORT, "Connected to Conquest team server.")
of CLIENT_AGENT_TASK: