diff --git a/src/agents/monarch/nim.cfg b/src/agents/monarch/nim.cfg index a83fcf6..d0fd628 100644 --- a/src/agents/monarch/nim.cfg +++ b/src/agents/monarch/nim.cfg @@ -1,8 +1,8 @@ # Agent configuration --d:ListenerUuid="JEBFQPEP" +-d:ListenerUuid="CFD80565" -d:Octet1="127" -d:Octet2="0" -d:Octet3="0" -d:Octet4="1" --d:ListenerPort=5555 --d:SleepDelay=5 +-d:ListenerPort=9999 +-d:SleepDelay=10 diff --git a/src/agents/monarch/types.nim b/src/agents/monarch/types.nim index 5df7e45..cb49155 100644 --- a/src/agents/monarch/types.nim +++ b/src/agents/monarch/types.nim @@ -1,4 +1,4 @@ -import winim, tables +import winim import ../../types export Task, CommandType, TaskResult, TaskStatus @@ -9,20 +9,19 @@ type DC = 2 SERVER = 3 - # API Structs type OSVersionInfoExW* {.importc: "OSVERSIONINFOEXW", header: "".} = object - dwOSVersionInfoSize*: ULONG - dwMajorVersion*: ULONG - dwMinorVersion*: ULONG - dwBuildNumber*: ULONG - dwPlatformId*: ULONG - szCSDVersion*: array[128, WCHAR] - wServicePackMajor*: USHORT - wServicePackMinor*: USHORT - wSuiteMask*: USHORT - wProductType*: UCHAR - wReserved*: UCHAR + dwOSVersionInfoSize*: ULONG + dwMajorVersion*: ULONG + dwMinorVersion*: ULONG + dwBuildNumber*: ULONG + dwPlatformId*: ULONG + szCSDVersion*: array[128, WCHAR] + wServicePackMajor*: USHORT + wServicePackMinor*: USHORT + wSuiteMask*: USHORT + wProductType*: UCHAR + wReserved*: UCHAR type AgentConfig* = ref object diff --git a/src/server/api/handlers.nim b/src/server/api/handlers.nim index fcaafbf..cd1e8f4 100644 --- a/src/server/api/handlers.nim +++ b/src/server/api/handlers.nim @@ -1,9 +1,13 @@ import terminal, strformat, strutils, sequtils, tables, json, times, base64, system -import ../globals +import ../[utils, globals] import ../db/database import ../../types +# Utility functions +proc add*(cq: Conquest, agent: Agent) = + cq.agents[agent.name] = agent + #[ Agent API Functions relevant for dealing with the agent API, such as registering new agents, querying tasks and posting results diff --git a/src/server/api/routes.nim b/src/server/api/routes.nim index c266293..5128c64 100644 --- a/src/server/api/routes.nim +++ b/src/server/api/routes.nim @@ -1,7 +1,8 @@ -import prologue, nanoid, json +import prologue, json import sequtils, strutils, times import ./handlers +import ../utils import ../../types proc error404*(ctx: Context) {.async.} = @@ -38,12 +39,28 @@ proc register*(ctx: Context) {.async.} = let postData: JsonNode = parseJson(ctx.request.body) agentRegistrationData: AgentRegistrationData = postData.to(AgentRegistrationData) - agentUuid: string = generate(alphabet=join(toSeq('A'..'Z'), ""), size=8) + agentUuid: string = generateUUID() listenerUuid: string = ctx.getPathParams("listener") date: DateTime = now() - let agent: Agent = newAgent(agentUuid, listenerUuid, date, agentRegistrationData) - + let agent: Agent = Agent( + name: agentUuid, + listener: listenerUuid, + username: agentRegistrationData.username, + hostname: agentRegistrationData.hostname, + domain: agentRegistrationData.domain, + process: agentRegistrationData.process, + pid: agentRegistrationData.pid, + ip: agentRegistrationData.ip, + os: agentRegistrationData.os, + elevated: agentRegistrationData.elevated, + sleep: agentRegistrationData.sleep, + jitter: 0.2, + tasks: @[], + firstCheckin: date, + latestCheckin: date + ) + # Fully register agent and add it to database if not agent.register(): # Either the listener the agent tries to connect to does not exist in the database, or the insertion of the agent failed diff --git a/src/server/core/agent.nim b/src/server/core/agent.nim index bf05ed8..2c8811b 100644 --- a/src/server/core/agent.nim +++ b/src/server/core/agent.nim @@ -5,10 +5,23 @@ import ../task/handler import ../db/database import ../../types -#[ - Agent management mode - These console commands allow dealing with agents from the Conquest framework's prompt interface -]# +# Utility functions +proc addMultiple*(cq: Conquest, agents: seq[Agent]) = + for a in agents: + cq.agents[a.name] = a + +proc delAgent*(cq: Conquest, agentName: string) = + cq.agents.del(agentName) + +proc getAgentsAsSeq*(cq: Conquest): seq[Agent] = + var agents: seq[Agent] = @[] + for agent in cq.agents.values: + agents.add(agent) + return agents + +#[ + Agent management +]# proc agentUsage*(cq: Conquest) = cq.writeLine("""Manage, build and interact with agents. diff --git a/src/server/core/listener.nim b/src/server/core/listener.nim index 566a5ce..89ab282 100644 --- a/src/server/core/listener.nim +++ b/src/server/core/listener.nim @@ -1,4 +1,4 @@ -import strformat, strutils, sequtils, nanoid, terminal +import strformat, strutils, sequtils, terminal import prologue import ../utils @@ -6,6 +6,25 @@ import ../api/routes import ../db/database import ../../types +# Utility functions +proc delListener(cq: Conquest, listenerName: string) = + cq.listeners.del(listenerName) + +proc add(cq: Conquest, listener: Listener) = + cq.listeners[listener.name] = listener + +proc newListener*(name: string, address: string, port: int): Listener = + var listener = new Listener + listener.name = name + listener.address = address + listener.port = port + listener.protocol = HTTP + + return listener + +#[ + Listener management +]# proc listenerUsage*(cq: Conquest) = cq.writeLine("""Manage, start and stop listeners. @@ -36,7 +55,7 @@ proc listenerStart*(cq: Conquest, host: string, portStr: string) = # Create new listener let - name: string = generate(alphabet=join(toSeq('A'..'Z'), ""), size=8) + name: string = generateUUID() listenerSettings = newSettings( appName = name, debug = false, diff --git a/src/server/core/server.nim b/src/server/core/server.nim index eae6586..9b0eaee 100644 --- a/src/server/core/server.nim +++ b/src/server/core/server.nim @@ -2,8 +2,8 @@ import prompt, terminal, argparse import strutils, strformat, times, system, tables import ./[agent, listener] +import ../[globals, utils] import ../db/database -import ../globals import ../../types #[ @@ -127,6 +127,17 @@ proc header(cq: Conquest) = cq.writeLine("─".repeat(21)) cq.writeLine("") +proc initConquest*(dbPath: string): Conquest = + var cq = new Conquest + var prompt = Prompt.init() + cq.prompt = prompt + cq.dbPath = dbPath + cq.listeners = initTable[string, Listener]() + cq.agents = initTable[string, Agent]() + cq.interactAgent = nil + + return cq + proc startServer*() = # Handle CTRL+C, proc exit() {.noconv.} = diff --git a/src/server/db/database.nim b/src/server/db/database.nim index 596f2af..6460bfc 100644 --- a/src/server/db/database.nim +++ b/src/server/db/database.nim @@ -1,6 +1,7 @@ import system, terminal, tiny_sqlite import ./[dbAgent, dbListener] +import ../utils import ../../types # Export functions so that only ./db/database is required to be imported diff --git a/src/server/db/dbAgent.nim b/src/server/db/dbAgent.nim index 06b9ab9..dbaeba9 100644 --- a/src/server/db/dbAgent.nim +++ b/src/server/db/dbAgent.nim @@ -1,4 +1,6 @@ import system, terminal, tiny_sqlite, times + +import ../utils import ../../types #[ diff --git a/src/server/db/dbListener.nim b/src/server/db/dbListener.nim index 8f02c1f..547e599 100644 --- a/src/server/db/dbListener.nim +++ b/src/server/db/dbListener.nim @@ -1,6 +1,15 @@ import system, terminal, tiny_sqlite + +import ../utils import ../../types +# Utility functions +proc stringToProtocol*(protocol: string): Protocol = + case protocol + of "http": + return HTTP + else: discard + #[ Listener database functions ]# diff --git a/src/server/main.nim b/src/server/main.nim index 11e110f..efb3153 100644 --- a/src/server/main.nim +++ b/src/server/main.nim @@ -1,5 +1,7 @@ +import random import core/server # Conquest framework entry point when isMainModule: + randomize() startServer() \ No newline at end of file diff --git a/src/server/task/dispatcher.nim b/src/server/task/dispatcher.nim index 4a4b5a0..290754d 100644 --- a/src/server/task/dispatcher.nim +++ b/src/server/task/dispatcher.nim @@ -1,11 +1,12 @@ -import argparse, times, strformat, terminal, nanoid, sequtils +import argparse, times, strformat, terminal, sequtils import ../../types +import ../utils proc createTask*(cq: Conquest, command: CommandType, args: string, message: string) = let date = now().format("dd-MM-yyyy HH:mm:ss") task = Task( - id: generate(alphabet=join(toSeq('A'..'Z'), ""), size=8), + id: generateUUID(), agent: cq.interactAgent.name, command: command, args: args, diff --git a/src/server/task/handler.nim b/src/server/task/handler.nim index e08ced4..bbaa149 100644 --- a/src/server/task/handler.nim +++ b/src/server/task/handler.nim @@ -1,5 +1,6 @@ import times, strformat, terminal, tables, json, sequtils, strutils import ./[parser, packer, dispatcher] +import ../utils import ../../types proc initAgentCommands*(): Table[CommandType, Command] = diff --git a/src/server/utils.nim b/src/server/utils.nim index 885ce0a..764839b 100644 --- a/src/server/utils.nim +++ b/src/server/utils.nim @@ -1,8 +1,9 @@ -import strutils, terminal, tables, sequtils, times, strformat +import strutils, terminal, tables, sequtils, times, strformat, random, prompt import std/wordwrap import ../types +# Utility functions proc parseOctets*(ip: string): tuple[first, second, third, fourth: int] = # TODO: Verify that address is in correct, expected format let octets = ip.split('.') @@ -15,8 +16,33 @@ proc validatePort*(portStr: string): bool = except ValueError: return false -# Table border characters +proc generateUUID*(): string = + # Create a 4-byte HEX UUID string (8 characters) + (0..<4).mapIt(rand(255)).mapIt(fmt"{it:02X}").join() +# Function templates and overwrites +template writeLine*(cq: Conquest, args: varargs[untyped]) = + cq.prompt.writeLine(args) +proc readLine*(cq: Conquest): string = + return cq.prompt.readLine() +template setIndicator*(cq: Conquest, indicator: string) = + cq.prompt.setIndicator(indicator) +template showPrompt*(cq: Conquest) = + cq.prompt.showPrompt() +template hidePrompt*(cq: Conquest) = + cq.prompt.hidePrompt() +template setStatusBar*(cq: Conquest, statusBar: seq[StatusBarItem]) = + cq.prompt.setStatusBar(statusBar) +template clear*(cq: Conquest) = + cq.prompt.clear() + +# Overwrite withOutput function to handle function arguments +proc withOutput*(cq: Conquest, outputFunction: proc(cq: Conquest, args: string), args: string) = + cq.hidePrompt() + outputFunction(cq, args) + cq.showPrompt() + +# Table border characters type Cell = object text: string diff --git a/src/types.nim b/src/types.nim index abc44f8..242af35 100644 --- a/src/types.nim +++ b/src/types.nim @@ -1,11 +1,8 @@ import prompt -import prologue import tables import times -#[ - Agent types & procs -]# +# Task structure type CommandType* = enum ExecuteShell = "shell" @@ -62,6 +59,8 @@ type args*: string # Json string containing all the positional arguments # Example: """{"command": "whoami", "arguments": "/all"}""" +# Agent structure +type AgentRegistrationData* = object username*: string hostname*: string @@ -90,30 +89,7 @@ type firstCheckin*: DateTime latestCheckin*: DateTime -# TODO: Take sleep value from agent registration data (set via nim.cfg file) -proc newAgent*(name, listener: string, firstCheckin: DateTime, postData: AgentRegistrationData): Agent = - var agent = new Agent - agent.name = name - agent.listener = listener - agent.username = postData.username - agent.hostname = postData.hostname - agent.domain = postData.domain - agent.process = postData.process - agent.pid = postData.pid - agent.ip = postData.ip - agent.os = postData.os - agent.elevated = postData.elevated - agent.sleep = postData.sleep - agent.jitter = 0.2 - agent.tasks = @[] - agent.firstCheckin = firstCheckin - agent.latestCheckin = firstCheckin - - return agent - -#[ - Listener types and procs -]# +# Listener structure type Protocol* = enum HTTP = "http" @@ -124,82 +100,11 @@ type port*: int protocol*: Protocol -proc newListener*(name: string, address: string, port: int): Listener = - var listener = new Listener - listener.name = name - listener.address = address - listener.port = port - listener.protocol = HTTP - - return listener - -proc stringToProtocol*(protocol: string): Protocol = - case protocol - of "http": - return HTTP - else: discard - -#[ - Conquest framework types & procs -]# +# Server structure type Conquest* = ref object prompt*: Prompt dbPath*: string listeners*: Table[string, Listener] agents*: Table[string, Agent] - interactAgent*: Agent - -proc add*(cq: Conquest, listener: Listener) = - cq.listeners[listener.name] = listener - -proc add*(cq: Conquest, agent: Agent) = - cq.agents[agent.name] = agent - -proc addMultiple*(cq: Conquest, agents: seq[Agent]) = - for a in agents: - cq.agents[a.name] = a - -proc delListener*(cq: Conquest, listenerName: string) = - cq.listeners.del(listenerName) - -proc delAgent*(cq: Conquest, agentName: string) = - cq.agents.del(agentName) - -proc getAgentsAsSeq*(cq: Conquest): seq[Agent] = - var agents: seq[Agent] = @[] - for agent in cq.agents.values: - agents.add(agent) - return agents - -proc initConquest*(dbPath: string): Conquest = - var cq = new Conquest - var prompt = Prompt.init() - cq.prompt = prompt - cq.dbPath = dbPath - cq.listeners = initTable[string, Listener]() - cq.agents = initTable[string, Agent]() - cq.interactAgent = nil - - return cq - -template writeLine*(cq: Conquest, args: varargs[untyped]) = - cq.prompt.writeLine(args) -proc readLine*(cq: Conquest): string = - return cq.prompt.readLine() -template setIndicator*(cq: Conquest, indicator: string) = - cq.prompt.setIndicator(indicator) -template showPrompt*(cq: Conquest) = - cq.prompt.showPrompt() -template hidePrompt*(cq: Conquest) = - cq.prompt.hidePrompt() -template setStatusBar*(cq: Conquest, statusBar: seq[StatusBarItem]) = - cq.prompt.setStatusBar(statusBar) -template clear*(cq: Conquest) = - cq.prompt.clear() - -# Overwrite withOutput function to handle function arguments -proc withOutput*(cq: Conquest, outputFunction: proc(cq: Conquest, args: string), args: string) = - cq.hidePrompt() - outputFunction(cq, args) - cq.showPrompt() + interactAgent*: Agent \ No newline at end of file