Started porting over functionality to the ImGui client via websocket communication.

This commit is contained in:
Jakob Friedl
2025-09-25 19:22:17 +02:00
parent f0dbcdfc58
commit 14771a4b50
22 changed files with 455 additions and 569 deletions

View File

@@ -1,5 +1,6 @@
switch "o", "bin/client" switch "o", "bin/client"
switch "d", "client"
switch "d", "ImGuiTextSelect" switch "d", "ImGuiTextSelect"
# Select compiler # Select compiler

77
src/client/event/recv.nim Normal file
View File

@@ -0,0 +1,77 @@
import whisky
import times, tables
import ../views/[sessions, listeners, console, eventlog]
import ../../common/[types, utils, event]
export recvEvent
#[
Server -> Client
]#
# proc getMessageType*(message: Message): EventType =
# var unpacker = Unpacker.init(message.data)
# return cast[EventType](unpacker.getUint8())
# proc receiveAgentPayload*(message: Message): seq[byte] =
# var unpacker = Unpacker.init(message.data)
# discard unpacker.getUint8()
# return string.toBytes(unpacker.getDataWithLengthPrefix())
# proc receiveAgentConnection*(message: Message, sessions: ptr SessionsTableComponent) =
# var unpacker = Unpacker.init(message.data)
# discard unpacker.getUint8()
# let agent = Agent(
# agentId: Uuid.toString(unpacker.getUint32()),
# listenerId: Uuid.toString(unpacker.getUint32()),
# username: unpacker.getDataWithLengthPrefix(),
# hostname: unpacker.getDataWithLengthPrefix(),
# domain: unpacker.getDataWithLengthPrefix(),
# ip: unpacker.getDataWithLengthPrefix(),
# os: unpacker.getDataWithLengthPrefix(),
# process: unpacker.getDataWithLengthPrefix(),
# pid: int(unpacker.getUint32()),
# elevated: unpacker.getUint8() != 0,
# sleep: int(unpacker.getUint32()),
# tasks: @[],
# firstCheckin: cast[int64](unpacker.getUint32()).fromUnix().utc(),
# latestCheckin: cast[int64](unpacker.getUint32()).fromUnix().utc(),
# )
# sessions.agents.add(agent)
# proc receiveAgentCheckin*(message: Message, sessions: ptr SessionsTableComponent)=
# var unpacker = Unpacker.init(message.data)
# discard unpacker.getUint8()
# let agentId = Uuid.toString(unpacker.getUint32())
# let timestamp = cast[int64](unpacker.getUint32())
# # TODO: Update checkin
# proc receiveConsoleItem*(message: Message, consoles: ptr Table[string, ConsoleComponent]) =
# var unpacker = Unpacker.init(message.data)
# discard unpacker.getUint8()
# let
# agentId = Uuid.toString(unpacker.getUint32())
# logType = cast[LogType](unpacker.getUint8())
# timestamp = cast[int64](unpacker.getUint32())
# message = unpacker.getDataWithLengthPrefix()
# consoles[][agentId].addItem(logType, message, timestamp)
# proc receiveEventlogItem*(message: Message, eventlog: ptr EventlogComponent) =
# var unpacker = Unpacker.init(message.data)
# discard unpacker.getUint8()
# let
# logType = cast[LogType](unpacker.getUint8())
# timestamp = cast[int64](unpacker.getUint32())
# message = unpacker.getDataWithLengthPrefix()
# eventlog[].addItem(logType, message, timestamp)

55
src/client/event/send.nim Normal file
View File

@@ -0,0 +1,55 @@
import whisky
import times, tables
import ../views/[sessions, listeners, console, eventlog]
import ../../common/[types, utils, serialize, event]
export sendHeartbeat
#[
Client -> Server
]#
proc sendStartListener*(ws: WebSocket, listener: UIListener) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_LISTENER_START))
packer.add(string.toUUid(listener.listenerId))
packer.addDataWithLengthPrefix(string.toBytes(listener.address))
packer.add(cast[uint16](listener.port))
packer.add(cast[uint8](listener.protocol))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc sendStopListener*(ws: WebSocket, listenerId: string) =
discard
# var packer = Packer.init()
# packer.add(cast[uint8](CLIENT_LISTENER_STOP))
# packer.add(string.toUuid(listenerId))
# let data = packer.pack()
# ws.send(Bytes.toString(data), BinaryMessage)
# 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)

View File

