diff --git a/src/client/views/console.nim b/src/client/views/console.nim index 5507735..98d0648 100644 --- a/src/client/views/console.nim +++ b/src/client/views/console.nim @@ -3,6 +3,7 @@ import strformat, strutils, times import imguin/[cimgui, glfw_opengl, simple] import ../utils/[appImGui, colors] import ../../common/[types] +import ../websocket const MAX_INPUT_LENGTH = 512 type @@ -270,6 +271,7 @@ proc draw*(component: ConsoleComponent, ws: WebSocket) = component.addItem(LOG_COMMAND, command) # Send command to team server + ws.sendAgentCommand(component.agent.agentId, 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 1152086..57cac7c 100644 --- a/src/client/views/listeners.nim +++ b/src/client/views/listeners.nim @@ -1,10 +1,10 @@ +import whisky import strutils import imguin/[cimgui, glfw_opengl, simple] import ../utils/appImGui import ../../common/[types, utils] import ./modals/[startListener, generatePayload] import ../websocket -import whisky type ListenersTableComponent* = ref object of RootObj diff --git a/src/client/websocket.nim b/src/client/websocket.nim index 3990699..f0bf0eb 100644 --- a/src/client/websocket.nim +++ b/src/client/websocket.nim @@ -1,6 +1,5 @@ import whisky import times, tables, json -import ./views/[sessions, listeners, console, eventlog] import ../common/[types, utils, serialize, event] export sendHeartbeat, recvEvent @@ -39,26 +38,13 @@ proc sendAgentBuild*(ws: WebSocket, buildInformation: AgentBuildInformation) = ) ws.sendEvent(event) -# proc sendAgentCommand*(ws: WebSocket, agentId: string, command: string) = -# var packer = Packer.init() - -# packer.add(cast[uint8](CLIENT_AGENT_COMMAND)) -# packer.add(string.toUuid(agentId)) -# packer.addDataWithLengthPrefix(string.toBytes(command)) -# let data = packer.pack() - -# ws.send(Bytes.toString(data), BinaryMessage) - -# proc sendAgentBuild*(ws: WebSocket, listenerId: string, sleepDelay: int, sleepMask: SleepObfuscationTechnique, spoofStack: bool, modules: uint32) = -# var packer = Packer.init() - -# packer.add(cast[uint8](CLIENT_AGENT_BUILD)) -# packer.add(string.toUuid(listenerId)) -# packer.add(cast[uint32](sleepDelay)) -# packer.add(cast[uint8](sleepMask)) -# packer.add(cast[uint8](spoofStack)) -# packer.add(modules) -# let data = packer.pack() - -# ws.send(Bytes.toString(data), BinaryMessage) - \ No newline at end of file +proc sendAgentCommand*(ws: WebSocket, agentId: string, command: string) = + let event = Event( + eventType: CLIENT_AGENT_COMMAND, + timestamp: now().toTime().toUnix(), + data: %*{ + "agentId": agentId, + "command": command + } + ) + ws.sendEvent(event) diff --git a/src/server/api/handlers.nim b/src/server/api/handlers.nim index 6763b2a..8315447 100644 --- a/src/server/api/handlers.nim +++ b/src/server/api/handlers.nim @@ -91,14 +91,17 @@ proc handleResult*(resultData: seq[byte]) = taskId = Uuid.toString(taskResult.taskId) agentId = Uuid.toString(taskResult.header.agentId) + cq.client.sendConsoleItem(agentId, LOG_INFO, fmt"{$resultData.len} bytes received.") cq.info(fmt"{$resultData.len} bytes received.") # Update task queue to include all tasks, except the one that was just completed case cast[StatusType](taskResult.status): of STATUS_COMPLETED: + cq.client.sendConsoleItem(agentId, LOG_SUCCESS, fmt"Task {taskId} completed.") cq.success(fmt"Task {taskId} completed.") cq.agents[agentId].tasks = cq.agents[agentId].tasks.filterIt(it.taskId != taskResult.taskId) of STATUS_FAILED: + cq.client.sendConsoleItem(agentId, LOG_ERROR, fmt"Task {taskId} failed.") cq.error(fmt"Task {taskId} failed.") cq.agents[agentId].tasks = cq.agents[agentId].tasks.filterIt(it.taskId != taskResult.taskId) of STATUS_IN_PROGRESS: @@ -106,8 +109,11 @@ proc handleResult*(resultData: seq[byte]) = case cast[ResultType](taskResult.resultType): of RESULT_STRING: - if int(taskResult.length) > 0: + if int(taskResult.length) > 0: + cq.client.sendConsoleItem(agentId, LOG_INFO, "Output:") cq.info("Output:") + cq.client.sendConsoleItem(agentId, LOG_OUTPUT, Bytes.toString(taskResult.data)) + # Split result string on newline to keep formatting for line in Bytes.toString(taskResult.data).split("\n"): cq.output(line) diff --git a/src/server/core/agent.nim b/src/server/core/agent.nim index 6990046..87e8306 100644 --- a/src/server/core/agent.nim +++ b/src/server/core/agent.nim @@ -5,6 +5,7 @@ import ../utils import ../core/logger import ../db/database import ../../common/types +import ../websocket # Utility functions proc addMultiple*(cq: Conquest, agents: seq[Agent]) = @@ -123,7 +124,7 @@ proc agentInteract*(cq: Conquest, name: string) = while command.replace(" ", "") != "back": command = cq.prompt.readLine() - cq.handleAgentCommand(command) + cq.handleAgentCommand(name, command) # Reset interactAgent field after interaction with agent is ended using 'back' command cq.interactAgent = nil diff --git a/src/server/core/server.nim b/src/server/core/server.nim index d23c670..3f8cca8 100644 --- a/src/server/core/server.nim +++ b/src/server/core/server.nim @@ -1,7 +1,7 @@ import prompt, terminal, argparse, parsetoml, times, json import strutils, strformat, system, tables -import ./[agent, listener, builder] +import ./[agent, listener, task, builder] import ../globals import ../db/database import ../core/logger @@ -175,7 +175,9 @@ proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {. case event.eventType: of CLIENT_AGENT_COMMAND: - discard + let agentId = event.data["agentId"].getStr() + let command = event.data["command"].getStr() + cq.handleAgentCommand(agentId, command) of CLIENT_LISTENER_START: let listener = event.data.to(UIListener) diff --git a/src/server/core/task.nim b/src/server/core/task.nim index b9e4620..93578ce 100644 --- a/src/server/core/task.nim +++ b/src/server/core/task.nim @@ -2,6 +2,7 @@ import strformat, terminal, tables, sequtils, strutils import ../protocol/parser import ../core/logger +import ../websocket import ../../modules/manager import ../../common/types @@ -50,33 +51,28 @@ proc handleHelp(cq: Conquest, parsed: seq[string]) = # Command was not found cq.error(fmt"The command '{parsed[1]}' does not exist." & '\n') -proc handleAgentCommand*(cq: Conquest, input: string) = - # Return if no command (or just whitespace) is entered - if input.replace(" ", "").len == 0: return +proc handleAgentCommand*(cq: Conquest, agentId: string, input: string) = cq.input(input) # Convert user input into sequence of string arguments let parsedArgs = parseInput(input) - # Handle 'back' command - if parsedArgs[0] == "back": - cq.interactAgent = nil - return - # Handle 'help' command if parsedArgs[0] == "help": cq.handleHelp(parsedArgs) return - + # Handle commands with actions on the agent try: let command = getCommandByName(parsedArgs[0]) - task = cq.createTask(command, parsedArgs[1..^1]) + task = cq.createTask(agentId, command, parsedArgs[1..^1]) # Add task to queue - cq.interactAgent.tasks.add(task) + cq.agents[agentId].tasks.add(task) + + cq.client.sendConsoleItem(agentId, LOG_INFO, fmt"Tasked agent to {command.description.toLowerAscii()}") cq.info(fmt"Tasked agent to {command.description.toLowerAscii()}") except CatchableError: diff --git a/src/server/protocol/parser.nim b/src/server/protocol/parser.nim index 03c6764..ae5f6be 100644 --- a/src/server/protocol/parser.nim +++ b/src/server/protocol/parser.nim @@ -1,5 +1,5 @@ import std/paths -import strutils, sequtils, times +import strutils, sequtils, times, tables import ../../common/[types, sequence, crypto, utils, serialize] proc parseInput*(input: string): seq[string] = @@ -84,12 +84,12 @@ proc parseArgument*(argument: Argument, value: string): TaskArg = return arg -proc createTask*(cq: Conquest, command: Command, arguments: seq[string]): Task = +proc createTask*(cq: Conquest, agentId: string, command: Command, arguments: seq[string]): Task = # Construct the task payload prefix var task: Task task.taskId = string.toUuid(generateUUID()) - task.listenerId = string.toUuid(cq.interactAgent.listenerId) + task.listenerId = string.toUuid(cq.agents[agentId].listenerId) task.timestamp = uint32(now().toTime().toUnix()) task.command = cast[uint16](command.commandType) task.argCount = uint8(arguments.len) @@ -116,7 +116,7 @@ proc createTask*(cq: Conquest, command: Command, arguments: seq[string]): Task = taskHeader.packetType = cast[uint8](MSG_TASK) taskHeader.flags = cast[uint16](FLAG_ENCRYPTED) taskHeader.size = 0'u32 - taskHeader.agentId = string.toUuid(cq.interactAgent.agentId) + taskHeader.agentId = string.toUuid(agentId) taskHeader.seqNr = nextSequence(taskHeader.agentId) taskHeader.iv = generateBytes(Iv) # Generate a random IV for AES-256 GCM taskHeader.gmac = default(AuthenticationTag)