From fbe85493b25cd3cafd8db2f6bb30865175eb4426 Mon Sep 17 00:00:00 2001 From: Jakob Friedl <71284620+jakobfriedl@users.noreply.github.com> Date: Wed, 1 Oct 2025 21:57:26 +0200 Subject: [PATCH] Implemented websocket (client <-> server) traffic encryption & compression. --- src/client/main.nim | 28 ++++--- src/client/views/console.nim | 8 +- src/client/views/listeners.nim | 8 +- src/client/websocket.nim | 28 ++++--- src/common/event.nim | 60 ++++++++++++--- src/common/serialize.nim | 1 - src/common/types.nim | 23 ++++-- src/server/core/listener.nim | 2 +- src/server/core/server.nim | 132 --------------------------------- src/server/main.nim | 34 ++++++--- src/server/websocket.nim | 42 +++++++---- 11 files changed, 160 insertions(+), 206 deletions(-) delete mode 100644 src/server/core/server.nim diff --git a/src/client/main.nim b/src/client/main.nim index c9e6f37..dea94e0 100644 --- a/src/client/main.nim +++ b/src/client/main.nim @@ -2,7 +2,7 @@ import whisky import tables, strutils, strformat, json, parsetoml, base64, os # native_dialogs import ./utils/[appImGui, globals] import ./views/[dockspace, sessions, listeners, eventlog, console] -import ../common/[types, utils] +import ../common/[types, utils, crypto] import ./websocket import sugar @@ -39,9 +39,15 @@ proc main(ip: string = "localhost", port: int = 37573) = let io = igGetIO() + # Create key pair + let clientKeyPair = generateKeyPair() + # Initiate WebSocket connection - let ws = newWebSocket(fmt"ws://{ip}:{$port}") - defer: ws.close() + var connection = WsConnection( + ws: newWebSocket(fmt"ws://{ip}:{$port}"), + sessionKey: default(Key) + ) + defer: connection.ws.close() # main loop while not app.handle.windowShouldClose: @@ -59,13 +65,17 @@ proc main(ip: string = "localhost", port: int = 37573) = WebSocket communication with the team server ]# # Continuously send heartbeat messages - ws.sendHeartbeat() + connection.ws.sendHeartbeat() # Receive and parse websocket response message - let event = recvEvent(ws.receiveMessage().get()) + let event = recvEvent(connection.ws.receiveMessage().get(), connection.sessionKey) case event.eventType: + of CLIENT_KEY_EXCHANGE: + connection.sessionKey = deriveSessionKey(clientKeyPair, decode(event.data["publicKey"].getStr()).toKey()) + connection.sendPublicKey(clientKeyPair.publicKey) + of CLIENT_PROFILE: - profile = parsetoml.parseString(event.data["profile"].getStr()) + profile = parsetoml.parseString(event.data["profile"].getStr()) of CLIENT_LISTENER_ADD: let listener = event.data.to(UIListener) @@ -90,7 +100,7 @@ proc main(ip: string = "localhost", port: int = 37573) = igSetNextWindowDockID(listenersWindow.DockNode.ID, ImGuiCond_FirstUseEver.int32) else: igSetNextWindowDockID(dockBottom, ImGuiCond_FirstUseEver.int32) - consoles[agent.agentId].draw(ws) + consoles[agent.agentId].draw(connection) consoles[agent.agentId].showConsole = false of CLIENT_AGENT_CHECKIN: @@ -127,7 +137,7 @@ proc main(ip: string = "localhost", port: int = 37573) = # Draw/update UI components/views if showSessionsTable: sessionsTable.draw(addr showSessionsTable) - if showListeners: listenersTable.draw(addr showListeners, ws) + if showListeners: listenersTable.draw(addr showListeners, connection) if showEventlog: eventlog.draw(addr showEventlog) # Show console windows @@ -136,7 +146,7 @@ proc main(ip: string = "localhost", port: int = 37573) = if console.showConsole: # Ensure that new console windows are docked to the bottom panel by default igSetNextWindowDockID(dockBottom, ImGuiCond_FirstUseEver.int32) - console.draw(ws) + console.draw(connection) newConsoleTable[agentId] = console # Update the consoles table with only those sessions that have not been closed yet diff --git a/src/client/views/console.nim b/src/client/views/console.nim index 44dfabb..3fd8d4d 100644 --- a/src/client/views/console.nim +++ b/src/client/views/console.nim @@ -170,7 +170,7 @@ proc handleHelp(component: ConsoleComponent, parsed: seq[string]) = # Command was not found component.addItem(LOG_ERROR, fmt"The command '{parsed[1]}' does not exist.") -proc handleAgentCommand*(component: ConsoleComponent, ws: WebSocket, input: string) = +proc handleAgentCommand*(component: ConsoleComponent, connection: WsConnection, input: string) = # Convert user input into sequence of string arguments let parsedArgs = parseInput(input) @@ -186,7 +186,7 @@ proc handleAgentCommand*(component: ConsoleComponent, ws: WebSocket, input: stri command = getCommandByName(parsedArgs[0]) task = createTask(component.agent.agentId, component.agent.listenerId, command, parsedArgs[1..^1]) - ws.sendAgentTask(component.agent.agentId, task) + connection.sendAgentTask(component.agent.agentId, task) component.addItem(LOG_INFO, fmt"Tasked agent to {command.description.toLowerAscii()} ({Uuid.toString(task.taskId)})") except CatchableError: @@ -219,7 +219,7 @@ proc print(item: ConsoleItem) = igSameLine(0.0f, 0.0f) igTextUnformatted(item.text.cstring, nil) -proc draw*(component: ConsoleComponent, ws: WebSocket) = +proc draw*(component: ConsoleComponent, connection: WsConnection) = igBegin(fmt"[{component.agent.agentId}] {component.agent.username}@{component.agent.hostname}".cstring, addr component.showConsole, 0) defer: igEnd() @@ -340,7 +340,7 @@ proc draw*(component: ConsoleComponent, ws: WebSocket) = component.addItem(LOG_COMMAND, command) # Send command to team server - component.handleAgentCommand(ws, command) + component.handleAgentCommand(connection, command) # Add command to console history component.history.add(command) diff --git a/src/client/views/listeners.nim b/src/client/views/listeners.nim index 57cac7c..eda4264 100644 --- a/src/client/views/listeners.nim +++ b/src/client/views/listeners.nim @@ -22,7 +22,7 @@ proc ListenersTable*(title: string): ListenersTableComponent = result.startListenerModal = ListenerModal() result.generatePayloadModal = AgentModal() -proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebSocket) = +proc draw*(component: ListenersTableComponent, showComponent: ptr bool, connection: WsConnection) = igBegin(component.title, showComponent, 0) defer: igEnd() @@ -41,11 +41,11 @@ proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebS let listener = component.startListenerModal.draw() if listener != nil: - ws.sendStartListener(listener) + connection.sendStartListener(listener) let buildInformation = component.generatePayloadModal.draw(component.listeners) if buildInformation != nil: - ws.sendAgentBuild(buildInformation) + connection.sendAgentBuild(buildInformation) #[ Listener table @@ -106,7 +106,7 @@ proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebS if not ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](i)): newListeners.add(listener) else: - ws.sendStopListener(listener.listenerId) + connection.sendStopListener(listener.listenerId) component.listeners = newListeners ImGuiSelectionBasicStorage_Clear(component.selection) diff --git a/src/client/websocket.nim b/src/client/websocket.nim index 2bad938..2051912 100644 --- a/src/client/websocket.nim +++ b/src/client/websocket.nim @@ -1,20 +1,30 @@ import whisky -import times, tables, json +import times, tables, json, base64 import ../common/[types, utils, serialize, event] export sendHeartbeat, recvEvent #[ Client -> Server ]# -proc sendStartListener*(ws: WebSocket, listener: UIListener) = +proc sendPublicKey*(connection: WsConnection, publicKey: Key) = + let event = Event( + eventType: CLIENT_KEY_EXCHANGE, + timestamp: now().toTime().toUnix(), + data: %*{ + "publicKey": encode(Bytes.toString(publicKey)) + } + ) + connection.ws.sendEvent(event, connection.sessionKey) + +proc sendStartListener*(connection: WsConnection, listener: UIListener) = let event = Event( eventType: CLIENT_LISTENER_START, timestamp: now().toTime().toUnix(), data: %listener ) - ws.sendEvent(event) + connection.ws.sendEvent(event, connection.sessionKey) -proc sendStopListener*(ws: WebSocket, listenerId: string) = +proc sendStopListener*(connection: WsConnection, listenerId: string) = let event = Event( eventType: CLIENT_LISTENER_STOP, timestamp: now().toTime().toUnix(), @@ -22,9 +32,9 @@ proc sendStopListener*(ws: WebSocket, listenerId: string) = "listenerId": listenerId } ) - ws.sendEvent(event) + connection.ws.sendEvent(event, connection.sessionKey) -proc sendAgentBuild*(ws: WebSocket, buildInformation: AgentBuildInformation) = +proc sendAgentBuild*(connection: WsConnection, buildInformation: AgentBuildInformation) = let event = Event( eventType: CLIENT_AGENT_BUILD, timestamp: now().toTime().toUnix(), @@ -36,9 +46,9 @@ proc sendAgentBuild*(ws: WebSocket, buildInformation: AgentBuildInformation) = "modules": buildInformation.modules } ) - ws.sendEvent(event) + connection.ws.sendEvent(event, connection.sessionKey) -proc sendAgentTask*(ws: WebSocket, agentId: string, task: Task) = +proc sendAgentTask*(connection: WsConnection, agentId: string, task: Task) = let event = Event( eventType: CLIENT_AGENT_TASK, timestamp: now().toTime().toUnix(), @@ -47,4 +57,4 @@ proc sendAgentTask*(ws: WebSocket, agentId: string, task: Task) = "task": task } ) - ws.sendEvent(event) + connection.ws.sendEvent(event, connection.sessionKey) diff --git a/src/common/event.nim b/src/common/event.nim index 8629ca9..3c4d6af 100644 --- a/src/common/event.nim +++ b/src/common/event.nim @@ -3,26 +3,66 @@ when defined(server): when defined(client): import whisky -import times, json -import ./[types, utils, serialize] +import times, json, zippy +import ./[types, utils, serialize, crypto] -proc sendEvent*(ws: WebSocket, event: Event) = +proc sendEvent*(ws: WebSocket, event: Event, key: Key = default(Key)) = var packer = Packer.init() + let iv = generateBytes(Iv) + + var + data = string.toBytes($event.data) + packer.add(cast[uint8](event.eventType)) packer.add(cast[uint32](event.timestamp)) - packer.addDataWithLengthPrefix(string.toBytes($event.data)) - let data = packer.pack() + + if event.eventType != CLIENT_KEY_EXCHANGE and event.eventType != CLIENT_HEARTBEAT: + # Compress data + let compressed = compress(data, BestCompression, dfGzip) + + # Encrypt data + let (encData, gmac) = encrypt(key, iv, compressed) + + packer.addData(iv) # 12 bytes IV + packer.addData(gmac) # 16 bytes Authentication Tag + packer.addDataWithLengthPrefix(encData) + else: + packer.addDataWithLengthPrefix(data) + + let body = packer.pack() - ws.send(Bytes.toString(data), BinaryMessage) + ws.send(Bytes.toString(body), BinaryMessage) + +proc recvEvent*(message: Message, key: Key = default(Key)): Event = -proc recvEvent*(message: Message): Event = var unpacker = Unpacker.init(message.data) + let + eventType = cast[EventType](unpacker.getUint8()) + timestamp = cast[int64](unpacker.getUint32()) + var data: string + + if eventType != CLIENT_KEY_EXCHANGE and eventType != CLIENT_HEARTBEAT: + + let + iv = unpacker.getByteArray(Iv) + gmac = unpacker.getByteArray(AuthenticationTag) + encData = string.toBytes(unpacker.getDataWithLengthPrefix()) + + # Decrypt data + let (decData, tag) = decrypt(key, iv, encData) + if tag != gmac: + raise newException(CatchableError, "Invalid authentication tag.") + + # Decompress data + data = Bytes.toString(uncompress(decData, dfGzip)) + else: + data = unpacker.getDataWithLengthPrefix() return Event( - eventType: cast[EventType](unpacker.getUint8()), - timestamp: cast[int64](unpacker.getUint32()), - data: parseJson(unpacker.getDataWithLengthPrefix()) + eventType: eventType, + timestamp: timestamp, + data: parseJson(data) ) proc sendHeartbeat*(ws: WebSocket) = diff --git a/src/common/serialize.nim b/src/common/serialize.nim index c758eb3..04f9632 100644 --- a/src/common/serialize.nim +++ b/src/common/serialize.nim @@ -93,7 +93,6 @@ proc getUint64*(unpacker: Unpacker): uint64 = unpacker.position += 8 proc getBytes*(unpacker: Unpacker, length: int): seq[byte] = - if length <= 0: return @[] diff --git a/src/common/types.nim b/src/common/types.nim index 8d1ed7d..8d85e8e 100644 --- a/src/common/types.nim +++ b/src/common/types.nim @@ -1,8 +1,10 @@ import tables import times import parsetoml, json -import mummy import system +import mummy +when defined(client): + import whisky # Custom Binary Task structure const @@ -242,10 +244,11 @@ type type EventType* = enum CLIENT_HEARTBEAT = 0'u8 # Basic checkin - + CLIENT_KEY_EXCHANGE = 200'u8 + # 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_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 @@ -255,8 +258,8 @@ type CLIENT_AGENT_ADD = 102'u8 # Add agent to sessions table 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_CONSOLE_ITEM = 105'u8 # Add entry to a agent's console + CLIENT_EVENTLOG_ITEM = 106'u8 # Add entry to the eventlog Event* = object eventType*: EventType @@ -271,8 +274,12 @@ type Profile* = TomlValueRef - UIClient* = ref object - ws*: WebSocket + WsConnection* = ref object + when defined(server): + ws*: mummy.WebSocket + when defined(client): + ws*: whisky.WebSocket + sessionKey*: Key Conquest* = ref object dbPath*: string @@ -281,7 +288,7 @@ type agents*: Table[string, Agent] keyPair*: KeyPair profile*: Profile - client*: UIClient + client*: WsConnection AgentCtx* = ref object agentId*: string diff --git a/src/server/core/listener.nim b/src/server/core/listener.nim index d72dbfc..7fa80ff 100644 --- a/src/server/core/listener.nim +++ b/src/server/core/listener.nim @@ -5,7 +5,7 @@ import parsetoml import ../api/routes import ../db/database import ../core/logger -import ../../common/[types, utils, profile] +import ../../common/[types, profile] import ../websocket proc serve(listener: Listener) {.thread.} = diff --git a/src/server/core/server.nim b/src/server/core/server.nim deleted file mode 100644 index e2bf208..0000000 --- a/src/server/core/server.nim +++ /dev/null @@ -1,132 +0,0 @@ -import prompt, terminal, argparse, parsetoml, times, json, math -import strutils, strformat, system, tables - -import ./[agent, listener, builder] -import ../globals -import ../db/database -import ../core/logger -import ../../common/[types, crypto, utils, profile, event] -import ../websocket -import mummy, mummy/routers - -proc header() = - echo "" - echo "┏┏┓┏┓┏┓┓┏┏┓┏╋" - echo "┗┗┛┛┗┗┫┗┻┗ ┛┗ V0.1" - echo " ┗ @jakobfriedl" - echo "─".repeat(21) - echo "" - -proc init*(T: type Conquest, profile: Profile): Conquest = - var cq = new Conquest - cq.listeners = initTable[string, Listener]() - cq.threads = initTable[string, Thread[Listener]]() - cq.agents = initTable[string, Agent]() - cq.interactAgent = nil - cq.profile = profile - cq.keyPair = loadKeyPair(CONQUEST_ROOT & "/" & profile.getString("private-key-file")) - cq.dbPath = CONQUEST_ROOT & "/" & profile.getString("database-file") - cq.client = nil - return cq - -#[ - WebSocket -]# -proc upgradeHandler(request: Request) = - {.cast(gcsafe).}: - let ws = request.upgradeToWebSocket() - cq.client = UIClient( - ws: ws - ) - -proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {.gcsafe.} = - {.cast(gcsafe).}: - case event: - of OpenEvent: - # New client connected to team server - # Send profile, sessions and listeners to the UI client - cq.client.sendProfile(cq.profile) - for id, listener in cq.listeners: - cq.client.sendListener(listener) - for id, agent in cq.agents: - cq.client.sendAgent(agent) - cq.client.sendEventlogItem(LOG_SUCCESS_SHORT, "CQ-V1") - - of MessageEvent: - # Continuously send heartbeat messages - ws.sendHeartbeat() - - let event = message.recvEvent() - - case event.eventType: - of CLIENT_AGENT_TASK: - let agentId = event.data["agentId"].getStr() - let task = event.data["task"].to(Task) - cq.agents[agentId].tasks.add(task) - - of CLIENT_LISTENER_START: - let listener = event.data.to(UIListener) - cq.listenerStart(listener.listenerId, listener.address, listener.port, listener.protocol) - - of CLIENT_LISTENER_STOP: - let listenerId = event.data["listenerId"].getStr() - cq.listenerStop(listenerId) - - of CLIENT_AGENT_BUILD: - let - listenerId = event.data["listenerId"].getStr() - sleepDelay = event.data["sleepDelay"].getInt() - sleepTechnique = cast[SleepObfuscationTechnique](event.data["sleepTechnique"].getInt()) - spoofStack = event.data["spoofStack"].getBool() - modules = cast[uint32](event.data["modules"].getInt()) - - let payload = cq.agentBuild(listenerId, sleepDelay, sleepTechnique, spoofStack, modules) - if payload.len() != 0: - cq.client.sendAgentPayload(payload) - - else: discard - - of ErrorEvent: - discard - of CloseEvent: - # Set the client instance to nil again to prevent debug error messages - cq.client = nil - -proc startServer*(profilePath: string) = - - # Ensure that the conquest root directory was passed as a compile-time define - when not defined(CONQUEST_ROOT): - quit(0) - - header() - - try: - # Initialize framework context - # Load and parse profile - let profile = parsetoml.parseFile(profilePath) - cq = Conquest.init(profile) - - cq.info("Using profile \"", profile.getString("name"), "\" (", profilePath ,").") - - except CatchableError as err: - echo err.msg - quit(0) - - # Initialize database - cq.dbInit() - for agent in cq.dbGetAllAgents(): - cq.agents[agent.agentId] = agent - for listener in cq.dbGetAllListeners(): - cq.listeners[listener.listenerId] = listener - - # Restart existing listeners - for listenerId, listener in cq.listeners: - cq.listenerStart(listenerId, listener.address, listener.port, listener.protocol) - - # Start websocket server - 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) - server.serve(Port(cq.profile.getInt("team-server.port")), "0.0.0.0") \ No newline at end of file diff --git a/src/server/main.nim b/src/server/main.nim index 429c00d..cdea99c 100644 --- a/src/server/main.nim +++ b/src/server/main.nim @@ -1,11 +1,11 @@ -import terminal, parsetoml, json, math +import terminal, parsetoml, json, math, base64 import strutils, strformat, system, tables import ./core/[listener, builder] import ./globals import ./db/database import ./core/logger -import ../common/[types, crypto, profile, event] +import ../common/[types, crypto, utils, profile, event] import ./websocket import mummy, mummy/routers @@ -25,7 +25,7 @@ proc init*(T: type Conquest, profile: Profile): Conquest = cq.profile = profile cq.keyPair = loadKeyPair(CONQUEST_ROOT & "/" & profile.getString("private-key-file")) cq.dbPath = CONQUEST_ROOT & "/" & profile.getString("database-file") - cq.client = nil + cq.client = nil return cq #[ @@ -34,7 +34,7 @@ proc init*(T: type Conquest, profile: Profile): Conquest = proc upgradeHandler(request: Request) = {.cast(gcsafe).}: let ws = request.upgradeToWebSocket() - cq.client = UIClient( + cq.client = WsConnection( ws: ws ) @@ -43,21 +43,31 @@ proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {. case event: of OpenEvent: # New client connected to team server - # Send profile, sessions and listeners to the UI client - cq.client.sendProfile(cq.profile) - for id, listener in cq.listeners: - cq.client.sendListener(listener) - for id, agent in cq.agents: - cq.client.sendAgent(agent) - cq.client.sendEventlogItem(LOG_SUCCESS_SHORT, "CQ-V1") + # Send the public key for the key exchange, all other information with be transmitted when the key exchange is completed + cq.client.sendPublicKey(cq.keyPair.publicKey) of MessageEvent: # Continuously send heartbeat messages ws.sendHeartbeat() - let event = message.recvEvent() + let event = message.recvEvent(cq.client.sessionKey) case event.eventType: + of CLIENT_KEY_EXCHANGE: + let publicKey = decode(event.data["publicKey"].getStr()).toKey() + cq.client.sessionKey = deriveSessionKey(cq.keyPair, publicKey) + + # Send relevant information to the client + # - C2 profile + # - agent sessions + # - listeners + cq.client.sendProfile(cq.profile) + for id, listener in cq.listeners: + cq.client.sendListener(listener) + for id, agent in cq.agents: + cq.client.sendAgent(agent) + cq.client.sendEventlogItem(LOG_SUCCESS_SHORT, "CQ-V1") + of CLIENT_AGENT_TASK: let agentId = event.data["agentId"].getStr() let task = event.data["task"].to(Task) diff --git a/src/server/websocket.nim b/src/server/websocket.nim index 9e32cde..43a836a 100644 --- a/src/server/websocket.nim +++ b/src/server/websocket.nim @@ -1,6 +1,5 @@ import times, json, base64, parsetoml -import ../common/[types, event] - +import ../common/[types, utils, event] export sendHeartbeat, recvEvent proc `%`*(agent: Agent): JsonNode = @@ -29,7 +28,18 @@ proc `%`*(listener: Listener): JsonNode = #[ Server -> Client ]# -proc sendProfile*(client: UIClient, profile: Profile) = +proc sendPublicKey*(client: WsConnection, publicKey: Key) = + let event = Event( + eventType: CLIENT_KEY_EXCHANGE, + timestamp: now().toTime().toUnix(), + data: %*{ + "publicKey": encode(Bytes.toString(publicKey)) + } + ) + if client != nil: + client.ws.sendEvent(event, client.sessionKey) + +proc sendProfile*(client: WsConnection, profile: Profile) = let event = Event( eventType: CLIENT_PROFILE, timestamp: now().toTime().toUnix(), @@ -38,9 +48,9 @@ proc sendProfile*(client: UIClient, profile: Profile) = } ) if client != nil: - client.ws.sendEvent(event) + client.ws.sendEvent(event, client.sessionKey) -proc sendEventlogItem*(client: UIClient, logType: LogType, message: string) = +proc sendEventlogItem*(client: WsConnection, logType: LogType, message: string) = let event = Event( eventType: CLIENT_EVENTLOG_ITEM, timestamp: now().toTime().toUnix(), @@ -50,27 +60,27 @@ proc sendEventlogItem*(client: UIClient, logType: LogType, message: string) = } ) if client != nil: - client.ws.sendEvent(event) + client.ws.sendEvent(event, client.sessionKey) -proc sendAgent*(client: UIClient, agent: Agent) = +proc sendAgent*(client: WsConnection, agent: Agent) = let event = Event( eventType: CLIENT_AGENT_ADD, timestamp: now().toTime().toUnix(), data: %agent ) if client != nil: - client.ws.sendEvent(event) + client.ws.sendEvent(event, client.sessionKey) -proc sendListener*(client: UIClient, listener: Listener) = +proc sendListener*(client: WsConnection, listener: Listener) = let event = Event( eventType: CLIENT_LISTENER_ADD, timestamp: now().toTime().toUnix(), data: %listener ) if client != nil: - client.ws.sendEvent(event) + client.ws.sendEvent(event, client.sessionKey) -proc sendAgentCheckin*(client: UIClient, agentId: string) = +proc sendAgentCheckin*(client: WsConnection, agentId: string) = let event = Event( eventType: CLIENT_AGENT_CHECKIN, timestamp: now().toTime().toUnix(), @@ -79,9 +89,9 @@ proc sendAgentCheckin*(client: UIClient, agentId: string) = } ) if client != nil: - client.ws.sendEvent(event) + client.ws.sendEvent(event, client.sessionKey) -proc sendAgentPayload*(client: UIClient, bytes: seq[byte]) = +proc sendAgentPayload*(client: WsConnection, bytes: seq[byte]) = let event = Event( eventType: CLIENT_AGENT_PAYLOAD, timestamp: now().toTime().toUnix(), @@ -90,9 +100,9 @@ proc sendAgentPayload*(client: UIClient, bytes: seq[byte]) = } ) if client != nil: - client.ws.sendEvent(event) + client.ws.sendEvent(event, client.sessionKey) -proc sendConsoleItem*(client: UIClient, agentId: string, logType: LogType, message: string) = +proc sendConsoleItem*(client: WsConnection, agentId: string, logType: LogType, message: string) = let event = Event( eventType: CLIENT_CONSOLE_ITEM, timestamp: now().toTime().toUnix(), @@ -103,4 +113,4 @@ proc sendConsoleItem*(client: UIClient, agentId: string, logType: LogType, messa } ) if client != nil: - client.ws.sendEvent(event) + client.ws.sendEvent(event, client.sessionKey)