@@ -1,157 +0,0 @@
[Window][Sessions [Table View]]
Pos=10,43
Size=1310,279
Collapsed=0
DockId=0x00000003,0
[Window][Listeners]
Pos=10,324
Size=1888,665
Collapsed=0
DockId=0x00000006,0
[Window][Eventlog]
Pos=1322,43
Size=576,279
Collapsed=0
DockId=0x00000004,0
[Window][Dear ImGui Demo]
Pos=1322,43
Size=576,279
Collapsed=0
DockId=0x00000004,1
[Window][Dockspace]
Pos=0,0
Size=1908,999
Collapsed=0
[Window][[FACEDEAD] bob@LAPTOP-02]
Pos=10,395
Size=1888,594
Collapsed=0
DockId=0x00000006,1
[Window][[C9D8E7F6] charlie@SERVER-03]
Pos=10,324
Size=1888,665
Collapsed=0
DockId=0x00000006,1
[Window][Debug##Default]
Pos=60,60
Size=400,400
Collapsed=0
[Window][[G1H2I3J5] diana@WORKSTATION-04]
Pos=10,125
Size=784,665
Collapsed=0
DockId=0x00000006,1
[Window][[DEADBEEF] alice@DESKTOP-01]
Pos=10,324
Size=1888,665
Collapsed=0
DockId=0x00000006,1
[Window][Example: Console]
Pos=10,661
Size=2848,1024
Collapsed=0
DockId=0x00000006,2
[Window][Example: Assets Browser]
Pos=60,60
Size=800,480
Collapsed=0
[Window][Example: Documents]
Pos=186,108
Size=997,993
Collapsed=0
[Window][Example: Log]
Pos=119,266
Size=1717,576
Collapsed=0
[Window][Same title as another window##1]
Pos=274,278
Size=754,103
Collapsed=1
[Window][Same title as another window##2]
Pos=100,200
Size=754,103
Collapsed=0
DockId=0x00000009,1
[Window][###AnimatedTitle]
Pos=100,200
Size=754,103
Collapsed=0
DockId=0x00000009,0
[Window][Delete?]
Pos=696,412
Size=516,175
Collapsed=0
[Window][Stacked 1]
Pos=588,335
Size=669,457
Collapsed=0
[Window][StartListener]
Pos=753,446
Size=76,76
Collapsed=0
[Window][Start Listener]
Pos=704,387
Size=500,225
Collapsed=0
[Window][Dear ImGui Demo/0_E1BADA21]
IsChild=1
Size=1363,540
[Window][Generate Payload]
Pos=704,161
Size=500,677
Collapsed=0
[Window][Generate Payload/0_B6B17D5F]
IsChild=1
Size=217,310
[Table][0x32886A44,8]
Column 0 Weight=0.6432
Column 1 Weight=0.9647
Column 2 Weight=0.6694
Column 3 Weight=1.0960
Column 4 Weight=1.5816
Column 5 Weight=1.1551
Column 6 Weight=0.4331
Column 7 Weight=1.4570
[Table][0xB6880529,2]
RefScale=27
Column 0 Sort=0v
[Table][0x064A67CC,4]
Column 0 Weight=1.2081
Column 1 Weight=1.3299
Column 2 Weight=0.4873
Column 3 Weight=0.9746
[Docking][Data]
DockNode ID=0x00000009 Pos=100,200 Size=754,103 Selected=0x64D005CF
DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=1888,946 Split=Y
DockNode ID=0x00000005 Parent=0x85940918 SizeRef=1888,279 Split=X
DockNode ID=0x00000003 Parent=0x00000005 SizeRef=1310,159 CentralNode=1 Selected=0x61E02D75
DockNode ID=0x00000004 Parent=0x00000005 SizeRef=576,159 Selected=0x5E5F7166
DockNode ID=0x00000006 Parent=0x85940918 SizeRef=1888,665 Selected=0x6BE22050

View File

@@ -1,19 +1,21 @@
import whisky import whisky
import tables, strutils import tables, strutils, json, parsetoml
import ./utils/appImGui import ./utils/appImGui
import ./views/[dockspace, sessions, listeners, eventlog, console] import ./views/[dockspace, sessions, listeners, eventlog, console]
import ../common/[types, utils] import ../common/[types, utils]
import ./websocket import ./event/[send, recv]
import sugar
proc main() = proc main() =
var app = createApp(1024, 800, imnodes = true, title = "Conquest", docking = true) var app = createApp(1024, 800, imnodes = true, title = "Conquest", docking = true)
defer: app.destroyApp() defer: app.destroyApp()
var var
profile: Profile
views: Table[string, ptr bool] views: Table[string, ptr bool]
showConquest = true showConquest = true
showSessionsTable = true showSessionsTable = true
showSessionsGraph = false
showListeners = true showListeners = true
showEventlog = true showEventlog = true
consoles: Table[string, ConsoleComponent] consoles: Table[string, ConsoleComponent]
@@ -25,7 +27,6 @@ proc main() =
dockTopRight: ImGuiID = 0 dockTopRight: ImGuiID = 0
views["Sessions [Table View]"] = addr showSessionsTable views["Sessions [Table View]"] = addr showSessionsTable
views["Sessions [Graph View]"] = addr showSessionsGraph
views["Listeners"] = addr showListeners views["Listeners"] = addr showListeners
views["Eventlog"] = addr showEventlog views["Eventlog"] = addr showEventlog
@@ -58,12 +59,34 @@ proc main() =
ws.sendHeartbeat() ws.sendHeartbeat()
# Receive and parse websocket response message # Receive and parse websocket response message
let message = ws.receiveMessage().get() let event = recvEvent(ws.receiveMessage().get())
case message.getMessageType() case event.eventType:
of CLIENT_EVENT_LOG: of CLIENT_PROFILE:
message.receiveEventlogItem(addr eventlog) profile = parseString(event.data["profile"].getStr())
else: discard
of CLIENT_LISTENER_ADD:
let listener = event.data.to(UIListener)
dump listener.listenerId
listenersTable.listeners.add(listener)
of CLIENT_AGENT_ADD:
let agent = event.data.to(UIAgent)
dump agent.agentId
sessionsTable.agents.add(agent)
of CLIENT_AGENT_CHECKIN:
discard
of CLIENT_AGENT_PAYLOAD:
discard
of CLIENT_CONSOLE_ITEM:
consoles[event.data["agentId"].getStr()].addItem(cast[LogType](event.data["logType"].getInt()), event.data["message"].getStr(), event.timestamp)
of CLIENT_EVENTLOG_ITEM:
eventlog.addItem(cast[LogType](event.data["logType"].getInt()), event.data["message"].getStr(), event.timestamp)
else: discard
# Draw/update UI components/views # Draw/update UI components/views
dockspace.draw(addr showConquest, views, addr dockTop, addr dockBottom, addr dockTopLeft, addr dockTopRight) dockspace.draw(addr showConquest, views, addr dockTop, addr dockBottom, addr dockTopLeft, addr dockTopRight)

View File

@@ -7,7 +7,7 @@ import ../../common/[types]
const MAX_INPUT_LENGTH = 512 const MAX_INPUT_LENGTH = 512
type type
ConsoleComponent* = ref object of RootObj ConsoleComponent* = ref object of RootObj
agent*: Agent agent*: UIAgent
showConsole*: bool showConsole*: bool
inputBuffer: array[MAX_INPUT_LENGTH, char] inputBuffer: array[MAX_INPUT_LENGTH, char]
console*: ConsoleItems console*: ConsoleItems
@@ -42,7 +42,7 @@ proc getLineAtIndex(i: csize_t, data: pointer, outLen: ptr csize_t): cstring {.c
outLen[] = line.len.csize_t outLen[] = line.len.csize_t
return line return line
proc Console*(agent: Agent): ConsoleComponent = proc Console*(agent: UIAgent): ConsoleComponent =
result = new ConsoleComponent result = new ConsoleComponent
result.agent = agent result.agent = agent
result.showConsole = true result.showConsole = true

View File

@@ -3,41 +3,25 @@ import imguin/[cimgui, glfw_opengl, simple]
import ../utils/appImGui import ../utils/appImGui
import ../../common/[types, utils] import ../../common/[types, utils]
import ./modals/[startListener, generatePayload] import ./modals/[startListener, generatePayload]
import ../websocket import ../event/send
import whisky import whisky
type type
ListenersTableComponent* = ref object of RootObj ListenersTableComponent* = ref object of RootObj
title: string title: string
listeners*: seq[Listener] listeners*: seq[UIListener]
selection: ptr ImGuiSelectionBasicStorage selection: ptr ImGuiSelectionBasicStorage
startListenerModal: ListenerModalComponent startListenerModal: ListenerModalComponent
generatePayloadModal: AgentModalComponent generatePayloadModal: AgentModalComponent
let exampleListeners: seq[Listener] = @[
Listener(
listenerId: "L1234567",
address: "192.168.1.1",
port: 8080,
protocol: HTTP
),
Listener(
listenerId: "L7654321",
address: "10.0.0.2",
port: 443,
protocol: HTTP
)
]
proc ListenersTable*(title: string): ListenersTableComponent = proc ListenersTable*(title: string): ListenersTableComponent =
result = new ListenersTableComponent result = new ListenersTableComponent
result.title = title result.title = title
result.listeners = exampleListeners result.listeners = @[]
result.selection = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage() result.selection = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage()
result.startListenerModal = ListenerModal() result.startListenerModal = ListenerModal()
result.generatePayloadModal = AgentModal() result.generatePayloadModal = AgentModal()
proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebSocket) = proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebSocket) =
igBegin(component.title, showComponent, 0) igBegin(component.title, showComponent, 0)
defer: igEnd() defer: igEnd()
@@ -73,7 +57,7 @@ proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebS
ImGuiTableFlags_ScrollY.int32 or ImGuiTableFlags_ScrollY.int32 or
ImGuiTableFlags_ScrollX.int32 or ImGuiTableFlags_ScrollX.int32 or
ImGuiTableFlags_NoBordersInBodyUntilResize.int32 or ImGuiTableFlags_NoBordersInBodyUntilResize.int32 or
ImGui_TableFlags_SizingStretchProp.int32 ImGui_TableFlags_SizingStretchSame.int32
) )
let cols: int32 = 4 let cols: int32 = 4
@@ -114,7 +98,7 @@ proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebS
if igMenuItem("Stop", nil, false, true): if igMenuItem("Stop", nil, false, true):
# Update agents table with only non-selected ones # Update agents table with only non-selected ones
var newListeners: seq[Listener] = @[] var newListeners: seq[UIListener] = @[]
for i in 0 ..< component.listeners.len(): for i in 0 ..< component.listeners.len():
if not ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](i)): if not ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](i)):
newListeners.add(component.listeners[i]) newListeners.add(component.listeners[i])

