Started porting over functionality to the ImGui client via websocket communication.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
switch "o", "bin/client"
|
||||
|
||||
switch "d", "client"
|
||||
switch "d", "ImGuiTextSelect"
|
||||
|
||||
# Select compiler
|
||||
|
||||
77
src/client/event/recv.nim
Normal file
77
src/client/event/recv.nim
Normal 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
55
src/client/event/send.nim
Normal 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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import whisky
|
||||
import tables, strutils
|
||||
import tables, strutils, json, parsetoml
|
||||
import ./utils/appImGui
|
||||
import ./views/[dockspace, sessions, listeners, eventlog, console]
|
||||
import ../common/[types, utils]
|
||||
import ./websocket
|
||||
import ./event/[send, recv]
|
||||
|
||||
import sugar
|
||||
|
||||
proc main() =
|
||||
var app = createApp(1024, 800, imnodes = true, title = "Conquest", docking = true)
|
||||
defer: app.destroyApp()
|
||||
|
||||
var
|
||||
profile: Profile
|
||||
views: Table[string, ptr bool]
|
||||
showConquest = true
|
||||
showSessionsTable = true
|
||||
showSessionsGraph = false
|
||||
showListeners = true
|
||||
showEventlog = true
|
||||
consoles: Table[string, ConsoleComponent]
|
||||
@@ -25,7 +27,6 @@ proc main() =
|
||||
dockTopRight: ImGuiID = 0
|
||||
|
||||
views["Sessions [Table View]"] = addr showSessionsTable
|
||||
views["Sessions [Graph View]"] = addr showSessionsGraph
|
||||
views["Listeners"] = addr showListeners
|
||||
views["Eventlog"] = addr showEventlog
|
||||
|
||||
@@ -58,12 +59,34 @@ proc main() =
|
||||
ws.sendHeartbeat()
|
||||
|
||||
# Receive and parse websocket response message
|
||||
let message = ws.receiveMessage().get()
|
||||
case message.getMessageType()
|
||||
of CLIENT_EVENT_LOG:
|
||||
message.receiveEventlogItem(addr eventlog)
|
||||
else: discard
|
||||
let event = recvEvent(ws.receiveMessage().get())
|
||||
case event.eventType:
|
||||
of CLIENT_PROFILE:
|
||||
profile = parseString(event.data["profile"].getStr())
|
||||
|
||||
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
|
||||
dockspace.draw(addr showConquest, views, addr dockTop, addr dockBottom, addr dockTopLeft, addr dockTopRight)
|
||||
|
||||
@@ -7,7 +7,7 @@ import ../../common/[types]
|
||||
const MAX_INPUT_LENGTH = 512
|
||||
type
|
||||
ConsoleComponent* = ref object of RootObj
|
||||
agent*: Agent
|
||||
agent*: UIAgent
|
||||
showConsole*: bool
|
||||
inputBuffer: array[MAX_INPUT_LENGTH, char]
|
||||
console*: ConsoleItems
|
||||
@@ -42,7 +42,7 @@ proc getLineAtIndex(i: csize_t, data: pointer, outLen: ptr csize_t): cstring {.c
|
||||
outLen[] = line.len.csize_t
|
||||
return line
|
||||
|
||||
proc Console*(agent: Agent): ConsoleComponent =
|
||||
proc Console*(agent: UIAgent): ConsoleComponent =
|
||||
result = new ConsoleComponent
|
||||
result.agent = agent
|
||||
result.showConsole = true
|
||||
|
||||
@@ -3,41 +3,25 @@ import imguin/[cimgui, glfw_opengl, simple]
|
||||
import ../utils/appImGui
|
||||
import ../../common/[types, utils]
|
||||
import ./modals/[startListener, generatePayload]
|
||||
import ../websocket
|
||||
import ../event/send
|
||||
import whisky
|
||||
|
||||
type
|
||||
ListenersTableComponent* = ref object of RootObj
|
||||
title: string
|
||||
listeners*: seq[Listener]
|
||||
listeners*: seq[UIListener]
|
||||
selection: ptr ImGuiSelectionBasicStorage
|
||||
startListenerModal: ListenerModalComponent
|
||||
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 =
|
||||
result = new ListenersTableComponent
|
||||
result.title = title
|
||||
result.listeners = exampleListeners
|
||||
result.listeners = @[]
|
||||
result.selection = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage()
|
||||
result.startListenerModal = ListenerModal()
|
||||
result.generatePayloadModal = AgentModal()
|
||||
|
||||
|
||||
proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebSocket) =
|
||||
igBegin(component.title, showComponent, 0)
|
||||
defer: igEnd()
|
||||
@@ -73,7 +57,7 @@ proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebS
|
||||
ImGuiTableFlags_ScrollY.int32 or
|
||||
ImGuiTableFlags_ScrollX.int32 or
|
||||
ImGuiTableFlags_NoBordersInBodyUntilResize.int32 or
|
||||
ImGui_TableFlags_SizingStretchProp.int32
|
||||
ImGui_TableFlags_SizingStretchSame.int32
|
||||
)
|
||||
|
||||
let cols: int32 = 4
|
||||
@@ -114,7 +98,7 @@ proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebS
|
||||
|
||||
if igMenuItem("Stop", nil, false, true):
|
||||
# Update agents table with only non-selected ones
|
||||
var newListeners: seq[Listener] = @[]
|
||||
var newListeners: seq[UIListener] = @[]
|
||||
for i in 0 ..< component.listeners.len():
|
||||
if not ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](i)):
|
||||
newListeners.add(component.listeners[i])
|
||||
|
||||
@@ -39,7 +39,7 @@ proc resetModalValues(component: AgentModalComponent) =
|
||||
component.spoofStack = false
|
||||
component.moduleSelection.reset()
|
||||
|
||||
proc draw*(component: AgentModalComponent, listeners: seq[Listener]) =
|
||||
proc draw*(component: AgentModalComponent, listeners: seq[UIListener]) =
|
||||
|
||||
let textSpacing = igGetStyle().ItemSpacing.x
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ proc resetModalValues(component: ListenerModalComponent) =
|
||||
component.port = DEFAULT_PORT
|
||||
component.protocol = 0
|
||||
|
||||
proc draw*(component: ListenerModalComponent): Listener =
|
||||
proc draw*(component: ListenerModalComponent): UIListener =
|
||||
let textSpacing = igGetStyle().ItemSpacing.x
|
||||
|
||||
# Center modal
|
||||
@@ -76,7 +76,7 @@ proc draw*(component: ListenerModalComponent): Listener =
|
||||
|
||||
if igButton("Start", vec2(availableSize.x * 0.5 - textSpacing * 0.5, 0.0f)):
|
||||
|
||||
result = Listener(
|
||||
result = UIListener(
|
||||
listenerId: generateUUID(),
|
||||
address: $(addr component.address[0]),
|
||||
port: int(component.port),
|
||||
|
||||
@@ -8,85 +8,14 @@ import ../../common/[types, utils]
|
||||
type
|
||||
SessionsTableComponent* = ref object of RootObj
|
||||
title: string
|
||||
agents*: seq[Agent]
|
||||
agents*: seq[UIAgent]
|
||||
selection: ptr ImGuiSelectionBasicStorage
|
||||
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 =
|
||||
result = new SessionsTableComponent
|
||||
result.title = title
|
||||
result.agents = exampleAgents
|
||||
result.agents = @[]
|
||||
result.selection = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage()
|
||||
result.consoles = consoles
|
||||
|
||||
@@ -97,6 +26,7 @@ proc interact(component: SessionsTableComponent) =
|
||||
while ImGuiSelectionBasicStorage_GetNextSelectedItem(component.selection, addr it, addr row):
|
||||
let agent = component.agents[cast[int](row)]
|
||||
|
||||
|
||||
# Create a new console window
|
||||
if not component.consoles[].hasKey(agent.agentId):
|
||||
component.consoles[][agent.agentId] = Console(agent)
|
||||
@@ -104,7 +34,9 @@ proc interact(component: SessionsTableComponent) =
|
||||
# Focus the existing console window
|
||||
else:
|
||||
igSetWindowFocus_Str(fmt"[{agent.agentId}] {agent.username}@{agent.hostname}")
|
||||
|
||||
|
||||
# TODO: Clear selection properly
|
||||
ImGuiSelectionBasicStorage_Clear(component.selection)
|
||||
|
||||
proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
|
||||
igBegin(component.title, showComponent, 0)
|
||||
@@ -121,7 +53,7 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
|
||||
ImGuiTableFlags_ScrollY.int32 or
|
||||
ImGuiTableFlags_ScrollX.int32 or
|
||||
ImGuiTableFlags_NoBordersInBodyUntilResize.int32 or
|
||||
ImGui_TableFlags_SizingStretchProp.int32
|
||||
ImGui_TableFlags_SizingStretchSame.int32
|
||||
)
|
||||
|
||||
let cols: int32 = 8
|
||||
@@ -134,7 +66,7 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
|
||||
igTableSetupColumn("OS", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
|
||||
igTableSetupColumn("Process", 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)
|
||||
igTableHeadersRow()
|
||||
@@ -170,7 +102,17 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
|
||||
if igTableSetColumnIndex(6):
|
||||
igText($agent.pid)
|
||||
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
|
||||
# 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):
|
||||
# Update agents table with only non-selected ones
|
||||
var newAgents: seq[Agent] = @[]
|
||||
var newAgents: seq[UIAgent] = @[]
|
||||
for i in 0 ..< component.agents.len():
|
||||
if not ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](i)):
|
||||
newAgents.add(component.agents[i])
|
||||
|
||||
@@ -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
34
src/common/event.nim
Normal 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)
|
||||
@@ -1,7 +1,7 @@
|
||||
import prompt
|
||||
import tables
|
||||
import times
|
||||
import parsetoml
|
||||
import parsetoml, json
|
||||
import mummy
|
||||
|
||||
# Custom Binary Task structure
|
||||
@@ -202,6 +202,22 @@ type
|
||||
latestCheckin*: DateTime
|
||||
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
|
||||
type
|
||||
Protocol* {.size: sizeof(uint8).} = enum
|
||||
@@ -214,6 +230,12 @@ type
|
||||
port*: int
|
||||
protocol*: Protocol
|
||||
|
||||
UIListener* = ref object of RootObj
|
||||
listenerId*: string
|
||||
address*: string
|
||||
port*: int
|
||||
protocol*: Protocol
|
||||
|
||||
# Context structures
|
||||
type
|
||||
KeyPair* = object
|
||||
@@ -225,7 +247,8 @@ type
|
||||
Conquest* = ref object
|
||||
prompt*: Prompt
|
||||
dbPath*: string
|
||||
listeners*: Table[string, tuple[listener: Listener, thread: Thread[Listener]]]
|
||||
listeners*: Table[string, Listener]
|
||||
threads*: Table[string, Thread[Listener]]
|
||||
agents*: Table[string, Agent]
|
||||
interactAgent*: Agent
|
||||
keyPair*: KeyPair
|
||||
@@ -280,19 +303,27 @@ type
|
||||
Client <-> Server WebSocket communication
|
||||
]#
|
||||
type
|
||||
WsPacketType* = enum
|
||||
# Sent by client
|
||||
EventType* = enum
|
||||
CLIENT_HEARTBEAT = 0'u8 # Basic checkin
|
||||
|
||||
# Sent by client
|
||||
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_STOP = 4'u8 # Stop a listener
|
||||
|
||||
# Sent by team server
|
||||
CLIENT_AGENT_BINARY = 100'u8 # Return the agent binary to write to the operator's client machine
|
||||
CLIENT_AGENT_CONNECTION = 101'u8 # Notify new agent connection
|
||||
CLIENT_AGENT_CHECKIN = 102'u8 # Update agent checkin
|
||||
CLIENT_CONSOLE_LOG = 103'u8 # Add entry to a agent's console
|
||||
CLIENT_EVENT_LOG = 104'u8 # Add entry to the eventlog
|
||||
|
||||
CLIENT_CONNECTION = 200'u8 # Return team server profile
|
||||
CLIENT_PROFILE = 100'u8 # Team server profile and configuration
|
||||
CLIENT_LISTENER_ADD = 101'u8 # Add listener to listeners table
|
||||
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
|
||||
|
||||
Event* = object
|
||||
eventType*: EventType
|
||||
timestamp*: int64
|
||||
data*: JsonNode
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import ../globals
|
||||
import ../db/database
|
||||
import ../protocol/packer
|
||||
import ../core/logger
|
||||
import ../event/send
|
||||
import ../../common/[types, utils, serialize]
|
||||
|
||||
#[
|
||||
@@ -36,6 +37,9 @@ proc register*(registrationData: seq[byte]): bool =
|
||||
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.ws.sendAgent(agent)
|
||||
cq.ws.sendEventlogItem(LOG_INFO_SHORT, fmt"Agent {agent.agentId} connected to listener {agent.listenerId}.")
|
||||
|
||||
return true
|
||||
|
||||
@@ -69,6 +73,7 @@ proc getTasks*(heartbeat: seq[byte]): seq[seq[byte]] =
|
||||
|
||||
# Update the last check-in date for the accessed agent
|
||||
cq.agents[agentId].latestCheckin = cast[int64](timestamp).fromUnix().local()
|
||||
# cq.ws.sendAgentCheckin(agentId)
|
||||
|
||||
# Return tasks
|
||||
for task in cq.agents[agentId].tasks.mitems: # Iterate over agents as mutable items in order to modify GMAC tag
|
||||
|
||||
@@ -141,7 +141,7 @@ proc agentBuild*(cq: Conquest, listener, sleepDelay: string, sleepTechnique: str
|
||||
cq.error(fmt"Listener {listener.toUpperAscii} does not exist.")
|
||||
return false
|
||||
|
||||
let listener = cq.listeners[listener.toUpperAscii].listener
|
||||
let listener = cq.listeners[listener.toUpperAscii]
|
||||
|
||||
var config: seq[byte]
|
||||
if sleepDelay.isEmptyOrWhitespace():
|
||||
|
||||
@@ -8,7 +8,7 @@ import ../api/routes
|
||||
import ../db/database
|
||||
import ../core/logger
|
||||
import ../../common/[types, utils, profile]
|
||||
import ../websocket/send
|
||||
import ../event/send
|
||||
|
||||
#[
|
||||
Listener management
|
||||
@@ -79,7 +79,9 @@ proc listenerStart*(cq: Conquest, name: string, host: string, port: int, protoco
|
||||
createThread(thread, serve, listener)
|
||||
server.waitUntilReady()
|
||||
|
||||
cq.listeners[name] = (listener, thread)
|
||||
cq.listeners[name] = listener
|
||||
cq.threads[name] = thread
|
||||
|
||||
if not cq.dbStoreListener(listener):
|
||||
raise newException(CatchableError, "Failed to store listener in database.")
|
||||
|
||||
@@ -97,7 +99,6 @@ proc restartListeners*(cq: Conquest) =
|
||||
for listener in listeners:
|
||||
try:
|
||||
# Create new listener
|
||||
let name: string = generateUUID()
|
||||
var router: Router
|
||||
router.notFoundHandler = routes.error404
|
||||
router.methodNotAllowedHandler = routes.error405
|
||||
@@ -128,9 +129,11 @@ proc restartListeners*(cq: Conquest) =
|
||||
createThread(thread, serve, listener)
|
||||
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}.")
|
||||
|
||||
|
||||
except CatchableError as err:
|
||||
cq.error("Failed to restart listener: ", err.msg)
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import ./[agent, listener, builder]
|
||||
import ../globals
|
||||
import ../db/database
|
||||
import ../core/logger
|
||||
import ../../common/[types, crypto, profile]
|
||||
import ../websocket/[receive, send]
|
||||
import ../../common/[types, crypto, utils, profile]
|
||||
import ../event/[recv, send]
|
||||
import mummy, mummy/routers
|
||||
|
||||
#[
|
||||
@@ -88,10 +88,10 @@ proc handleConsoleCommand(cq: Conquest, args: string) =
|
||||
of "list":
|
||||
cq.listenerList()
|
||||
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
|
||||
of "stop":
|
||||
#cq.listenerStop(opts.listener.get.stop.get.name)
|
||||
cq.listenerStop(opts.listener.get.stop.get.name)
|
||||
discard
|
||||
else:
|
||||
cq.listenerUsage()
|
||||
@@ -133,7 +133,8 @@ proc header() =
|
||||
proc init*(T: type Conquest, profile: Profile): Conquest =
|
||||
var cq = new Conquest
|
||||
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.interactAgent = nil
|
||||
cq.profile = profile
|
||||
@@ -148,27 +149,36 @@ proc upgradeHandler(request: Request) =
|
||||
{.cast(gcsafe).}:
|
||||
let ws = request.upgradeToWebSocket()
|
||||
cq.ws = ws
|
||||
# Send client connection message
|
||||
ws.sendEventlogItem(LOG_SUCCESS_SHORT, "CQ-V1")
|
||||
|
||||
proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {.gcsafe.} =
|
||||
{.cast(gcsafe).}:
|
||||
case event:
|
||||
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:
|
||||
# Continuously send heartbeat messages
|
||||
ws.sendHeartbeat()
|
||||
|
||||
case message.getMessageType():
|
||||
of CLIENT_AGENT_COMMAND:
|
||||
discard
|
||||
of CLIENT_LISTENER_START:
|
||||
message.receiveStartListener()
|
||||
of CLIENT_LISTENER_STOP:
|
||||
message.receiveStopListener()
|
||||
of CLIENT_AGENT_BUILD:
|
||||
discard
|
||||
else: discard
|
||||
# case message.getMessageType():
|
||||
# of CLIENT_AGENT_COMMAND:
|
||||
# discard
|
||||
# of CLIENT_LISTENER_START:
|
||||
# message.receiveStartListener()
|
||||
# of CLIENT_LISTENER_STOP:
|
||||
# discard
|
||||
# # message.receiveStopListener()
|
||||
# of CLIENT_AGENT_BUILD:
|
||||
# discard
|
||||
# else: discard
|
||||
|
||||
of ErrorEvent:
|
||||
discard
|
||||
of CloseEvent:
|
||||
|
||||
40
src/server/event/recv.nim
Normal file
40
src/server/event/recv.nim
Normal 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
78
src/server/event/send.nim
Normal 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)
|
||||
@@ -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 ../common/types
|
||||
import core/logger
|
||||
|
||||
proc validatePort*(portStr: string): bool =
|
||||
try:
|
||||
let port: int = portStr.parseInt
|
||||
return port >= 1 and port <= 65535
|
||||
except ValueError:
|
||||
return false
|
||||
proc `%`*(agent: Agent): JsonNode =
|
||||
result = newJObject()
|
||||
result["agentId"] = %agent.agentId
|
||||
result["listenerId"] = %agent.listenerId
|
||||
result["username"] = %agent.username
|
||||
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
|
||||
type
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user