diff --git a/server/agent/agent.nim b/server/agent/agent.nim index 40b4f83..e6651d0 100644 --- a/server/agent/agent.nim +++ b/server/agent/agent.nim @@ -1,5 +1,5 @@ -import terminal, strformat, times -import ../[types, globals] +import terminal, strformat +import ../[types, globals, utils] import ../db/database @@ -23,7 +23,8 @@ Options: -h, --help""") proc agentList*(cq: Conquest, args: varargs[string]) = - discard + let agents = cq.dbGetAllAgents() + cq.drawTable(agents) proc agentBuild*(cq: Conquest, args: varargs[string]) = discard @@ -44,8 +45,6 @@ proc agentInteract*(cq: Conquest, args: varargs[string]) = ]# proc register*(agent: Agent): bool = - let date: string = now().format("dd-MM-yyyy HH:mm:ss") - # The following line is required to be able to use the `cq` global variable for console output {.cast(gcsafe).}: @@ -62,7 +61,7 @@ proc register*(agent: Agent): bool = return false cq.add(agent.name, agent) - cq.writeLine(fgYellow, styleBright, fmt"[{date}] ", resetStyle, "Agent ", fgYellow, styleBright, agent.name, resetStyle, " connected to listener ", fgGreen, styleBright, agent.listener, resetStyle, ": ", fgYellow, styleBright, fmt"{agent.username}@{agent.hostname}", "\n") + cq.writeLine(fgYellow, styleBright, fmt"[{agent.firstCheckin}] ", resetStyle, "Agent ", fgYellow, styleBright, agent.name, resetStyle, " connected to listener ", fgGreen, styleBright, agent.listener, resetStyle, ": ", fgYellow, styleBright, fmt"{agent.username}@{agent.hostname}", "\n") return true #[ diff --git a/server/config.nims b/server/config.nims deleted file mode 100644 index f177b11..0000000 --- a/server/config.nims +++ /dev/null @@ -1,2 +0,0 @@ -# Compiler flags ---threads:on \ No newline at end of file diff --git a/server/db/database.nim b/server/db/database.nim index ee5de01..d124b34 100644 --- a/server/db/database.nim +++ b/server/db/database.nim @@ -21,7 +21,8 @@ proc dbInit*(cq: Conquest) = CREATE TABLE agents ( name TEXT PRIMARY KEY, - listener TEXT NOT NULL, + listener TEXT NOT NULL, + process TEXT NOT NULL, pid INTEGER NOT NULL, username TEXT NOT NULL, hostname TEXT NOT NULL, @@ -30,6 +31,7 @@ proc dbInit*(cq: Conquest) = elevated BOOLEAN NOT NULL, sleep INTEGER DEFAULT 10, jitter REAL DEFAULT 0.1, + firstCheckin DATETIME NOT NULL, FOREIGN KEY (listener) REFERENCES listeners(name) ); @@ -40,6 +42,9 @@ proc dbInit*(cq: Conquest) = except SqliteError: cq.writeLine(fgGreen, "[+] ", cq.dbPath, ": Database file found.") +#[ + Listeners +]# proc dbStoreListener*(cq: Conquest, listener: Listener): bool = try: @@ -108,15 +113,18 @@ proc listenerExists*(cq: Conquest, listenerName: string): bool = cq.writeLine(fgRed, styleBright, "[-] ", getCurrentExceptionMsg()) return false +#[ + Agents +]# proc dbStoreAgent*(cq: Conquest, agent: Agent): bool = try: let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) conquestDb.exec(""" - INSERT INTO agents (name, listener, sleep, jitter, pid,username, hostname, ip, os, elevated) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?); - """, agent.name, agent.listener, agent.sleep, agent.jitter, agent.pid, agent.username, agent.hostname, agent.ip, agent.os, agent.elevated) + INSERT INTO agents (name, listener, sleep, jitter, process, pid, username, hostname, ip, os, elevated, firstCheckin) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + """, agent.name, agent.listener, agent.sleep, agent.jitter, agent.process, agent.pid, agent.username, agent.hostname, agent.ip, agent.os, agent.elevated, $agent.firstCheckin) conquestDb.close() except: @@ -125,3 +133,36 @@ proc dbStoreAgent*(cq: Conquest, agent: Agent): bool = return true +proc dbGetAllAgents*(cq: Conquest): seq[Agent] = + + var agents: seq[Agent] = @[] + + try: + let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) + + for row in conquestDb.iterate("SELECT name, listener, sleep, jitter, process, pid, username, hostname, ip, os, elevated, firstCheckin FROM agents;"): + let (name, listener, sleep, jitter, process, pid, username, hostname, ip, os, elevated, firstCheckin) = row.unpack((string, string, int, float, string, int, string, string, string, string, bool, string)) + + let a = Agent( + name: name, + listener: listener, + sleep: sleep, + jitter: jitter, + process: process, + pid: pid, + username: username, + hostname: hostname, + ip: ip, + os: os, + elevated: elevated, + firstCheckin: firstCheckin, + tasks: @[] + ) + + agents.add(a) + + conquestDb.close() + except: + cq.writeLine(fgRed, styleBright, "[-] ", getCurrentExceptionMsg()) + + return agents \ No newline at end of file diff --git a/server/listener/api.nim b/server/listener/api.nim index e1d9703..cc8cd2d 100644 --- a/server/listener/api.nim +++ b/server/listener/api.nim @@ -1,5 +1,5 @@ import prologue, nanoid -import terminal, sequtils, strutils +import terminal, sequtils, strutils, times import ../[types] import ../agent/agent @@ -26,6 +26,7 @@ proc register*(ctx: Context) {.async.} = "hostname":"hostname", "ip": "ip-address", "os": "operating-system", + "process": "agent.exe", "pid": 1234, "elevated": false } @@ -34,11 +35,12 @@ proc register*(ctx: Context) {.async.} = try: let postData: JsonNode = parseJson(ctx.request.body) - agentRegistrationData = postData.to(AgentRegistrationData) - agentUuid = generate(alphabet=join(toSeq('A'..'Z'), ""), size=8) - listenerUuid = ctx.getPathParams("listener") + agentRegistrationData: AgentRegistrationData = postData.to(AgentRegistrationData) + agentUuid: string = generate(alphabet=join(toSeq('A'..'Z'), ""), size=8) + listenerUuid: string = ctx.getPathParams("listener") + date: string = now().format("dd-MM-yyyy HH:mm:ss") - let agent: Agent = newAgent(agentUuid, listenerUuid, agentRegistrationData) + let agent: Agent = newAgent(agentUuid, listenerUuid, date, agentRegistrationData) # Fully register agent and add it to database if not agent.register(): diff --git a/server/listener/listener.nim b/server/listener/listener.nim index 812bf1f..eb24b3d 100644 --- a/server/listener/listener.nim +++ b/server/listener/listener.nim @@ -1,8 +1,8 @@ import strformat, strutils, sequtils, checksums/sha1, nanoid, terminal import prologue -import ./[api, utils] -import ../types +import ./api +import ../[types, utils] import ../db/database proc listenerUsage*(cq: Conquest) = diff --git a/server/nim.cfg b/server/nim.cfg new file mode 100644 index 0000000..9f51a60 --- /dev/null +++ b/server/nim.cfg @@ -0,0 +1,3 @@ +# Compiler flags +--threads:on +-d:httpxServerName="nginx" \ No newline at end of file diff --git a/server/server.nim b/server/server.nim index df1b0af..4ccbb68 100644 --- a/server/server.nim +++ b/server/server.nim @@ -31,6 +31,7 @@ var parser = newParser: command("list"): help("List all agents.") + # TODO: Add a flag that allows the user to only list agents that are connected to a specific listener (-n ) command("build"): help("Build an agent to connect to an active listener.") @@ -112,7 +113,6 @@ proc header(cq: Conquest) = Conquest framework entry point ]# proc main() = - # Handle CTRL+C, proc exit() {.noconv.} = echo "Received CTRL+C. Type \"exit\" to close the application.\n" diff --git a/server/types.nim b/server/types.nim index 03bc1e6..812cf82 100644 --- a/server/types.nim +++ b/server/types.nim @@ -1,6 +1,6 @@ import prompt import prologue -import tables +import tables, times #[ Agent @@ -35,14 +35,17 @@ type hostname*: string ip*: string os*: string + process*: string pid*: int elevated*: bool + # TODO: Add additional fields: domain, ... Agent* = ref object name*: string listener*: string sleep*: int jitter*: float + process*: string pid*: int username*: string hostname*: string @@ -50,11 +53,14 @@ type os*: string elevated*: bool tasks*: seq[Task] + firstCheckin*: string + lastCheckin*: string -proc newAgent*(name, listener, username, hostname, ip, os: string, pid: int, elevated: bool): Agent = +proc newAgent*(name, listener, username, hostname, process, ip, os, firstCheckin: string, pid: int, elevated: bool): Agent = var agent = new Agent agent.name = name agent.listener = listener + agent.process = process agent.pid = pid agent.username = username agent.hostname = hostname @@ -64,13 +70,15 @@ proc newAgent*(name, listener, username, hostname, ip, os: string, pid: int, ele agent.sleep = 10 agent.jitter = 0.2 agent.tasks = @[] - + agent.firstCheckin = firstCheckin + return agent -proc newAgent*(name, listener: string, postData: AgentRegistrationData): Agent = +proc newAgent*(name, listener, firstCheckin: string, postData: AgentRegistrationData): Agent = var agent = new Agent agent.name = name agent.listener = listener + agent.process = postData.process agent.pid = postData.pid agent.username = postData.username agent.hostname = postData.hostname @@ -80,10 +88,10 @@ proc newAgent*(name, listener: string, postData: AgentRegistrationData): Agent = agent.sleep = 10 agent.jitter = 0.2 agent.tasks = @[] + agent.firstCheckin = firstCheckin return agent - #[ Listener ]# @@ -118,7 +126,7 @@ proc stringToProtocol*(protocol: string): Protocol = #[ - Conquest + Conquest framework ]# type Conquest* = ref object diff --git a/server/listener/utils.nim b/server/utils.nim similarity index 73% rename from server/listener/utils.nim rename to server/utils.nim index 1ba92db..1adca78 100644 --- a/server/listener/utils.nim +++ b/server/utils.nim @@ -1,6 +1,6 @@ import re, strutils -import ../types +import ./types proc validateIPv4Address*(ip: string): bool = let ipv4Pattern = re"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$" @@ -45,7 +45,7 @@ proc drawTable*(cq: Conquest, listeners: seq[Listener]) = # Column headers and widths let headers = @["Name", "Address", "Port", "Protocol", "Agents"] - let widths = @[10, 15, 7, 10, 8] + let widths = @[10, 17, 7, 10, 8] cq.writeLine(border(topLeft, topMid, topRight, widths)) cq.writeLine(row(headers, widths)) @@ -60,4 +60,17 @@ proc drawTable*(cq: Conquest, listeners: seq[Listener]) = proc drawTable*(cq: Conquest, agents: seq[Agent]) = - discard \ No newline at end of file + + let headers: seq[string] = @["Name", "Address", "Username", "Hostname", "Operating System", "Process", "PID"] + let widths = @[10, 17, 25, 20, 22, 15, 8] + + cq.writeLine(border(topLeft, topMid, topRight, widths)) + cq.writeLine(row(headers, widths)) + cq.writeLine(border(midLeft, midMid, midRight, widths)) + + # TODO: Highlight elevated processes + for a in agents: + let row = @[a.name, a.ip, a.username, a.hostname, a.os, a.process, $a.pid] + cq.writeLine(row(row, widths)) + + cq.writeLine(border(botLeft, botMid, botRight, widths)) \ No newline at end of file