View File

@@ -39,7 +39,7 @@ proc resetModalValues(component: AgentModalComponent) =
component.spoofStack = false component.spoofStack = false
component.moduleSelection.reset() component.moduleSelection.reset()
proc draw*(component: AgentModalComponent, listeners: seq[Listener]) = proc draw*(component: AgentModalComponent, listeners: seq[UIListener]) =
let textSpacing = igGetStyle().ItemSpacing.x let textSpacing = igGetStyle().ItemSpacing.x

View File

@@ -25,7 +25,7 @@ proc resetModalValues(component: ListenerModalComponent) =
component.port = DEFAULT_PORT component.port = DEFAULT_PORT
component.protocol = 0 component.protocol = 0
proc draw*(component: ListenerModalComponent): Listener = proc draw*(component: ListenerModalComponent): UIListener =
let textSpacing = igGetStyle().ItemSpacing.x let textSpacing = igGetStyle().ItemSpacing.x
# Center modal # Center modal
@@ -76,7 +76,7 @@ proc draw*(component: ListenerModalComponent): Listener =
if igButton("Start", vec2(availableSize.x * 0.5 - textSpacing * 0.5, 0.0f)): if igButton("Start", vec2(availableSize.x * 0.5 - textSpacing * 0.5, 0.0f)):
result = Listener( result = UIListener(
listenerId: generateUUID(), listenerId: generateUUID(),
address: $(addr component.address[0]), address: $(addr component.address[0]),
port: int(component.port), port: int(component.port),

View File

@@ -8,85 +8,14 @@ import ../../common/[types, utils]
type type
SessionsTableComponent* = ref object of RootObj SessionsTableComponent* = ref object of RootObj
title: string title: string
agents*: seq[Agent] agents*: seq[UIAgent]
selection: ptr ImGuiSelectionBasicStorage selection: ptr ImGuiSelectionBasicStorage
consoles: ptr Table[string, ConsoleComponent] consoles: ptr Table[string, ConsoleComponent]
let exampleAgents: seq[Agent] = @[
Agent(
agentId: "DEADBEEF",
listenerId: "L1234567",
username: "alice",
hostname: "DESKTOP-01",
domain: "corp.local",
ip: "192.168.1.10",
os: "Windows 10",
process: "explorer.exe",
pid: 2340,
elevated: true,
sleep: 60,
tasks: @[],
firstCheckin: now() - initDuration(hours = 2),
latestCheckin: now(),
sessionKey: [byte 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
),
Agent(
agentId: "FACEDEAD",
listenerId: "L7654321",
username: "bob",
hostname: "LAPTOP-02",
domain: "corp.local",
ip: "10.0.0.5",
os: "Windows 11",
process: "cmd.exe",
pid: 4567,
elevated: false,
sleep: 120,
tasks: @[],
firstCheckin: now() - initDuration(hours = 1, minutes = 30),
latestCheckin: now() - initDuration(minutes = 5),
sessionKey: [byte 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
),
Agent(
agentId: "C9D8E7F6",
listenerId: "L2468135",
username: "charlie",
hostname: "SERVER-03",
domain: "child.corp.local",
ip: "172.16.0.20",
os: "Windows Server 2019",
process: "powershell.exe",
pid: 7890,
elevated: true,
sleep: 30,
tasks: @[],
firstCheckin: now() - initDuration(hours = 3, minutes = 15),
latestCheckin: now() - initDuration(minutes = 10),
sessionKey: [byte 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
),
Agent(
agentId: "G1H2I3J5",
listenerId: "L1357924",
username: "diana",
hostname: "WORKSTATION-04",
domain: "external.local",
ip: "192.168.2.15",
os: "Windows 10",
process: "chrome.exe",
pid: 3210,
elevated: false,
sleep: 90,
tasks: @[],
firstCheckin: now() - initDuration(hours = 4),
latestCheckin: now() - initDuration(minutes = 2),
sessionKey: [byte 5, 4, 3, 2, 1, 0, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6]
)
]
proc SessionsTable*(title: string, consoles: ptr Table[string, ConsoleComponent]): SessionsTableComponent = proc SessionsTable*(title: string, consoles: ptr Table[string, ConsoleComponent]): SessionsTableComponent =
result = new SessionsTableComponent result = new SessionsTableComponent
result.title = title result.title = title
result.agents = exampleAgents result.agents = @[]
result.selection = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage() result.selection = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage()
result.consoles = consoles result.consoles = consoles
@@ -97,6 +26,7 @@ proc interact(component: SessionsTableComponent) =
while ImGuiSelectionBasicStorage_GetNextSelectedItem(component.selection, addr it, addr row): while ImGuiSelectionBasicStorage_GetNextSelectedItem(component.selection, addr it, addr row):
let agent = component.agents[cast[int](row)] let agent = component.agents[cast[int](row)]
# Create a new console window # Create a new console window
if not component.consoles[].hasKey(agent.agentId): if not component.consoles[].hasKey(agent.agentId):
component.consoles[][agent.agentId] = Console(agent) component.consoles[][agent.agentId] = Console(agent)
@@ -104,7 +34,9 @@ proc interact(component: SessionsTableComponent) =
# Focus the existing console window # Focus the existing console window
else: else:
igSetWindowFocus_Str(fmt"[{agent.agentId}] {agent.username}@{agent.hostname}") igSetWindowFocus_Str(fmt"[{agent.agentId}] {agent.username}@{agent.hostname}")
# TODO: Clear selection properly
ImGuiSelectionBasicStorage_Clear(component.selection)
proc draw*(component: SessionsTableComponent, showComponent: ptr bool) = proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
igBegin(component.title, showComponent, 0) igBegin(component.title, showComponent, 0)
@@ -121,7 +53,7 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
ImGuiTableFlags_ScrollY.int32 or ImGuiTableFlags_ScrollY.int32 or
ImGuiTableFlags_ScrollX.int32 or ImGuiTableFlags_ScrollX.int32 or
ImGuiTableFlags_NoBordersInBodyUntilResize.int32 or ImGuiTableFlags_NoBordersInBodyUntilResize.int32 or
ImGui_TableFlags_SizingStretchProp.int32 ImGui_TableFlags_SizingStretchSame.int32
) )
let cols: int32 = 8 let cols: int32 = 8
@@ -134,7 +66,7 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
igTableSetupColumn("OS", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("OS", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Process", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Process", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("PID", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("PID", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Activity", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Last seen", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupScrollFreeze(0, 1) igTableSetupScrollFreeze(0, 1)
igTableHeadersRow() igTableHeadersRow()
@@ -170,7 +102,17 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
if igTableSetColumnIndex(6): if igTableSetColumnIndex(6):
igText($agent.pid) igText($agent.pid)
if igTableSetColumnIndex(7): if igTableSetColumnIndex(7):
igText(agent.latestCheckin.format("yyyy-MM-dd HH:mm:ss")) let duration = now() - agent.latestCheckin.fromUnix().utc()
let totalSeconds = duration.inSeconds
let hours = totalSeconds div 3600
let minutes = (totalSeconds mod 3600) div 60
let seconds = totalSeconds mod 60
let dummyTime = dateTime(2000, mJan, 1, hours.int, minutes.int, seconds.int)
let timeText = dummyTime.format("HH:mm:ss")
igText(fmt"{timeText} ago")
# Handle right-click context menu # Handle right-click context menu
# Right-clicking the table header to hide/show columns or reset the layout is only possible when no sessions are selected # Right-clicking the table header to hide/show columns or reset the layout is only possible when no sessions are selected
@@ -182,7 +124,7 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
if igMenuItem("Remove", nil, false, true): if igMenuItem("Remove", nil, false, true):
# Update agents table with only non-selected ones # Update agents table with only non-selected ones
var newAgents: seq[Agent] = @[] var newAgents: seq[UIAgent] = @[]
for i in 0 ..< component.agents.len(): for i in 0 ..< component.agents.len():
if not ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](i)): if not ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](i)):
newAgents.add(component.agents[i]) newAgents.add(component.agents[i])

View File

@@ -1,135 +0,0 @@
import times, tables
import ../common/[types, utils, serialize]
import views/[sessions, listeners, console, eventlog]
import whisky
#[
[ Sending Functions ]
Client -> Server
- Heartbeat
- ListenerStart
- ListenerStop
- AgentBuild
- AgentCommand
]#
proc sendHeartbeat*(ws: WebSocket) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_HEARTBEAT))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc sendStartListener*(ws: WebSocket, listener: Listener) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_LISTENER_START))
packer.add(string.toUUid(listener.listenerId))
packer.addDataWithLengthPrefix(string.toBytes(listener.address))
packer.add(cast[uint16](listener.port))
packer.add(cast[uint8](listener.protocol))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc sendStopListener*(ws: WebSocket, listenerId: string) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_LISTENER_STOP))
packer.add(string.toUuid(listenerId))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
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)
#[
[ Retrieval Functions ]
Server -> Client
]#
proc getMessageType*(message: Message): WsPacketType =
var unpacker = Unpacker.init(message.data)
return cast[WsPacketType](unpacker.getUint8())
proc receiveAgentPayload*(message: Message): seq[byte] =
var unpacker = Unpacker.init(message.data)
discard unpacker.getUint8()
return string.toBytes(unpacker.getDataWithLengthPrefix())
proc receiveAgentConnection*(message: Message, sessions: ptr SessionsTableComponent) =
var unpacker = Unpacker.init(message.data)
discard unpacker.getUint8()
let agent = Agent(
agentId: Uuid.toString(unpacker.getUint32()),
listenerId: Uuid.toString(unpacker.getUint32()),
username: unpacker.getDataWithLengthPrefix(),
hostname: unpacker.getDataWithLengthPrefix(),
domain: unpacker.getDataWithLengthPrefix(),
ip: unpacker.getDataWithLengthPrefix(),
os: unpacker.getDataWithLengthPrefix(),
process: unpacker.getDataWithLengthPrefix(),
pid: int(unpacker.getUint32()),
elevated: unpacker.getUint8() != 0,
sleep: int(unpacker.getUint32()),
tasks: @[],
firstCheckin: cast[int64](unpacker.getUint32()).fromUnix().utc(),
latestCheckin: cast[int64](unpacker.getUint32()).fromUnix().utc(),
)
sessions.agents.add(agent)
proc receiveAgentCheckin*(message: Message, sessions: ptr SessionsTableComponent)=
var unpacker = Unpacker.init(message.data)
discard unpacker.getUint8()
let agentId = Uuid.toString(unpacker.getUint32())
let timestamp = cast[int64](unpacker.getUint32())
# TODO: Update checkin
proc receiveConsoleItem*(message: Message, consoles: ptr Table[string, ConsoleComponent]) =
var unpacker = Unpacker.init(message.data)
discard unpacker.getUint8()
let
agentId = Uuid.toString(unpacker.getUint32())
logType = cast[LogType](unpacker.getUint8())
timestamp = cast[int64](unpacker.getUint32())
message = unpacker.getDataWithLengthPrefix()
consoles[][agentId].addItem(logType, message, timestamp)
proc receiveEventlogItem*(message: Message, eventlog: ptr EventlogComponent) =
var unpacker = Unpacker.init(message.data)
discard unpacker.getUint8()
let
logType = cast[LogType](unpacker.getUint8())
timestamp = cast[int64](unpacker.getUint32())
message = unpacker.getDataWithLengthPrefix()
eventlog[].addItem(logType, message, timestamp)

34
src/common/event.nim Normal file
View File

@@ -0,0 +1,34 @@
when defined(server):
import mummy
when defined(client):
import whisky
import times, json
import ./[types, utils, serialize]
proc sendEvent*(ws: WebSocket, event: Event) =
var packer = Packer.init()
packer.add(cast[uint8](event.eventType))
packer.add(cast[uint32](event.timestamp))
packer.addDataWithLengthPrefix(string.toBytes($event.data))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc recvEvent*(message: Message): Event =
var unpacker = Unpacker.init(message.data)
return Event(
eventType: cast[EventType](unpacker.getUint8()),
timestamp: cast[int64](unpacker.getUint32()),
data: parseJson(unpacker.getDataWithLengthPrefix())
)
proc sendHeartbeat*(ws: WebSocket) =
let event = Event(
eventType: CLIENT_HEARTBEAT,
timestamp: now().toTime().toUnix(),
data: %*{}
)
ws.sendEvent(event)

View File

@@ -1,7 +1,7 @@
import prompt import prompt
import tables import tables
import times import times
import parsetoml import parsetoml, json
import mummy import mummy
# Custom Binary Task structure # Custom Binary Task structure
@@ -202,6 +202,22 @@ type
latestCheckin*: DateTime latestCheckin*: DateTime
sessionKey*: Key sessionKey*: Key
# Session entry for client UI
UIAgent* = ref object
agentId*: string
listenerId*: string
username*: string
hostname*: string
domain*: string
ip*: string
os*: string
process*: string
pid*: int
elevated*: bool
sleep*: int
firstCheckin*: int64
latestCheckin*: int64
# Listener structure # Listener structure
type type
Protocol* {.size: sizeof(uint8).} = enum Protocol* {.size: sizeof(uint8).} = enum
@@ -214,6 +230,12 @@ type
port*: int port*: int
protocol*: Protocol protocol*: Protocol
UIListener* = ref object of RootObj
listenerId*: string
address*: string
port*: int
protocol*: Protocol
# Context structures # Context structures
type type
KeyPair* = object KeyPair* = object
@@ -225,7 +247,8 @@ type
Conquest* = ref object Conquest* = ref object
prompt*: Prompt prompt*: Prompt
dbPath*: string dbPath*: string
listeners*: Table[string, tuple[listener: Listener, thread: Thread[Listener]]] listeners*: Table[string, Listener]
threads*: Table[string, Thread[Listener]]
agents*: Table[string, Agent] agents*: Table[string, Agent]
interactAgent*: Agent interactAgent*: Agent
keyPair*: KeyPair keyPair*: KeyPair
@@ -280,19 +303,27 @@ type
Client <-> Server WebSocket communication Client <-> Server WebSocket communication
]# ]#
type type
WsPacketType* = enum EventType* = enum
# Sent by client
CLIENT_HEARTBEAT = 0'u8 # Basic checkin CLIENT_HEARTBEAT = 0'u8 # Basic checkin
# Sent by client
CLIENT_AGENT_BUILD = 1'u8 # Generate an agent binary for a specific listener CLIENT_AGENT_BUILD = 1'u8 # Generate an agent binary for a specific listener
CLIENT_AGENT_COMMAND = 2'u8 # Instruct TS to send queue a command for a specific agent CLIENT_AGENT_COMMAND = 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_START = 3'u8 # Start a listener on the TS
CLIENT_LISTENER_STOP = 4'u8 # Stop a listener CLIENT_LISTENER_STOP = 4'u8 # Stop a listener
# Sent by team server # Sent by team server
CLIENT_AGENT_BINARY = 100'u8 # Return the agent binary to write to the operator's client machine CLIENT_PROFILE = 100'u8 # Team server profile and configuration
CLIENT_AGENT_CONNECTION = 101'u8 # Notify new agent connection CLIENT_LISTENER_ADD = 101'u8 # Add listener to listeners table
CLIENT_AGENT_CHECKIN = 102'u8 # Update agent checkin CLIENT_AGENT_ADD = 102'u8 # Add agent to sessions table
CLIENT_CONSOLE_LOG = 103'u8 # Add entry to a agent's console CLIENT_AGENT_CHECKIN = 103'u8 # Update agent checkin
CLIENT_EVENT_LOG = 104'u8 # Add entry to the eventlog CLIENT_AGENT_PAYLOAD = 104'u8 # Return agent payload binary
CLIENT_CONSOLE_ITEM = 105'u8 # Add entry to a agent's console
CLIENT_CONNECTION = 200'u8 # Return team server profile CLIENT_EVENTLOG_ITEM = 106'u8 # Add entry to the eventlog
Event* = object
eventType*: EventType
timestamp*: int64
data*: JsonNode

View File

@@ -4,6 +4,7 @@ import ../globals
import ../db/database import ../db/database
import ../protocol/packer import ../protocol/packer
import ../core/logger import ../core/logger
import ../event/send
import ../../common/[types, utils, serialize] import ../../common/[types, utils, serialize]
#[ #[
@@ -36,6 +37,9 @@ proc register*(registrationData: seq[byte]): bool =
cq.agents[agent.agentId] = agent cq.agents[agent.agentId] = agent
cq.info("Agent ", fgYellow, styleBright, agent.agentId, resetStyle, " connected to listener ", fgGreen, styleBright, agent.listenerId, resetStyle, ": ", fgYellow, styleBright, fmt"{agent.username}@{agent.hostname}", "\n") cq.info("Agent ", fgYellow, styleBright, agent.agentId, resetStyle, " connected to listener ", fgGreen, styleBright, agent.listenerId, resetStyle, ": ", fgYellow, styleBright, fmt"{agent.username}@{agent.hostname}", "\n")
cq.ws.sendAgent(agent)
cq.ws.sendEventlogItem(LOG_INFO_SHORT, fmt"Agent {agent.agentId} connected to listener {agent.listenerId}.")
return true return true
@@ -69,6 +73,7 @@ proc getTasks*(heartbeat: seq[byte]): seq[seq[byte]] =
# 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).fromUnix().local()
# cq.ws.sendAgentCheckin(agentId)
# Return tasks # Return tasks
for task in cq.agents[agentId].tasks.mitems: # Iterate over agents as mutable items in order to modify GMAC tag for task in cq.agents[agentId].tasks.mitems: # Iterate over agents as mutable items in order to modify GMAC tag

View File

@@ -141,7 +141,7 @@ proc agentBuild*(cq: Conquest, listener, sleepDelay: string, sleepTechnique: str
cq.error(fmt"Listener {listener.toUpperAscii} does not exist.") cq.error(fmt"Listener {listener.toUpperAscii} does not exist.")
return false return false
let listener = cq.listeners[listener.toUpperAscii].listener let listener = cq.listeners[listener.toUpperAscii]
var config: seq[byte] var config: seq[byte]
if sleepDelay.isEmptyOrWhitespace(): if sleepDelay.isEmptyOrWhitespace():

View File

@@ -8,7 +8,7 @@ import ../api/routes
import ../db/database import ../db/database
import ../core/logger import ../core/logger
import ../../common/[types, utils, profile] import ../../common/[types, utils, profile]
import ../websocket/send import ../event/send
#[ #[
Listener management Listener management
@@ -79,7 +79,9 @@ proc listenerStart*(cq: Conquest, name: string, host: string, port: int, protoco
createThread(thread, serve, listener) createThread(thread, serve, listener)
server.waitUntilReady() server.waitUntilReady()
cq.listeners[name] = (listener, thread) cq.listeners[name] = listener
cq.threads[name] = thread
if not cq.dbStoreListener(listener): if not cq.dbStoreListener(listener):
raise newException(CatchableError, "Failed to store listener in database.") raise newException(CatchableError, "Failed to store listener in database.")
@@ -97,7 +99,6 @@ proc restartListeners*(cq: Conquest) =
for listener in listeners: for listener in listeners:
try: try:
# Create new listener # Create new listener
let name: string = generateUUID()
var router: Router var router: Router
router.notFoundHandler = routes.error404 router.notFoundHandler = routes.error404
router.methodNotAllowedHandler = routes.error405 router.methodNotAllowedHandler = routes.error405
@@ -128,9 +129,11 @@ proc restartListeners*(cq: Conquest) =
createThread(thread, serve, listener) createThread(thread, serve, listener)
server.waitUntilReady() server.waitUntilReady()
cq.listeners[listener.listenerId] = (listener, thread) cq.listeners[listener.listenerId] = listener
cq.threads[listener.listenerId] = thread
cq.success("Restarted listener", fgGreen, fmt" {listener.listenerId} ", resetStyle, fmt"on {listener.address}:{$listener.port}.") cq.success("Restarted listener", fgGreen, fmt" {listener.listenerId} ", resetStyle, fmt"on {listener.address}:{$listener.port}.")
except CatchableError as err: except CatchableError as err:
cq.error("Failed to restart listener: ", err.msg) cq.error("Failed to restart listener: ", err.msg)

View File

@@ -5,8 +5,8 @@ import ./[agent, listener, builder]
import ../globals import ../globals
import ../db/database import ../db/database
import ../core/logger import ../core/logger
import ../../common/[types, crypto, profile] import ../../common/[types, crypto, utils, profile]
import ../websocket/[receive, send] import ../event/[recv, send]
import mummy, mummy/routers import mummy, mummy/routers
#[ #[
@@ -88,10 +88,10 @@ proc handleConsoleCommand(cq: Conquest, args: string) =
of "list": of "list":
cq.listenerList() cq.listenerList()
of "start": of "start":
#cq.listenerStart(opts.listener.get.start.get.ip, opts.listener.get.start.get.port) cq.listenerStart(generateUUID(), opts.listener.get.start.get.ip, parseInt(opts.listener.get.start.get.port), HTTP)
discard discard
of "stop": of "stop":
#cq.listenerStop(opts.listener.get.stop.get.name) cq.listenerStop(opts.listener.get.stop.get.name)
discard discard
else: else:
cq.listenerUsage() cq.listenerUsage()
@@ -133,7 +133,8 @@ proc header() =
proc init*(T: type Conquest, profile: Profile): Conquest = proc init*(T: type Conquest, profile: Profile): Conquest =
var cq = new Conquest var cq = new Conquest
cq.prompt = Prompt.init() cq.prompt = Prompt.init()
cq.listeners = initTable[string, tuple[listener: Listener, thread: Thread[Listener]]]() cq.listeners = initTable[string, Listener]()
cq.threads = initTable[string, Thread[Listener]]()
cq.agents = initTable[string, Agent]() cq.agents = initTable[string, Agent]()
cq.interactAgent = nil cq.interactAgent = nil
cq.profile = profile cq.profile = profile
@@ -148,27 +149,36 @@ proc upgradeHandler(request: Request) =
{.cast(gcsafe).}: {.cast(gcsafe).}:
let ws = request.upgradeToWebSocket() let ws = request.upgradeToWebSocket()
cq.ws = ws cq.ws = ws
# Send client connection message
ws.sendEventlogItem(LOG_SUCCESS_SHORT, "CQ-V1")
proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {.gcsafe.} = proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {.gcsafe.} =
{.cast(gcsafe).}: {.cast(gcsafe).}:
case event: case event:
of OpenEvent: of OpenEvent:
discard # New client connected to team server
# Send profile, sessions and listeners to the UI client
ws.sendProfile(cq.profile)
for id, listener in cq.listeners:
ws.sendListener(listener)
for id, agent in cq.agents:
ws.sendAgent(agent)
ws.sendEventlogItem(LOG_SUCCESS_SHORT, "CQ-V1")
of MessageEvent: of MessageEvent:
# Continuously send heartbeat messages
ws.sendHeartbeat() ws.sendHeartbeat()
case message.getMessageType(): # case message.getMessageType():
of CLIENT_AGENT_COMMAND: # of CLIENT_AGENT_COMMAND:
discard # discard
of CLIENT_LISTENER_START: # of CLIENT_LISTENER_START:
message.receiveStartListener() # message.receiveStartListener()
of CLIENT_LISTENER_STOP: # of CLIENT_LISTENER_STOP:
message.receiveStopListener() # discard
of CLIENT_AGENT_BUILD: # # message.receiveStopListener()
discard # of CLIENT_AGENT_BUILD:
else: discard # discard
# else: discard
of ErrorEvent: of ErrorEvent:
discard discard
of CloseEvent: of CloseEvent:

40
src/server/event/recv.nim Normal file
View File

@@ -0,0 +1,40 @@
import mummy
import times, tables, json
import ./send
import ../globals
import ../core/[task, listener]
import ../../common/[types, utils, serialize, event]
#[
Client -> Server
]#
# proc getMessageType*(message: Message): EventType =
# var unpacker = Unpacker.init(message.data)
# return cast[EventType](unpacker.getUint8())
# proc receiveStartListener*(message: Message) =
# var unpacker = Unpacker.init(message.data)
# discard unpacker.getUint8()
# let
# listenerId = Uuid.toString(unpacker.getUint32())
# address = unpacker.getDataWithLengthPrefix()
# port = int(unpacker.getUint16())
# protocol = cast[Protocol](unpacker.getUint8())
# cq.listenerStart(listenerId, address, port, protocol)
# proc receiveStopListener*(message: Message) =
# var unpacker = Unpacker.init(message.data)
# discard unpacker.getUint8()
# let listenerId = Uuid.toString(unpacker.getUint32())
# cq.listenerStop(listenerId)
# proc receiveAgentCommand*(message: Message) =
# var unpacker = Unpacker.init(message.data)
# discard unpacker.getUint8()
# let
# agentId = Uuid.toString(unpacker.getUint32())
# command = unpacker.getDataWithLengthPrefix()

78
src/server/event/send.nim Normal file
View File

@@ -0,0 +1,78 @@
import mummy
import times, tables, json, base64, parsetoml
import ../utils
import ../../common/[types, utils, serialize, event]
export sendHeartbeat
#[
Server -> Client
]#
proc sendProfile*(ws: WebSocket, profile: Profile) =
let event = Event(
eventType: CLIENT_PROFILE,
timestamp: now().toTime().toUnix(),
data: %*{
"profile": profile.toTomlString()
}
)
ws.sendEvent(event)
proc sendEventlogItem*(ws: WebSocket, logType: LogType, message: string) =
let event = Event(
eventType: CLIENT_EVENTLOG_ITEM,
timestamp: now().toTime().toUnix(),
data: %*{
"logType": cast[uint8](logType),
"message": message
}
)
ws.sendEvent(event)
proc sendAgent*(ws: WebSocket, agent: Agent) =
let event = Event(
eventType: CLIENT_AGENT_ADD,
timestamp: now().toTime().toUnix(),
data: %agent
)
ws.sendEvent(event)
proc sendListener*(ws: WebSocket, listener: Listener) =
let event = Event(
eventType: CLIENT_LISTENER_ADD,
timestamp: now().toTime().toUnix(),
data: %listener
)
ws.sendEvent(event)
proc sendAgentCheckin*(ws: WebSocket, agentId: string) =
let event = Event(
eventType: CLIENT_AGENT_CHECKIN,
timestamp: now().toTime().toUnix(),
data: %*{
"agentId": agentId
}
)
ws.sendEvent(event)
proc sendAgentPayload*(ws: WebSocket, agentId: string, bytes: seq[byte]) =
let event = Event(
eventType: CLIENT_AGENT_PAYLOAD,
timestamp: now().toTime().toUnix(),
data: %*{
"agentId": agentId,
"payload": encode(bytes)
}
)
ws.sendEvent(event)
proc sendConsoleItem*(ws: WebSocket, agentId: string, logType: LogType, message: string) =
let event = Event(
eventType: CLIENT_CONSOLE_ITEM,
timestamp: now().toTime().toUnix(),
data: %*{
"agentId": agentId,
"logType": cast[uint8](logType),
"message": message
}
)
ws.sendEvent(event)

View File

@@ -1,15 +1,31 @@
import strutils, terminal, tables, sequtils, times, strformat, prompt import strutils, terminal, tables, sequtils, times, strformat, prompt, json
import std/wordwrap import std/wordwrap
import ../common/types import ../common/types
import core/logger import core/logger
proc validatePort*(portStr: string): bool = proc `%`*(agent: Agent): JsonNode =
try: result = newJObject()
let port: int = portStr.parseInt result["agentId"] = %agent.agentId
return port >= 1 and port <= 65535 result["listenerId"] = %agent.listenerId
except ValueError: result["username"] = %agent.username
return false result["hostname"] = %agent.hostname
result["domain"] = %agent.domain
result["ip"] = %agent.ip
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()
proc `%`*(listener: Listener): JsonNode =
result = newJObject()
result["listenerId"] = %listener.listenerId
result["address"] = %listener.address
result["port"] = %listener.port
result["protocol"] = %listener.protocol
# Table border characters # Table border characters
type type

View File

@@ -1,42 +0,0 @@
import times, tables
import ../globals
import ../../common/[types, utils, serialize]
import mummy
import ./send
import ../core/[task, listener]
#[
[ Retrieval functions ]
Client -> Server
]#
proc getMessageType*(message: Message): WsPacketType =
var unpacker = Unpacker.init(message.data)
return cast[WsPacketType](unpacker.getUint8())
proc receiveStartListener*(message: Message) =
var unpacker = Unpacker.init(message.data)
discard unpacker.getUint8()
let
listenerId = Uuid.toString(unpacker.getUint32())
address = unpacker.getDataWithLengthPrefix()
port = int(unpacker.getUint16())
protocol = cast[Protocol](unpacker.getUint8())
cq.ws.sendEventlogItem(LOG_INFO_SHORT, "Attempting to start listener.")
cq.listenerStart(listenerId, address, port, protocol)
proc receiveStopListener*(message: Message) =
var unpacker = Unpacker.init(message.data)
discard unpacker.getUint8()
let listenerId = Uuid.toString(unpacker.getUint32())
cq.listenerStop(listenerId)
proc receiveAgentCommand*(message: Message) =
var unpacker = Unpacker.init(message.data)
discard unpacker.getUint8()
let
agentId = Uuid.toString(unpacker.getUint32())
command = unpacker.getDataWithLengthPrefix()

View File

@@ -1,79 +0,0 @@
import times, tables
import ../../common/[types, utils, serialize]
import mummy
#[
[ Sending functions ]
Server -> Client
]#
proc sendHeartbeat*(ws: WebSocket) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_HEARTBEAT))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc sendEventlogItem*(ws: WebSocket, logType: LogType, message: string, timestamp: int64 = now().toTime().toUnix()) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_EVENT_LOG))
packer.add(cast[uint8](logType))
packer.add(cast[uint32](timestamp))
packer.addDataWithLengthPrefix(string.toBytes(message))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc sendConsoleItem*(ws: WebSocket, agentId: string, logType: LogType, message: string, timestamp: int64 = now().toTime().toUnix()) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_CONSOLE_LOG))
packer.add(string.toUUid(agentId))
packer.add(cast[uint8](logType))
packer.add(cast[uint32](timestamp))
packer.addDataWithLengthPrefix(string.toBytes(message))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc sendAgentCheckin*(ws: WebSocket, agentId: string, timestamp: int64) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_AGENT_CHECKIN))
packer.add(string.toUUid(agentId))
packer.add(cast[uint32](timestamp))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc sendAgentPayload*(ws: WebSocket, payload: seq[byte]) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_AGENT_BINARY))
packer.addDataWithLengthPrefix(payload)
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)
proc sendAgentConnection*(ws: WebSocket, agent: Agent) =
var packer = Packer.init()
packer.add(cast[uint8](CLIENT_AGENT_CONNECTION))
packer.add(string.toUuid(agent.agentId))
packer.add(string.toUuid(agent.listenerId))
packer.addDataWithLengthPrefix(string.toBytes(agent.username))
packer.addDataWithLengthPrefix(string.toBytes(agent.hostname))
packer.addDataWithLengthPrefix(string.toBytes(agent.domain))
packer.addDataWithLengthPrefix(string.toBytes(agent.ip))
packer.addDataWithLengthPrefix(string.toBytes(agent.os))
packer.addDataWithLengthPrefix(string.toBytes(agent.process))
packer.add(uint32(agent.pid))
packer.add(uint8(agent.elevated))
packer.add(uint32(agent.sleep))
packer.add(cast[uint32](agent.firstCheckin))
packer.add(cast[uint32](agent.latestCheckin))
let data = packer.pack()
ws.send(Bytes.toString(data), BinaryMessage)